1/*
2 * cmstool.cpp - manipluate CMS messages, intended to be an alternate for the
3 *		 currently useless cms command in /usr/bin/security
4 */
5
6#include <Security/Security.h>
7#include <security_cdsa_utils/cuFileIo.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <unistd.h>
12#include <utilLib/common.h>
13#include <security_cdsa_utils/cuFileIo.h>
14#include <security_cdsa_utils/cuPrintCert.h>
15#include <clAppUtils/identPicker.h>
16#include <clAppUtils/sslAppUtils.h>
17#include <security_cdsa_utils/cuOidParser.h>
18#include <CoreFoundation/CoreFoundation.h>
19#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
20#include <Security/SecCmsEncoder.h>
21#include <Security/SecCmsDecoder.h>
22#include <Security/SecCmsEncryptedData.h>
23#include <Security/SecCmsEnvelopedData.h>
24#include <Security/SecCmsMessage.h>
25#include <Security/SecCmsRecipientInfo.h>
26#include <Security/SecCmsSignedData.h>
27#include <Security/SecCmsSignerInfo.h>
28#include <Security/SecCmsContentInfo.h>
29#include <Security/SecCmsDigestContext.h>
30#include <Security/SecTrustPriv.h>
31#include <Security/SecKeychainItemPriv.h>
32#include <Security/SecCertificate.h>
33#include <Security/SecSMIME.h>
34#include <Security/oidsattr.h>
35#include <Security/SecAsn1Coder.h>
36#include <Security/secasn1t.h>
37#include <Security/SecAsn1Templates.h>
38
39static void usage(char **argv)
40{
41    printf("Usage: %s cmd [option ...]\n", argv[0]);
42	printf("cmd values:\n");
43	printf("   sign       -- create signedData\n");
44	printf("   envel      -- create envelopedData\n");
45	printf("   signEnv    -- create nested EnvelopedData(signedData(data))\n");
46	printf("   parse      -- parse a CMS message file\n");
47	printf("Options:\n");
48	printf("   -i infile\n");
49	printf("   -o outfile\n");
50	printf("   -k keychain        -- Keychain to search for certs\n");
51	printf("   -p                 -- Use identity picker\n");
52	printf("   -r recipient       -- specify recipient of enveloped data\n");
53	printf("   -c                 -- parse signer cert\n");
54	printf("   -v sign|encr       -- verify message is signed/encrypted\n");
55	printf("   -e eContentType    -- a(uthData)|r(keyData)\n");
56	printf("   -d detached        -- infile contains detached content (sign only)\n");
57	printf("   -D detachedContent -- detached content (parse only)\n");
58	printf("   -q                 -- quiet\n");
59	exit(1);
60}
61
62/* high level op */
63typedef enum {
64	CTO_Sign,
65	CTO_Envelop,
66	CTO_SignEnvelop,
67	CTO_Parse
68} CT_Op;
69
70/* to verify */
71typedef enum {
72	CTV_None,
73	CTV_Sign,
74	CTV_Envelop,
75	CTV_SignEnvelop
76} CT_Vfy;
77
78/* additional OIDS to specify as eContentType */
79#define OID_PKINIT	0x2B, 6, 1, 5, 2, 3
80#define OID_PKINIT_LEN	6
81
82static const uint8	OID_PKINIT_AUTH_DATA[]	    = {OID_PKINIT, 1};
83static const uint8	OID_PKINIT_DH_KEY_DATA[]    = {OID_PKINIT, 2};
84static const uint8	OID_PKINIT_RKEY_DATA[]	    = {OID_PKINIT, 3};
85static const uint8	OID_PKINIT_KP_CLIENTAUTH[]  = {OID_PKINIT, 3};
86static const uint8	OID_PKINIT_KPKDC[]			= {OID_PKINIT, 5};
87
88static const CSSM_OID	CSSMOID_PKINIT_AUTH_DATA =
89	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA};
90static const CSSM_OID	CSSMOID_PKINIT_DH_KEY_DATA =
91	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA};
92static const CSSM_OID	CSSMOID_PKINIT_RKEY_DATA =
93	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA};
94static const CSSM_OID	CSSMOID_PKINIT_KP_CLIENTAUTH =
95	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH};
96static const CSSM_OID	CSSMOID_PKINIT_KPKDC =
97	{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC};
98
99typedef struct {
100    CSSM_OID	contentType;
101    CSSM_DATA	content;
102} SimpleContentInfo;
103
104const SecAsn1Template SimpleContentInfoTemplate[] = {
105    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) },
106    { SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) },
107    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
108	  offsetof(SimpleContentInfo, content),
109	  kSecAsn1AnyTemplate },
110    { 0, }
111};
112
113/*
114 * Obtain the content of a contentInfo, This basically strips off the contentType OID
115 * and returns a mallocd copy of the ASN_ANY content.
116 */
117static OSStatus ContentInfoContent(
118    const unsigned char *contentInfo,
119    unsigned contentInfoLen,
120    unsigned char **content,	    /* mallocd and RETURNED */
121    unsigned *contentLen)	    /* RETURNED */
122{
123    SecAsn1CoderRef coder = NULL;
124    OSStatus ortn;
125    SimpleContentInfo decodedInfo;
126
127    ortn = SecAsn1CoderCreate(&coder);
128    if(ortn) {
129		return ortn;
130    }
131    memset(&decodedInfo, 0, sizeof(decodedInfo));
132    ortn = SecAsn1Decode(coder, contentInfo, contentInfoLen,
133		SimpleContentInfoTemplate, &decodedInfo);
134    if(ortn) {
135		goto errOut;
136    }
137    if(decodedInfo.content.Data == NULL) {
138	printf("***Error decoding contentInfo: no content\n");
139	ortn = internalComponentErr;
140		goto errOut;
141    }
142    *content = (unsigned char *)malloc(decodedInfo.content.Length);
143    memmove(*content, decodedInfo.content.Data, decodedInfo.content.Length);
144    *contentLen = decodedInfo.content.Length;
145errOut:
146    SecAsn1CoderRelease(coder);
147    return ortn;
148}
149
150/*
151 * Find a cert in specified keychain or keychain list matching specified
152 * email address. We happen to knopw that the email address is stored with the
153 * kSecKeyAlias attribute.
154 */
155static OSStatus findCert(
156	const char *emailAddress,
157	CFTypeRef kcArArray,			// kc, array, or even NULL
158	SecCertificateRef *cert)
159{
160	OSStatus					ortn;
161	SecKeychainSearchRef		srch;
162	SecKeychainAttributeList	attrList;
163	SecKeychainAttribute		attr;
164
165	attr.tag = kSecKeyAlias;
166	attr.length = strlen(emailAddress);
167	attr.data = (void *)emailAddress;
168	attrList.count = 1;
169	attrList.attr = &attr;
170
171	ortn = SecKeychainSearchCreateFromAttributes(kcArArray,
172		kSecCertificateItemClass,
173		&attrList,
174		&srch);
175	if(ortn) {
176		cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
177		return ortn;
178	}
179
180	ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert);
181	if(ortn) {
182		printf("***No certs founmd matching recipient %s. Aborting.\n",
183			emailAddress);
184		return ortn;
185	}
186	CFRelease(srch);
187	return noErr;
188}
189
190static void evalSecTrust(
191	SecTrustRef secTrust,
192	bool quiet)
193{
194	OSStatus ortn;
195	SecTrustResultType			secTrustResult;
196
197	ortn = SecTrustEvaluate(secTrust, &secTrustResult);
198	if(ortn) {
199		/* should never happen */
200		cssmPerror("SecTrustEvaluate", ortn);
201		return;
202	}
203	switch(secTrustResult) {
204		case kSecTrustResultUnspecified:
205			/* cert chain valid, no special UserTrust assignments */
206		case kSecTrustResultProceed:
207			/* cert chain valid AND user explicitly trusts this */
208			if(!quiet) {
209				fprintf(stderr, "Successful\n");
210			}
211			return;
212		case kSecTrustResultDeny:
213		case kSecTrustResultConfirm:
214			/*
215			 * Cert chain may well have verified OK, but user has flagged
216			 * one of these certs as untrustable.
217			 */
218			printf("Not trusted per user-specified Trust level\n");
219			return;
220		default:
221		{
222			/* get low-level TP error */
223			OSStatus tpStatus;
224			ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus);
225			if(ortn) {
226				cssmPerror("SecTrustGetCssmResultCode", ortn);
227				return;
228			}
229			switch(tpStatus) {
230				case CSSMERR_TP_INVALID_ANCHOR_CERT:
231					fprintf(stderr, "Untrusted root\n");
232					return;
233				case CSSMERR_TP_NOT_TRUSTED:
234					/* no root, not even in implicit SSL roots */
235					fprintf(stderr, "No root cert found\n");
236					return;
237				case CSSMERR_TP_CERT_EXPIRED:
238					fprintf(stderr, "Expired cert\n");
239					return;
240				case CSSMERR_TP_CERT_NOT_VALID_YET:
241					fprintf(stderr, "Cert not valid yet\n");
242					break;
243				default:
244					printf("Other cert failure: ");
245					cssmPerror("", tpStatus);
246					return;
247			}
248		}
249	} 	/* SecTrustEvaluate error */
250
251}
252static OSStatus parseSignedData(
253	SecCmsSignedDataRef		signedData,
254	SecArenaPoolRef			arena,			/* used for detached content only */
255	const unsigned char		*detachedData,
256	unsigned				detachedDataLen,
257	CT_Vfy					vfyOp,
258	bool					quiet,
259	bool					parseSignerCert)
260{
261	Boolean b;
262	b = SecCmsSignedDataHasDigests(signedData);
263	if(!quiet) {
264		printf("      has digests   : %s\n", b ? "true" : "false");
265	}
266
267	SecTrustRef secTrust = NULL;
268	OSStatus ortn;
269	SecPolicyRef policy = NULL;
270	SecPolicySearchRef policySearch = NULL;
271
272	ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
273		&CSSMOID_APPLE_X509_BASIC,
274		NULL,
275		&policySearch);
276	if(ortn) {
277		cssmPerror("SecPolicySearchCreate", ortn);
278		return ortn;
279	}
280	ortn = SecPolicySearchCopyNext(policySearch, &policy);
281	if(ortn) {
282		cssmPerror("SecPolicySearchCopyNext", ortn);
283		return ortn;
284	}
285
286	int numSigners = SecCmsSignedDataSignerInfoCount(signedData);
287	if(!quiet) {
288		printf("      num signers   : %d\n", numSigners);
289	}
290	for(int dex=0; dex<numSigners; dex++) {
291		if(!quiet) {
292			fprintf(stderr, "      signer %d      :\n", dex);
293			fprintf(stderr, "         vfy status : ");
294		}
295		Boolean b = SecCmsSignedDataHasDigests(signedData);
296		if(b) {
297			if(detachedData != NULL) {
298				fprintf(stderr, "<provided detachedContent, but msg has digests> ");
299				/* FIXME - does this make sense? Error? */
300			}
301		}
302		else if(detachedData != NULL) {
303			/* digest the detached content */
304			SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData);
305			SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms);
306			CSSM_DATA **digests = NULL;
307
308			SecCmsDigestContextUpdate(digcx, detachedData, detachedDataLen);
309			ortn = SecCmsDigestContextFinishMultiple(digcx, arena, &digests);
310			if(ortn) {
311				fprintf(stderr, "SecCmsDigestContextFinishMultiple() returned %d\n", (int)ortn);
312			}
313			else {
314				SecCmsSignedDataSetDigests(signedData, digestAlgorithms, digests);
315			}
316		}
317		else {
318			fprintf(stderr, "<Msg has no digest: need detachedContent> ");
319		}
320		ortn = SecCmsSignedDataVerifySignerInfo(signedData, dex, NULL,
321			policy, &secTrust);
322		if(ortn) {
323			fprintf(stderr, "vfSignerInfo() returned %d\n", (int)ortn);
324			fprintf(stderr, "         vfy status : ");
325		}
326		if(secTrust == NULL) {
327			fprintf(stderr, "***NO SecTrust available!\n");
328		}
329		else {
330			evalSecTrust(secTrust, quiet);
331		}
332
333		SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, dex);
334		CFStringRef emailAddrs = SecCmsSignerInfoGetSignerCommonName(signerInfo);
335		char emailStr[1000];
336		if(!quiet) {
337			fprintf(stderr, "         signer     : ");
338		}
339		if(emailAddrs == NULL) {
340			fprintf(stderr, "<<SecCmsSignerInfoGetSignerCommonName returned NULL)>>\n");
341		}
342		else {
343			if(!CFStringGetCString(emailAddrs, emailStr, 1000, kCFStringEncodingASCII)) {
344				fprintf(stderr, "*** Error converting email address to C string\n");
345			}
346			else if(!quiet) {
347
348				fprintf(stderr, "%s\n", emailStr);
349			}
350		}
351		if(parseSignerCert) {
352			SecCertificateRef signer;
353			signer = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL);
354			if(signer) {
355				CSSM_DATA certData;
356				ortn = SecCertificateGetData(signer, &certData);
357				if(ortn) {
358					fprintf(stderr, "***Error getting signing cert data***\n");
359					cssmPerror("SecCertificateGetData", ortn);
360				}
361				else {
362					printf("========== Signer Cert==========\n\n");
363					printCert(certData.Data, certData.Length, CSSM_FALSE);
364					printf("========== End Signer Cert==========\n\n");
365				}
366			}
367			else {
368				fprintf(stderr, "***Error getting signing cert ***\n");
369			}
370		}
371	}
372	return ortn;
373}
374
375static OSStatus doParse(
376	const unsigned char *data,
377	unsigned			dataLen,
378	const unsigned char *detachedData,
379	unsigned			detachedDataLen,
380	CT_Vfy				vfyOp,
381	bool				parseSignerCert,
382	bool				quiet,
383	unsigned char		**outData,		// mallocd and RETURNED
384	unsigned			*outDataLen)	// RETURNED
385{
386	if((data == NULL) || (dataLen == 0)) {
387		fprintf(stderr, "***Parse requires input file. Aborting.\n");
388		return paramErr;
389	}
390
391	SecArenaPoolRef arena = NULL;
392	SecArenaPoolCreate(1024, &arena);
393	SecCmsMessageRef cmsMsg = NULL;
394	SecCmsDecoderRef decoder;
395	OSStatus ortn;
396	OSStatus ourRtn = noErr;
397	bool foundOneSigned = false;
398	bool foundOneEnveloped = false;
399
400	ortn = SecCmsDecoderCreate(arena, NULL, NULL, NULL, NULL, NULL, NULL, &decoder);
401	if(ortn) {
402		cssmPerror("SecCmsDecoderCreate", ortn);
403		return ortn;
404	}
405	ortn = SecCmsDecoderUpdate(decoder, data, dataLen);
406	if(ortn) {
407		cssmPerror("SecCmsDecoderUpdate", ortn);
408		return ortn;
409	}
410	ortn = SecCmsDecoderFinish(decoder, &cmsMsg);
411	if(ortn) {
412		cssmPerror("SecCmsDecoderFinish", ortn);
413		return ortn;
414	}
415
416	Boolean b = SecCmsMessageIsSigned(cmsMsg);
417	switch(vfyOp) {
418		case CTV_None:
419			break;
420		case CTV_Sign:
421			if(!b) {
422				fprintf(stderr, "***Expected SignedData, but !SecCmsMessageIsSigned()\n");
423				ourRtn = -1;
424			}
425			break;
426		case CTV_SignEnvelop:
427			if(!b) {
428				fprintf(stderr, "***Expected Signed&Enveloped, but !SecCmsMessageIsSigned()\n");
429				ourRtn = -1;
430			}
431			break;
432		case CTV_Envelop:
433			if(b) {
434				fprintf(stderr, "***Expected EnvelopedData, but SecCmsMessageIsSigned() "
435						"TRUE\n");
436				ourRtn = -1;
437			}
438			break;
439	}
440	int numContentInfos = SecCmsMessageContentLevelCount(cmsMsg);
441	if(!quiet) {
442		fprintf(stderr, "=== CMS message info ===\n");
443		fprintf(stderr, "   Signed           : %s\n", b ? "true" : "false");
444		b = SecCmsMessageIsEncrypted(cmsMsg);
445		fprintf(stderr, "   Encrypted        : %s\n", b ? "true" : "false");
446		b = SecCmsMessageContainsCertsOrCrls(cmsMsg);
447		fprintf(stderr, "   certs/crls       : %s\n", b ? "present" : "not present");
448		fprintf(stderr, "   Num ContentInfos : %d\n", numContentInfos);
449	}
450
451	/* FIXME needs work for CTV_SignEnvelop */
452	OidParser oidParser;
453	for(int dex=0; dex<numContentInfos; dex++) {
454		SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsMsg, dex);
455		if(!quiet) {
456			/* can't use stderr - oidparser is fixed w/stdout */
457			printf("   Content Info %d   :\n", dex);
458			CSSM_OID *typeOid = SecCmsContentInfoGetContentTypeOID(ci);
459			printf("      OID Tag       : ");
460			if(typeOid == NULL) {
461				printf("***NONE FOUND***]n");
462			}
463			else if(typeOid->Length == 0) {
464				printf("***EMPTY***\n");
465			}
466			else {
467				char str[OID_PARSER_STRING_SIZE];
468				oidParser.oidParse(typeOid->Data, typeOid->Length, str);
469				printf("%s\n", str);
470			}
471		}
472		SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
473		switch(tag) {
474			case SEC_OID_PKCS7_SIGNED_DATA:
475			{
476				switch(vfyOp) {
477					case CTV_None:		// caller doesn't care
478					case CTV_Sign:		// got what we wanted
479						break;
480					case CTV_Envelop:
481						fprintf(stderr, "***Expected EnvelopedData, got SignedData\n");
482						ourRtn = -1;
483						break;
484					case CTV_SignEnvelop:
485						printf("CTV_SignEnvelop code on demand\n");
486						break;
487				}
488				foundOneSigned = true;
489				SecCmsSignedDataRef sd =
490					(SecCmsSignedDataRef) SecCmsContentInfoGetContent(ci);
491				parseSignedData(sd, arena,
492					detachedData, detachedDataLen,
493					vfyOp, quiet, parseSignerCert);
494				break;
495			}
496			case SEC_OID_PKCS7_DATA:
497			case SEC_OID_OTHER:
498			    break;
499			case SEC_OID_PKCS7_ENVELOPED_DATA:
500				foundOneEnveloped = true;
501				if(vfyOp == CTV_Sign) {
502					fprintf(stderr, "***Expected SignedData, EnvelopedData\n");
503					ourRtn = -1;
504					break;
505				}
506			case SEC_OID_PKCS7_ENCRYPTED_DATA:
507				switch(vfyOp) {
508					case CTV_None:
509						break;
510					case CTV_Sign:
511						fprintf(stderr, "***Expected SignedData, got EncryptedData\n");
512						ourRtn = -1;
513						break;
514					case CTV_Envelop:
515						fprintf(stderr, "***Expected EnvelopedData, got EncryptedData\n");
516						ourRtn = -1;
517						break;
518					case CTV_SignEnvelop:
519						printf("CTV_SignEnvelop code on demand\n");
520						break;
521				}
522				break;
523			default:
524				fprintf(stderr, "      other content type TBD\n");
525		}
526	}
527	if(outData) {
528		CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsMsg);
529		if(odata == NULL) {
530			fprintf(stderr, "***No inner content available\n");
531		}
532		else {
533			*outData = (unsigned char *)malloc(odata->Length);
534			memmove(*outData, odata->Data, odata->Length);
535			*outDataLen = odata->Length;
536		}
537	}
538	if(arena) {
539		SecArenaPoolFree(arena, false);
540	}
541	switch(vfyOp) {
542		case CTV_None:
543			break;
544		case CTV_Sign:
545			if(!foundOneSigned) {
546				fprintf(stderr, "Expected signed, never saw a SignedData\n");
547				ourRtn = -1;
548			}
549			break;
550		case CTV_Envelop:
551			if(!foundOneEnveloped) {
552				fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n");
553				ourRtn = -1;
554			}
555			break;
556		case CTV_SignEnvelop:
557			if(!foundOneSigned) {
558				fprintf(stderr, "Expected signed, never saw a SignedData\n");
559				ourRtn = -1;
560			}
561			if(!foundOneEnveloped) {
562				fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n");
563				ourRtn = -1;
564			}
565			break;
566	}
567	/* free decoder? cmsMsg? */
568	return ourRtn;
569}
570
571/*
572 * Common encode routine.
573 */
574#if 1
575/* the simple way, when 3655861 is fixed */
576static OSStatus encodeCms(
577	SecCmsMessageRef	cmsMsg,
578	const unsigned char *inData,		// add in this
579	unsigned			inDataLen,
580	unsigned char		**outData,		// mallocd and RETURNED
581	unsigned			*outDataLen)	// RETURNED
582{
583	SecArenaPoolRef arena = NULL;
584	SecArenaPoolCreate(1024, &arena);
585	CSSM_DATA cdataIn = {inDataLen, (uint8 *)inData};
586	CSSM_DATA cdataOut = {0, NULL};
587
588	OSStatus ortn = SecCmsMessageEncode(cmsMsg, &cdataIn, arena, &cdataOut);
589	if((ortn == noErr) && (cdataOut.Length != 0)) {
590		*outData = (unsigned char *)malloc(cdataOut.Length);
591		memmove(*outData, cdataOut.Data, cdataOut.Length);
592		*outDataLen = cdataOut.Length;
593	}
594	else {
595		cssmPerror("SecCmsMessageEncode", ortn);
596		*outData = NULL;
597		*outDataLen = 0;
598	}
599	SecArenaPoolFree(arena, false);
600	return ortn;
601}
602
603#else
604
605/* the hard way back when SecCmsMessageEncode() didn't work */
606static OSStatus encodeCms(
607	SecCmsMessageRef	cmsMsg,
608	const unsigned char *inData,		// add in this
609	unsigned			inDataLen,
610	unsigned char		**outData,		// mallocd and RETURNED
611	unsigned			*outDataLen)	// RETURNED
612{
613	SecArenaPoolRef arena = NULL;
614	SecArenaPoolCreate(1024, &arena);
615	SecCmsEncoderRef cmsEnc = NULL;
616	CSSM_DATA output = { 0, NULL };
617	OSStatus ortn;
618
619	ortn = SecCmsEncoderCreate(cmsMsg,
620		NULL, NULL,			// no callback
621		&output, arena,		// data goes here
622		NULL, NULL,			// no password callback (right?)
623		NULL, NULL,			// decrypt key callback
624		NULL, NULL,			// detached digests
625		&cmsEnc);
626	if(ortn) {
627		cssmPerror("SecKeychainItemCopyKeychain", ortn);
628		goto errOut;
629	}
630	ortn = SecCmsEncoderUpdate(cmsEnc, (char *)inData, inDataLen);
631	if(ortn) {
632		cssmPerror("SecCmsEncoderUpdate", ortn);
633		goto errOut;
634	}
635	ortn = SecCmsEncoderFinish(cmsEnc);
636	if(ortn) {
637		cssmPerror("SecCMsEncoderFinish", ortn);
638		goto errOut;
639	}
640
641	/* Did we get any data? */
642	if(output.Length) {
643		*outData = (unsigned char *)malloc(output.Length);
644		memmove(*outData, output.Data, output.Length);
645		*outDataLen = output.Length;
646	}
647	else {
648		*outData = NULL;
649		*outDataLen = 0;
650	}
651errOut:
652	if(arena) {
653		SecArenaPoolFree(arena, false);
654	}
655	return ortn;
656}
657
658#endif
659
660static OSStatus doSign(
661	SecIdentityRef		signerId,
662	const unsigned char *inData,
663	unsigned			inDataLen,
664	bool				detachedContent,
665	const CSSM_OID		*eContentType,	// OPTIONAL
666	unsigned char		**outData,		// mallocd and RETURNED
667	unsigned			*outDataLen)	// RETURNED
668{
669	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
670		fprintf(stderr, "***Sign requires input file. Aborting.\n");
671		return paramErr;
672	}
673	if(signerId == NULL) {
674		fprintf(stderr, "***Sign requires a signing identity. Aborting.\n");
675		return paramErr;
676	}
677
678	SecCmsMessageRef cmsMsg = NULL;
679    SecCmsContentInfoRef contentInfo = NULL;
680    SecCmsSignedDataRef signedData = NULL;
681	SecCertificateRef ourCert = NULL;
682    SecCmsSignerInfoRef signerInfo;
683	OSStatus ortn;
684	SecKeychainRef ourKc = NULL;
685
686	ortn = SecIdentityCopyCertificate(signerId, &ourCert);
687	if(ortn) {
688		cssmPerror("SecIdentityCopyCertificate", ortn);
689		return ortn;
690	}
691	ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
692	if(ortn) {
693		cssmPerror("SecKeychainItemCopyKeychain", ortn);
694		goto errOut;
695	}
696
697    // build chain of objects: message->signedData->data
698	cmsMsg = SecCmsMessageCreate(NULL);
699	if(cmsMsg == NULL) {
700		fprintf(stderr, "***Error creating SecCmsMessageRef\n");
701		ortn = -1;
702		goto errOut;
703	}
704	signedData = SecCmsSignedDataCreate(cmsMsg);
705	if(signedData == NULL) {
706		printf("***Error creating SecCmsSignedDataRef\n");
707		ortn = -1;
708		goto errOut;
709	}
710	contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
711	ortn = SecCmsContentInfoSetContentSignedData(cmsMsg, contentInfo, signedData);
712	if(ortn) {
713		cssmPerror("SecCmsContentInfoSetContentSignedData", ortn);
714		goto errOut;
715	}
716    contentInfo = SecCmsSignedDataGetContentInfo(signedData);
717	if(eContentType != NULL) {
718		ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo,
719			NULL /* data */,
720			detachedContent,
721			eContentType);
722		if(ortn) {
723			cssmPerror("SecCmsContentInfoSetContentData", ortn);
724			goto errOut;
725		}
726	}
727	else {
728		ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */,
729			detachedContent);
730		if(ortn) {
731			cssmPerror("SecCmsContentInfoSetContentData", ortn);
732			goto errOut;
733		}
734	}
735
736    /*
737     * create & attach signer information
738     */
739    signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1);
740    if (signerInfo == NULL) {
741		fprintf(stderr, "***Error on SecCmsSignerInfoCreate\n");
742		ortn = -1;
743		goto errOut;
744	}
745    /* we want the cert chain included for this one */
746	/* FIXME - what's the significance of the usage? */
747	ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner);
748	if(ortn) {
749		cssmPerror("SecCmsSignerInfoIncludeCerts", ortn);
750		goto errOut;
751	}
752
753	/* other options go here - signing time, etc. */
754
755	ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
756	if(ortn) {
757		cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
758		goto errOut;
759	}
760	ortn = SecCmsSignedDataAddCertificate(signedData, ourCert);
761	if(ortn) {
762		cssmPerror("SecCmsSignedDataAddCertificate", ortn);
763		goto errOut;
764	}
765
766	ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
767	if(ortn) {
768		cssmPerror("SecCmsSignedDataAddSignerInfo", ortn);
769		goto errOut;
770	}
771
772	/* go */
773	ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
774errOut:
775	/* free resources */
776	if(cmsMsg) {
777		SecCmsMessageDestroy(cmsMsg);
778	}
779	if(ourCert) {
780		CFRelease(ourCert);
781	}
782	if(ourKc) {
783		CFRelease(ourKc);
784	}
785	return ortn;
786}
787
788static OSStatus doEncrypt(
789	SecCertificateRef   recipCert,		// eventually more than one
790	const unsigned char *inData,
791	unsigned			inDataLen,
792	unsigned char		**outData,		// mallocd and RETURNED
793	unsigned			*outDataLen)	// RETURNED
794{
795	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
796		fprintf(stderr, "***Encrypt requires input file. Aborting.\n");
797		return paramErr;
798	}
799	if(recipCert == NULL) {
800		fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n");
801		return paramErr;
802	}
803
804	SecCmsMessageRef cmsMsg = NULL;
805    SecCmsContentInfoRef contentInfo = NULL;
806    SecCmsEnvelopedDataRef envelopedData = NULL;
807	SecCmsRecipientInfoRef recipientInfo = NULL;
808	OSStatus ortn;
809	SecCertificateRef allCerts[2] = { recipCert, NULL};
810
811	SECOidTag algorithmTag;
812    int keySize;
813
814	ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
815	if(ortn) {
816		cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
817		return ortn;
818	}
819
820    // build chain of objects: message->envelopedData->data
821	cmsMsg = SecCmsMessageCreate(NULL);
822	if(cmsMsg == NULL) {
823		fprintf(stderr, "***Error creating SecCmsMessageRef\n");
824		ortn = -1;
825		goto errOut;
826	}
827	envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize);
828	if(envelopedData == NULL) {
829		fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n");
830		ortn = -1;
831		goto errOut;
832	}
833	contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
834	ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData);
835	if(ortn) {
836		cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn);
837		goto errOut;
838	}
839    contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
840	ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false);
841	if(ortn) {
842		cssmPerror("SecCmsContentInfoSetContentData", ortn);
843		goto errOut;
844	}
845
846    /*
847     * create & attach recipient information
848     */
849	recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
850	ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
851	if(ortn) {
852		cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
853		goto errOut;
854	}
855
856
857	/* go */
858	ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen);
859errOut:
860	/* free resources */
861	if(cmsMsg) {
862		SecCmsMessageDestroy(cmsMsg);
863	}
864	return ortn;
865}
866
867/* create nested message: msg = EnvelopedData(SignedData(inData)) */
868static OSStatus doSignEncrypt(
869	SecCertificateRef   recipCert,		// encryption recipient
870	SecIdentityRef		signerId,		// signer
871	const CSSM_OID		*eContentType,	// OPTIONAL - for signedData
872	const unsigned char *inData,
873	unsigned			inDataLen,
874	unsigned char		**outData,		// mallocd and RETURNED
875	unsigned			*outDataLen)	// RETURNED
876{
877	if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
878		fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n");
879		return paramErr;
880	}
881	if(recipCert == NULL) {
882		fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
883		return paramErr;
884	}
885	if(signerId == NULL) {
886		fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
887		return paramErr;
888	}
889
890	OSStatus ortn;
891	unsigned char *signedData = NULL;
892	unsigned signedDataLen = 0;
893	SecCmsMessageRef cmsMsg = NULL;
894    SecCmsContentInfoRef contentInfo = NULL;
895    SecCmsEnvelopedDataRef envelopedData = NULL;
896	SecCmsRecipientInfoRef recipientInfo = NULL;
897	SecCertificateRef allCerts[2] = { recipCert, NULL};
898	SECOidTag algorithmTag;
899    int keySize;
900
901	/* first get a SignedData */
902	ortn = doSign(signerId, inData, inDataLen,
903		false,		/* can't do detached content here */
904		eContentType,
905		&signedData, &signedDataLen);
906	if(ortn) {
907		printf("***Error generating inner signedData. Aborting.\n");
908		return ortn;
909	}
910
911	/* extract just the content - don't need the whole ContentINfo */
912	unsigned char *signedDataContent = NULL;
913	unsigned signedDataContentLen = 0;
914	ortn = ContentInfoContent(signedData, signedDataLen, &signedDataContent, &signedDataContentLen);
915	if(ortn) {
916		goto errOut;
917	}
918
919	/* now wrap that in an EnvelopedData */
920	ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize);
921	if(ortn) {
922		cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn);
923		return ortn;
924	}
925
926    // build chain of objects: message->envelopedData->data
927	cmsMsg = SecCmsMessageCreate(NULL);
928	if(cmsMsg == NULL) {
929		fprintf(stderr, "***Error creating SecCmsMessageRef\n");
930		ortn = -1;
931		goto errOut;
932	}
933	envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize);
934	if(envelopedData == NULL) {
935		fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n");
936		ortn = -1;
937		goto errOut;
938	}
939	contentInfo = SecCmsMessageGetContentInfo(cmsMsg);
940	ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData);
941	if(ortn) {
942		cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn);
943		goto errOut;
944	}
945    contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
946
947	/* here's the difference: we override the 'data' content with a SignedData type,
948	 * but we fool the smime lib into thinking it's a plain old data so it doesn't try
949	 * to encode the SignedData */
950	ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo,
951		NULL /* data */,
952		false,
953		&CSSMOID_PKCS7_SignedData);
954	if(ortn) {
955		cssmPerror("SecCmsContentInfoSetContentData", ortn);
956		goto errOut;
957	}
958
959    /*
960     * create & attach recipient information
961     */
962	recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert);
963	ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
964	if(ortn) {
965		cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn);
966		goto errOut;
967	}
968
969
970	/* go */
971	ortn = encodeCms(cmsMsg, signedDataContent, signedDataContentLen, outData, outDataLen);
972errOut:
973	/* free resources */
974	if(cmsMsg) {
975		SecCmsMessageDestroy(cmsMsg);
976	}
977	if(signedData) {
978		free(signedData);
979	}
980	if(signedDataContent) {
981		free(signedDataContent);
982	}
983	return ortn;
984}
985
986int main(int argc, char **argv)
987{
988	if(argc < 2) {
989		usage(argv);
990	}
991
992	CT_Op op;
993	bool needId = false;
994	if(!strcmp(argv[1], "sign")) {
995		op = CTO_Sign;
996		needId = true;
997	}
998	else if(!strcmp(argv[1], "envel")) {
999		op = CTO_Envelop;
1000	}
1001	else if(!strcmp(argv[1], "signEnv")) {
1002		op = CTO_SignEnvelop;
1003		needId = true;
1004	}
1005	else if(!strcmp(argv[1], "parse")) {
1006		op = CTO_Parse;
1007	}
1008	else {
1009		fprintf(stderr, "***Unrecognized cmd.\n");
1010		usage(argv);
1011	}
1012
1013	extern int optind;
1014	extern char *optarg;
1015	int arg;
1016
1017	/* optional args */
1018	const char *keychainName = NULL;
1019	char *inFileName = NULL;
1020	char *outFileName = NULL;
1021	bool detachedContent = false;
1022	char *detachedFile = NULL;
1023	bool useIdPicker = false;
1024	char *recipient = NULL;
1025	bool quiet = false;
1026	bool parseSignerCert = false;
1027	CT_Vfy vfyOp = CTV_None;
1028	const CSSM_OID *eContentType = NULL;
1029
1030	optind = 2;
1031	while ((arg = getopt(argc, argv, "i:o:k:pr:e:dD:qcv:")) != -1) {
1032		switch (arg) {
1033			case 'i':
1034				inFileName = optarg;
1035				break;
1036			case 'o':
1037				outFileName = optarg;
1038				break;
1039			case 'k':
1040				keychainName = optarg;
1041				break;
1042			case 'p':
1043				useIdPicker = true;
1044				break;
1045			case 'r':
1046				recipient = optarg;
1047				break;
1048			case 'c':
1049				parseSignerCert = true;
1050				break;
1051			case 'v':
1052				if(!strcmp(optarg, "sign")) {
1053					vfyOp = CTV_Sign;
1054				}
1055				else if(!strcmp(optarg, "encr")) {
1056					vfyOp = CTV_Envelop;
1057				}
1058				else if(!strcmp(optarg, "signEnv")) {
1059					vfyOp = CTV_SignEnvelop;
1060				}
1061				else {
1062					usage(argv);
1063				}
1064				break;
1065			case 'e':
1066				switch(optarg[0]) {
1067					case 'a':
1068						eContentType = &CSSMOID_PKINIT_AUTH_DATA;
1069						break;
1070					case 'r':
1071						eContentType = &CSSMOID_PKINIT_RKEY_DATA;
1072						break;
1073					default:
1074						usage(argv);
1075				}
1076				break;
1077			case 'd':
1078				if(op != CTO_Sign) {
1079					printf("-d only valid for op sign\n");
1080					exit(1);
1081				}
1082				detachedContent = true;
1083				break;
1084			case 'D':
1085				if(op != CTO_Parse) {
1086					printf("-D only valid for op sign\n");
1087					exit(1);
1088				}
1089				detachedFile = optarg;
1090				break;
1091			case 'q':
1092				quiet = true;
1093				break;
1094			default:
1095			case '?':
1096				usage(argv);
1097		}
1098	}
1099	if(optind != argc) {
1100		/* getopt does not return '?' */
1101		usage(argv);
1102	}
1103
1104	SecIdentityRef idRef = NULL;
1105	SecKeychainRef kcRef = NULL;
1106	SecCertificateRef recipientCert = NULL;
1107	unsigned char *inData = NULL;
1108	unsigned inDataLen = 0;
1109	unsigned char *outData = NULL;
1110	unsigned outDataLen = 0;
1111	unsigned char *detachedData = NULL;
1112	unsigned detachedDataLen = 0;
1113	OSStatus ortn;
1114
1115	if(inFileName) {
1116		if(readFile(inFileName, &inData, &inDataLen)) {
1117			fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName);
1118			exit(1);
1119		}
1120	}
1121	if(detachedFile) {
1122		if(readFile(detachedFile, &detachedData, &detachedDataLen)) {
1123			fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile);
1124			exit(1);
1125		}
1126	}
1127	if(keychainName) {
1128		ortn = SecKeychainOpen(keychainName, &kcRef);
1129		if(ortn) {
1130			cssmPerror("SecKeychainOpen", ortn);
1131			exit(1);
1132		}
1133	}
1134	if(useIdPicker) {
1135		ortn = sslSimpleIdentPicker(kcRef, &idRef);
1136		if(ortn) {
1137			fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n");
1138			exit(1);
1139		}
1140	}
1141	else if(needId) {
1142		/* use first identity in specified keychain */
1143		CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE,
1144			NULL,		// no verify policy
1145			NULL);
1146		if(array == NULL) {
1147			fprintf(stderr, "***Error finding a signing cert. Aborting.\n");
1148			exit(1);
1149		}
1150		idRef = (SecIdentityRef)CFArrayGetValueAtIndex(array, 0);
1151		if(idRef == NULL) {
1152			fprintf(stderr, "***No identities found. Aborting.\n");
1153			exit(1);
1154		}
1155		CFRetain(idRef);
1156		CFRelease(array);
1157	}
1158	if(recipient) {
1159		ortn = findCert(recipient, kcRef, &recipientCert);
1160		if(ortn) {
1161			exit(1);
1162		}
1163	}
1164
1165	switch(op) {
1166		case CTO_Sign:
1167			ortn = doSign(idRef, inData, inDataLen,
1168				detachedContent, eContentType,
1169				&outData, &outDataLen);
1170			break;
1171		case CTO_Envelop:
1172			if(recipientCert == NULL) {
1173				if(idRef == NULL) {
1174					printf("***Need a recipient or an identity to encrypt\n");
1175					exit(1);
1176				}
1177				ortn = SecIdentityCopyCertificate(idRef, &recipientCert);
1178				if(ortn) {
1179					cssmPerror("SecIdentityCopyCertificate", ortn);
1180					exit(1);
1181				}
1182			}
1183			ortn = doEncrypt(recipientCert, inData, inDataLen, &outData, &outDataLen);
1184			break;
1185		case CTO_SignEnvelop:
1186			ortn = doSignEncrypt(recipientCert, idRef, eContentType,
1187				inData, inDataLen, &outData, &outDataLen);
1188			break;
1189		case CTO_Parse:
1190			ortn = doParse(inData, inDataLen,
1191				detachedData, detachedDataLen,
1192				vfyOp, parseSignerCert, quiet,
1193				&outData, &outDataLen);
1194			break;
1195	}
1196	if(ortn) {
1197		goto errOut;
1198	}
1199	if(outData && outFileName) {
1200		if(writeFile(outFileName, outData, outDataLen)) {
1201			fprintf(stderr, "***Error writing to %s.\n", outFileName);
1202			ortn = -1;
1203		}
1204		else {
1205			if(!quiet) {
1206				fprintf(stderr, "...wrote %u bytes to %s.\n", outDataLen, outFileName);
1207			}
1208		}
1209	}
1210	else if(outData) {
1211		fprintf(stderr, "...generated %u bytes but no place to write it.\n", outDataLen);
1212	}
1213	else if(outFileName) {
1214		fprintf(stderr, "...nothing to write to file %s.\n", outFileName);
1215		/* assume this is an error, caller wanted something */
1216		ortn = -1;
1217	}
1218errOut:
1219	return ortn;
1220}
1221