#include <Security/Security.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utilLib/common.h>
#include <security_cdsa_utils/cuFileIo.h>
#include <security_cdsa_utils/cuPrintCert.h>
#include <clAppUtils/identPicker.h>
#include <clAppUtils/sslAppUtils.h>
#include <security_cdsa_utils/cuOidParser.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecIdentityPriv.h>
#include <Security/CMSEncoder.h>
#include <Security/CMSDecoder.h>
#include <Security/CMSPrivate.h>
#include <Security/SecCertificate.h>
#include <Security/oidsattr.h>
#define CFRELEASE(cfr) if(cfr != NULL) { CFRelease(cfr); }
static SecKeychainRef keychain_open(const char *name);
static void usage(char **argv)
{
printf("Usage: %s cmd [option ...]\n", argv[0]);
printf("cmd values:\n");
printf(" sign -- create signedData\n");
printf(" envel -- create envelopedData\n");
printf(" signEnv -- create nested EnvelopedData(signedData(data))\n");
printf(" certs -- create certs-only CMS msg\n");
printf(" parse -- parse a CMS message file\n");
printf("Input/output options:\n");
printf(" -i infile\n");
printf(" -o outfile\n");
printf(" -D detachedContent -- detached content (parse only)\n");
printf(" -d detached -- infile contains detached content (sign only)\n");
printf(" -f certFileBase -- dump all certs to certFileBase\n");
printf("Signer and recipient options:\n");
printf(" -k keychain -- Keychain to search for certs\n");
printf(" -p -- Use identity picker\n");
printf(" -r recipient -- add recipient (via email address) of enveloped data\n");
printf(" -R recipCertFile -- add recipient (via cert from file) of enveloped data\n");
printf(" -S signerEmail -- add signer email address\n");
printf(" -C cert -- add (general) signedData cert\n");
printf("Misc. options:\n");
printf(" -e eContentType -- a(uthData)|r(keyData)\n");
printf(" -m -- multi updates; default is one-shot\n");
printf(" -1 (one) -- custom encoder/decoder\n");
printf(" -2 -- fetch SecCmsMessageRef\n");
printf(" -c -- parse signer certs\n");
printf(" -a [ceEt] -- Signed Attributes: c=SmimeCaps,\n");
printf(" e=EncrPrefs, E=MSEncrPrefs, t=signingTime\n");
printf(" -A anchorFile -- Verify certs using specified anchor cert\n");
printf(" -M -- Do SecTrustEvaluate manually\n");
printf(" -t certChainMode -- none|signer|chain|chainWithRoot; default is chain\n");
printf(" -l -- loop & pause for malloc debug\n");
printf(" -q -- quiet\n");
printf(" -Z -- silent, no output at all except for errors\n");
printf("Verification options:\n");
printf(" -v sign|encr|signEnv -- verify message is signed/encrypted/both\n");
printf(" -s numSigners -- verify msg has specified number of signers\n");
printf(" -E eContentType -- verify a(authData)|r(keyData)|d(data)\n");
printf(" -N numCerts -- verify number of certs\n");
exit(1);
}
typedef enum {
CTO_Sign,
CTO_Envelop,
CTO_SignEnvelop,
CTO_CertsOnly,
CTO_Parse
} CT_Op;
typedef enum {
CTV_None,
CTV_Sign,
CTV_Envelop,
CTV_SignEnvelop
} CT_Vfy;
#define OID_PKINIT 0x2B, 6, 1, 5, 2, 3
#define OID_PKINIT_LEN 6
static const uint8 OID_PKINIT_AUTH_DATA[] = {OID_PKINIT, 1};
static const uint8 OID_PKINIT_DH_KEY_DATA[] = {OID_PKINIT, 2};
static const uint8 OID_PKINIT_RKEY_DATA[] = {OID_PKINIT, 3};
static const uint8 OID_PKINIT_KP_CLIENTAUTH[] = {OID_PKINIT, 3};
static const uint8 OID_PKINIT_KPKDC[] = {OID_PKINIT, 5};
static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA =
{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA};
static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA =
{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA};
static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA =
{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA};
static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH =
{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH};
static const CSSM_OID CSSMOID_PKINIT_KPKDC =
{OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC};
static OSStatus findCert(
const char *emailAddress,
CFTypeRef kcArArray, SecCertificateRef *cert)
{
OSStatus ortn;
SecKeychainSearchRef srch;
SecKeychainAttributeList attrList;
SecKeychainAttribute attr;
attr.tag = kSecAlias;
attr.length = strlen(emailAddress);
attr.data = (void *)emailAddress;
attrList.count = 1;
attrList.attr = &attr;
ortn = SecKeychainSearchCreateFromAttributes(kcArArray,
kSecCertificateItemClass,
&attrList,
&srch);
if(ortn) {
cssmPerror("SecKeychainSearchCreateFromAttributes", ortn);
return ortn;
}
ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert);
if(ortn) {
printf("***No certs found matching recipient %s. Aborting.\n",
emailAddress);
return ortn;
}
CFRelease(srch);
return noErr;
}
static SecCertificateRef readCertFile(
const char *fileName)
{
unsigned char *certData = NULL;
unsigned certDataLen;
SecCertificateRef rtnCert = NULL;
if(readFile(fileName, &certData, &certDataLen)) {
printf("***Error reading %s. Aborting.\n", fileName);
return NULL;
}
CSSM_DATA cssmCert = {certDataLen, (uint8 *)certData};
OSStatus ortn = SecCertificateCreateFromData(&cssmCert,
CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER,
&rtnCert);
if(ortn) {
cssmPerror("SecCertificateCreateFromData", ortn);
printf("***Error creating cert fromn %s. Aborting.\n", fileName);
}
free(certData);
return rtnCert;
}
static int dumpCertFiles(
CFArrayRef allCerts,
const char *fileBase,
bool quiet)
{
char fileName[200];
if(allCerts == NULL) {
printf("...no certs to write.\n");
return 0;
}
CFIndex numCerts = CFArrayGetCount(allCerts);
if(numCerts == 0) {
printf("...no certs to write.\n");
return 0;
}
for(CFIndex dex=0; dex<numCerts; dex++) {
SecCertificateRef secCert =
(SecCertificateRef)CFArrayGetValueAtIndex(allCerts, dex);
CSSM_DATA certData;
OSStatus ortn;
ortn = SecCertificateGetData(secCert, &certData);
if(ortn) {
cssmPerror("SecCertificateGetData", ortn);
return -1;
}
sprintf(fileName, "%s_%u.cer", fileBase, (unsigned)dex);
if(writeFile(fileName, certData.Data, certData.Length)) {
printf("***Error writing cert data to %s. Aborting.\n", fileName);
return 1;
}
else if(!quiet) {
printf("...wrote %u bytes to %s.\n",
(unsigned)certData.Length, fileName);
}
}
return 0;
}
static OSStatus updateEncoder(
CMSEncoderRef cmsEncoder,
const unsigned char *inData,
unsigned inDataLen)
{
unsigned toMove = inDataLen;
unsigned thisMove;
while(toMove != 0) {
thisMove = genRand(1, toMove);
OSStatus ortn = CMSEncoderUpdateContent(cmsEncoder, inData, thisMove);
if(ortn) {
cssmPerror("CMSEncoderUpdateContent", ortn);
return ortn;
}
toMove -= thisMove;
inData += thisMove;
}
return noErr;
}
static OSStatus updateDecoder(
CMSDecoderRef cmsDecoder,
const unsigned char *inData,
unsigned inDataLen)
{
unsigned toMove = inDataLen;
unsigned thisMove;
while(toMove != 0) {
thisMove = genRand(1, toMove);
OSStatus ortn = CMSDecoderUpdateMessage(cmsDecoder, inData, thisMove);
if(ortn) {
cssmPerror("CMSDecoderUpdateMessage", ortn);
return ortn;
}
toMove -= thisMove;
inData += thisMove;
}
return noErr;
}
#define TRUST_STRING_MAX 128
static OSStatus evalSecTrust(
SecTrustRef secTrust,
CFMutableArrayRef anchorArray, char *trustStr, bool quiet)
{
OSStatus ortn;
SecTrustResultType secTrustResult;
if(anchorArray) {
ortn = SecTrustSetAnchorCertificates(secTrust, anchorArray);
if(ortn) {
cssmPerror("SecTrustSetAnchorCertificates", ortn);
return ortn;
}
}
ortn = SecTrustEvaluate(secTrust, &secTrustResult);
if(ortn) {
cssmPerror("SecTrustEvaluate", ortn);
return ortn;
}
switch(secTrustResult) {
case kSecTrustResultUnspecified:
case kSecTrustResultProceed:
sprintf(trustStr, "Successful\n");
return noErr;
case kSecTrustResultDeny:
case kSecTrustResultConfirm:
sprintf(trustStr, "Not trusted per user-specified Trust level\n");
return errSecInvalidTrustSetting;
default:
{
OSStatus tpStatus;
ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus);
if(ortn) {
cssmPerror("SecTrustGetCssmResultCode", ortn);
return ortn;
}
switch(tpStatus) {
case CSSMERR_TP_INVALID_ANCHOR_CERT:
sprintf(trustStr, "Untrusted root\n");
break;
case CSSMERR_TP_NOT_TRUSTED:
sprintf(trustStr, "No root cert found\n");
break;
case CSSMERR_TP_CERT_EXPIRED:
sprintf(trustStr, "Expired cert\n");
break;
case CSSMERR_TP_CERT_NOT_VALID_YET:
sprintf(trustStr, "Cert not valid yet\n");
break;
default:
sprintf(trustStr, "Other cert failure (%s)",
cssmErrToStr(tpStatus));
break;
}
return tpStatus;
}
}
}
static OSStatus doParse(
const unsigned char *data,
unsigned dataLen,
const unsigned char *detachedContent,
unsigned detachedContentLen,
bool multiUpdate,
CT_Vfy vfyOp,
const CSSM_OID *eContentVfy, int numSignersVfy, int numCertsVfy, bool parseSignerCert,
const char *certFileBase, bool customDecoder, bool manTrustEval, CFMutableArrayRef anchorArray, bool quiet,
CFDataRef *outData) {
if((data == NULL) || (dataLen == 0)) {
fprintf(stderr, "***Parse requires input file. Aborting.\n");
return paramErr;
}
CMSDecoderRef cmsDecoder;
size_t numSigners;
Boolean isEncrypted;
CFArrayRef allCerts = NULL;
unsigned signerDex;
SecPolicyRef policy = NULL;
SecPolicySearchRef policySearch = NULL;
CFIndex numCerts = 0;
int addDetachedAfterDecode = 0;
SecArenaPoolRef arena = NULL;
OSStatus ourRtn = noErr;
OSStatus ortn = noErr;
OSStatus trustRtn = noErr;
int vfyErr = 0;
ortn = CMSDecoderCreate(&cmsDecoder);
if(ortn) {
cssmPerror("CMSDecoderCreate", ortn);
return ortn;
}
if(detachedContent != NULL) {
addDetachedAfterDecode = genRand(0, 1);
if(!addDetachedAfterDecode) {
CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen);
ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach);
CFRelease(cfDetach);
if(ortn) {
cssmPerror("CMSDecoderSetDetachedContent", ortn);
goto errOut;
}
}
}
if(customDecoder) {
SecCmsDecoderRef coder = NULL;
ortn = SecArenaPoolCreate(1024, &arena);
if(ortn) {
cssmPerror("SecArenaPoolCreate", ortn);
goto errOut;
}
ortn = SecCmsDecoderCreate(arena,
NULL, NULL, NULL, NULL, NULL, NULL, &coder);
if(ortn) {
cssmPerror("SecCmsDecoderCreate", ortn);
goto errOut;
}
ortn = CMSDecoderSetDecoder(cmsDecoder, coder);
if(ortn) {
cssmPerror("CMSDecoderSetDecoder", ortn);
goto errOut;
}
else if(!quiet) {
printf("...set up custom SecCmsDecoderRef\n");
}
}
if(multiUpdate) {
ortn = updateDecoder(cmsDecoder, data, dataLen);
if(ortn) {
goto errOut;
}
}
else {
ortn = CMSDecoderUpdateMessage(cmsDecoder, data, dataLen);
if(ortn) {
cssmPerror("CMSDecoderUpdateMessage", ortn);
goto errOut;
}
}
ortn = CMSDecoderFinalizeMessage(cmsDecoder);
if(ortn) {
cssmPerror("CMSDecoderFinalizeMessage", ortn);
goto errOut;
}
if(addDetachedAfterDecode) {
CFDataRef cfDetach = CFDataCreate(NULL, detachedContent, detachedContentLen);
ortn = CMSDecoderSetDetachedContent(cmsDecoder, cfDetach);
CFRelease(cfDetach);
if(ortn) {
cssmPerror("CMSDecoderSetDetachedContent", ortn);
goto errOut;
}
}
ortn = CMSDecoderGetNumSigners(cmsDecoder, &numSigners);
if(ortn) {
cssmPerror("CMSDecoderGetNumSigners", ortn);
goto errOut;
}
ortn = CMSDecoderIsContentEncrypted(cmsDecoder, &isEncrypted);
if(ortn) {
cssmPerror("CMSDecoderIsContentEncrypted", ortn);
goto errOut;
}
ortn = CMSDecoderCopyAllCerts(cmsDecoder, &allCerts);
if(ortn) {
cssmPerror("CMSDecoderCopyAllCerts", ortn);
goto errOut;
}
if(allCerts) {
numCerts = CFArrayGetCount(allCerts);
}
switch(vfyOp) {
case CTV_None:
break;
case CTV_Sign:
if((numSigners == 0) && (allCerts == NULL)) {
fprintf(stderr, "***Expected SignedData, but no signersFound\n");
vfyErr = 1;
}
if(isEncrypted) {
fprintf(stderr, "***Expected SignedData, but msg IS encrypted\n");
vfyErr = 1;
}
break;
case CTV_SignEnvelop:
if(numSigners == 0) {
fprintf(stderr, "***Expected Signed&Enveloped, but no signersFound\n");
vfyErr = 1;
}
if(!isEncrypted) {
fprintf(stderr, "***Expected Signed&Enveloped, but msg not encrypted\n");
vfyErr = 1;
}
break;
case CTV_Envelop:
if(numSigners != 0) {
fprintf(stderr, "***Expected EnvelopedData, but signers found\n");
vfyErr = 1;
}
if(!isEncrypted) {
fprintf(stderr, "***Expected EnvelopedData, but msg not encrypted\n");
vfyErr = 1;
}
break;
}
if(numSignersVfy >= 0) {
if((unsigned)numSignersVfy != numSigners) {
fprintf(stderr, "***Expected %d signers; found %lu\n",
numSignersVfy, numSigners);
vfyErr = 1;
}
}
if(numCertsVfy >= 0) {
if((int)numCerts != numCertsVfy) {
fprintf(stderr, "***Expected %d certs; found %d\n",
numCertsVfy, (int)numCerts);
vfyErr = 1;
}
}
if(!quiet) {
fprintf(stderr, "=== CMS message info ===\n");
fprintf(stderr, " Num Signers : %lu\n", (unsigned long)numSigners);
fprintf(stderr, " Encrypted : %s\n", isEncrypted ? "true" : "false");
fprintf(stderr, " Num Certs : %lu\n",
allCerts ? (unsigned long)CFArrayGetCount(allCerts) : 0);
}
if((certFileBase != NULL) & (allCerts != NULL)) {
dumpCertFiles(allCerts, certFileBase, quiet);
}
if(numSigners) {
CSSM_OID eContentType = {0, NULL};
CFDataRef eContentData = NULL;
OidParser oidParser;
char str[OID_PARSER_STRING_SIZE];
ortn = CMSDecoderCopyEncapsulatedContentType(cmsDecoder, &eContentData);
if(ortn) {
cssmPerror("CMSDecoderCopyEncapsulatedContentType", ortn);
goto errOut;
}
if(eContentData != NULL) {
eContentType.Data = (uint8 *)CFDataGetBytePtr(eContentData);
eContentType.Length = CFDataGetLength(eContentData);
}
if(!quiet) {
printf(" eContentType : ");
if(eContentType.Data == NULL) {
printf("***NONE FOUND***\n");
}
else if(eContentType.Length == 0) {
printf("***EMPTY***\n");
}
else {
oidParser.oidParse(eContentType.Data, eContentType.Length, str);
printf("%s\n", str);
}
}
if(eContentVfy != NULL) {
if(eContentType.Data == NULL) {
fprintf(stderr, "***Tried to verify eContentType, but none found\n");
vfyErr = 1;
}
else if(!appCompareCssmData(eContentVfy, &eContentType)) {
fprintf(stderr, "***eContentType verify error\n");
fprintf(stderr, " Expected: ");
oidParser.oidParse(eContentVfy->Data, eContentVfy->Length, str);
printf("%s\n", str);
fprintf(stderr, " Found : ");
oidParser.oidParse(eContentType.Data, eContentType.Length, str);
printf("%s\n", str);
vfyErr = 1;
}
}
CFRELEASE(eContentData);
ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3,
&CSSMOID_APPLE_X509_BASIC,
NULL,
&policySearch);
if(ortn) {
cssmPerror("SecPolicySearchCreate", ortn);
goto errOut;
}
ortn = SecPolicySearchCopyNext(policySearch, &policy);
if(ortn) {
cssmPerror("SecPolicySearchCopyNext", ortn);
goto errOut;
}
}
for(signerDex=0; signerDex<numSigners; signerDex++) {
CMSSignerStatus signerStatus = kCMSSignerInvalidIndex;
SecCertificateRef signerCert;
CFStringRef signerEmailAddress;
SecTrustRef secTrust;
OSStatus certVerifyResultCode;
char trustStr[TRUST_STRING_MAX];
ortn = CMSDecoderCopySignerStatus(cmsDecoder, signerDex,
policy,
manTrustEval ? FALSE : TRUE,
&signerStatus,
&secTrust,
&certVerifyResultCode);
if(ortn) {
cssmPerror("CMSDecoderCopySignerStatus", ortn);
goto errOut;
}
if(ourRtn == noErr) {
if((signerStatus != kCMSSignerValid) ||
(certVerifyResultCode != CSSM_OK)) {
ourRtn = -1;
}
}
ortn = CMSDecoderCopySignerEmailAddress(cmsDecoder, signerDex,
&signerEmailAddress);
if(ortn) {
cssmPerror("CMSDecoderCopySignerEmailAddress", ortn);
goto errOut;
}
ortn = CMSDecoderCopySignerCert(cmsDecoder, signerDex,
&signerCert);
if(ortn) {
cssmPerror("CMSDecoderCopySignerCertificate", ortn);
goto errOut;
}
if(manTrustEval) {
trustRtn = evalSecTrust(secTrust, anchorArray, trustStr, quiet);
}
if(!quiet || (signerStatus != kCMSSignerValid) || (trustRtn != noErr)) {
fprintf(stderr, " Signer %u:\n", signerDex);
fprintf(stderr, " signerStatus : ");
switch(signerStatus) {
case kCMSSignerUnsigned:
fprintf(stderr, "kCMSSignerUnsigned\n"); break;
case kCMSSignerValid:
fprintf(stderr, "kCMSSignerValid\n"); break;
case kCMSSignerNeedsDetachedContent:
fprintf(stderr, "kCMSSignerNeedsDetachedContent\n"); break;
case kCMSSignerInvalidSignature:
fprintf(stderr, "kCMSSignerInvalidSignature\n"); break;
case kCMSSignerInvalidCert:
fprintf(stderr, "kCMSSignerInvalidCert\n"); break;
case kCMSSignerInvalidIndex:
fprintf(stderr, "kCMSSignerInvalidIndex\n"); break;
}
if(manTrustEval) {
fprintf(stderr, " Trust Eval : %s\n", trustStr);
}
fprintf(stderr, " emailAddrs : ");
if(signerEmailAddress == NULL) {
fprintf(stderr, "<<none found>>\n");
}
else {
char emailStr[1000];
if(!CFStringGetCString(signerEmailAddress,
emailStr, 1000, kCFStringEncodingASCII)) {
fprintf(stderr, "<<<Error converting email address to C string>>>\n");
}
else {
fprintf(stderr, "%s\n", emailStr);
}
}
fprintf(stderr, " vfyResult : %s\n",
certVerifyResultCode ?
cssmErrToStr(certVerifyResultCode) : "Success");
if(parseSignerCert) {
if(signerCert == NULL) {
fprintf(stderr, " <<<Unable to obtain signer cert>>>\n");
}
else {
CSSM_DATA certData;
ortn = SecCertificateGetData(signerCert, &certData);
if(ortn) {
fprintf(stderr, " <<<Unable to obtain signer cert>>>\n");
cssmPerror("SecCertificateGetData", ortn);
}
else {
printf("========== Signer Cert==========\n\n");
printCert(certData.Data, certData.Length, CSSM_FALSE);
printf("========== End Signer Cert==========\n\n");
}
}
}
}
CFRELEASE(signerCert);
signerCert = NULL;
CFRELEASE(signerEmailAddress);
signerEmailAddress = NULL;
CFRELEASE(secTrust);
secTrust = NULL;
}
if(ortn == noErr) {
ortn = CMSDecoderCopyContent(cmsDecoder, outData);
if(ortn) {
cssmPerror("CMSDecoderCopyContent", ortn);
}
}
errOut:
CFRelease(cmsDecoder);
if(arena != NULL) {
SecArenaPoolFree(arena, false);
}
CFRELEASE(allCerts);
CFRELEASE(policySearch);
CFRELEASE(policy);
if(ourRtn) {
return ourRtn;
}
else if(trustRtn) {
return trustRtn;
}
else if(vfyErr) {
return vfyErr;
}
else {
return ortn;
}
}
static OSStatus doSign(
CFTypeRef signerOrArray,
const unsigned char *inData,
unsigned inDataLen,
bool multiUpdate,
bool detachedContent,
const CSSM_OID *eContentType, CMSSignedAttributes attrs,
CFTypeRef otherCerts, bool customCoder,
bool getCmsMsg,
CMSCertificateChainMode chainMode,
bool quiet,
CFDataRef *outData) {
if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
fprintf(stderr, "***Sign requires input file. Aborting.\n");
return paramErr;
}
if(signerOrArray == NULL) {
fprintf(stderr, "***Sign requires a signing identity. Aborting.\n");
return paramErr;
}
OSStatus ortn;
CMSEncoderRef cmsEncoder = NULL;
SecCmsMessageRef msg = NULL;
SecArenaPoolRef arena = NULL;
CSSM_DATA encoderOut = {0, NULL};
if(multiUpdate || otherCerts || getCmsMsg || customCoder ||
(chainMode != kCMSCertificateChain)) {
ortn = CMSEncoderCreate(&cmsEncoder);
if(ortn) {
cssmPerror("CMSEncoderCreate", ortn);
return ortn;
}
if(signerOrArray != NULL) {
ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray);
if(ortn) {
goto errOut;
}
}
if(eContentType) {
ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
if(ortn) {
goto errOut;
}
}
if(detachedContent) {
ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
if(ortn) {
goto errOut;
}
}
if(otherCerts) {
ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts);
if(ortn) {
goto errOut;
}
}
if(attrs) {
ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs);
if(ortn) {
goto errOut;
}
}
if(chainMode != kCMSCertificateChain) {
ortn = CMSEncoderSetCertificateChainMode(cmsEncoder, chainMode);
if(ortn) {
goto errOut;
}
}
if(getCmsMsg || customCoder) {
ortn = CMSEncoderGetCmsMessage(cmsEncoder, &msg);
if(ortn) {
cssmPerror("CMSEncoderGetCmsMessage", ortn);
goto errOut;
}
}
if(customCoder) {
SecCmsEncoderRef coder = NULL;
ortn = SecArenaPoolCreate(1024, &arena);
if(ortn) {
cssmPerror("SecArenaPoolCreate", ortn);
goto errOut;
}
ortn = SecCmsEncoderCreate(msg,
NULL, NULL, &encoderOut, arena,
NULL, NULL, NULL, NULL, NULL, NULL, &coder);
if(ortn) {
cssmPerror("SecCmsEncoderCreate", ortn);
goto errOut;
}
ortn = CMSEncoderSetEncoder(cmsEncoder, coder);
if(ortn) {
cssmPerror("CMSEncoderSetEncoder", ortn);
goto errOut;
}
else if(!quiet) {
printf("...set up custom SecCmsEncoderRef\n");
}
}
ortn = updateEncoder(cmsEncoder, inData, inDataLen);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
if(ortn) {
cssmPerror("CMSEncoderCopyEncodedContent", ortn);
}
if(customCoder) {
*outData = CFDataCreate(NULL,
(const UInt8 *)encoderOut.Data, encoderOut.Length);
}
}
else {
ortn = CMSEncode(signerOrArray,
NULL,
eContentType,
detachedContent,
attrs,
inData, inDataLen,
outData);
if(ortn) {
printf("***CMSEncode returned %ld\n", (long)ortn);
cssmPerror("CMSEncode", ortn);
}
}
errOut:
if(cmsEncoder) {
CFRelease(cmsEncoder);
}
if(arena) {
SecArenaPoolFree(arena, false);
}
return ortn;
}
static OSStatus doEncrypt(
CFTypeRef recipOrArray,
const unsigned char *inData,
unsigned inDataLen,
bool multiUpdate,
CFDataRef *outData) {
if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
fprintf(stderr, "***Encrypt requires input file. Aborting.\n");
return paramErr;
}
if(recipOrArray == NULL) {
fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n");
return paramErr;
}
OSStatus ortn;
CMSEncoderRef cmsEncoder = NULL;
if(multiUpdate) {
ortn = CMSEncoderCreate(&cmsEncoder);
if(ortn) {
cssmPerror("CMSEncoderCreate", ortn);
return ortn;
}
ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray);
if(ortn) {
goto errOut;
}
ortn = updateEncoder(cmsEncoder, inData, inDataLen);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
if(ortn) {
cssmPerror("CMSEncoderCopyEncodedContent", ortn);
}
}
else {
ortn = CMSEncode(NULL,
recipOrArray,
NULL,
FALSE,
kCMSAttrNone,
inData, inDataLen,
outData);
if(ortn) {
printf("***CMSEncode returned %ld\n", (long)ortn);
cssmPerror("CMSEncode", ortn);
}
}
errOut:
if(cmsEncoder) {
CFRelease(cmsEncoder);
}
return ortn;
}
static OSStatus doSignEncrypt(
CFTypeRef recipOrArray, CFTypeRef signerOrArray, const CSSM_OID *eContentType, CMSSignedAttributes attrs,
const unsigned char *inData,
unsigned inDataLen,
bool multiUpdate,
CFTypeRef otherCerts, CFDataRef *outData) {
if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) {
fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n");
return paramErr;
}
if(recipOrArray == NULL) {
fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n");
return paramErr;
}
if(signerOrArray == NULL) {
fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n");
return paramErr;
}
OSStatus ortn;
CMSEncoderRef cmsEncoder = NULL;
if(multiUpdate || otherCerts) {
ortn = CMSEncoderCreate(&cmsEncoder);
if(ortn) {
cssmPerror("CMSEncoderCreate", ortn);
return ortn;
}
ortn = CMSEncoderAddRecipients(cmsEncoder, recipOrArray);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderAddSigners(cmsEncoder, signerOrArray);
if(ortn) {
goto errOut;
}
if(eContentType) {
ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
if(ortn) {
goto errOut;
}
}
if(otherCerts) {
ortn = CMSEncoderAddSupportingCerts(cmsEncoder, otherCerts);
if(ortn) {
goto errOut;
}
}
if(attrs) {
ortn = CMSEncoderAddSignedAttributes(cmsEncoder, attrs);
if(ortn) {
goto errOut;
}
}
ortn = updateEncoder(cmsEncoder, inData, inDataLen);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
if(ortn) {
cssmPerror("CMSEncoderCopyEncodedContent", ortn);
}
}
else {
ortn = CMSEncode(signerOrArray,
recipOrArray,
eContentType,
FALSE,
attrs,
inData, inDataLen,
outData);
if(ortn) {
printf("***CMSEncode returned %ld\n", (long)ortn);
cssmPerror("CMSEncode", ortn);
}
}
errOut:
if(cmsEncoder) {
CFRelease(cmsEncoder);
}
return ortn;
}
static OSStatus makeCertBag(
CFTypeRef certsOrArray,
CFDataRef *outData)
{
if(certsOrArray == NULL) {
printf("***Need some certs to generate this type of message.\n");
return -1;
}
OSStatus ortn;
CMSEncoderRef cmsEncoder = NULL;
ortn = CMSEncoderCreate(&cmsEncoder);
if(ortn) {
cssmPerror("CMSEncoderCreate", ortn);
return ortn;
}
ortn = CMSEncoderAddSupportingCerts(cmsEncoder, certsOrArray);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderCopyEncodedContent(cmsEncoder, outData);
if(ortn) {
cssmPerror("CMSEncoderCopyEncodedContent", ortn);
}
errOut:
CFRelease(cmsEncoder);
return ortn;
}
static void addThing(
CFTypeRef newThing,
CFTypeRef *currThing,
CFMutableArrayRef *thingArray)
{
if((*currThing == NULL) && (*thingArray == NULL)) {
*currThing = newThing;
return;
}
if(*thingArray == NULL) {
*thingArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
if(*currThing != NULL) {
CFArrayAppendValue(*thingArray, *currThing);
CFRelease(*currThing);
*currThing = NULL;
}
CFArrayAppendValue(*thingArray, newThing);
CFRelease(newThing);
}
int main(int argc, char **argv)
{
if(argc < 2) {
usage(argv);
}
CT_Op op;
bool needId = false;
if(!strcmp(argv[1], "sign")) {
op = CTO_Sign;
needId = true;
}
else if(!strcmp(argv[1], "envel")) {
op = CTO_Envelop;
}
else if(!strcmp(argv[1], "signEnv")) {
op = CTO_SignEnvelop;
needId = true;
}
else if(!strcmp(argv[1], "certs")) {
op = CTO_CertsOnly;
}
else if(!strcmp(argv[1], "parse")) {
op = CTO_Parse;
}
else {
fprintf(stderr, "***Unrecognized cmd.\n");
usage(argv);
}
extern int optind;
extern char *optarg;
int arg;
OSStatus ortn;
char *inFileName = NULL;
char *outFileName = NULL;
bool detachedContent = false;
char *detachedFile = NULL;
bool useIdPicker = false;
bool quiet = false;
bool silent = false;
bool parseSignerCert = false;
const CSSM_OID *eContentType = NULL;
bool multiUpdate = false;
bool loopPause = false;
char *certFileBase = NULL;
CMSSignedAttributes signedAttrs = 0;
char *anchorFile = NULL;
bool manTrustEval = false;
CMSCertificateChainMode chainMode = kCMSCertificateChain;
CT_Vfy vfyOp = CTV_None;
const CSSM_OID *eContentVfy = NULL;
int numSignersVfy = -1;
int numCertsVfy = -1;
bool customCoder = false;
bool fetchSecCmsMsg = false;
SecIdentityRef signerId = NULL;
CFMutableArrayRef signerArray = NULL;
SecCertificateRef recipCert = NULL;
CFMutableArrayRef recipArray = NULL;
SecCertificateRef generalCert = NULL;
CFMutableArrayRef generalCertArray = NULL;
SecKeychainRef kcRef = NULL;
CFMutableArrayRef anchorArray = NULL;
optind = 2;
while ((arg = getopt(argc, argv, "i:o:k:pr:R:dD:e:mlqcv:s:E:S:C:f:N:a:A:M12t:Z")) != -1) {
switch (arg) {
case 'i':
inFileName = optarg;
break;
case 'o':
outFileName = optarg;
break;
case 'k':
kcRef = keychain_open(optarg);
if(!kcRef) {
exit(1);
}
break;
case 'p':
useIdPicker = true;
break;
case 'r':
{
SecCertificateRef newCert = NULL;
char *recipient = optarg;
ortn = findCert(recipient, kcRef, &newCert);
if(ortn) {
exit(1);
}
addThing(newCert, (CFTypeRef *)&recipCert, &recipArray);
break;
}
case 'R':
{
SecCertificateRef certRef = readCertFile(optarg);
if(certRef == NULL) {
exit(1);
}
addThing(certRef, (CFTypeRef *)&recipCert, &recipArray);
break;
}
case 'S':
{
SecIdentityRef newId = NULL;
SecCertificateRef newCert = NULL;
char *signerEmail = optarg;
ortn = findCert(signerEmail, kcRef, &newCert);
if(ortn) {
exit(1);
}
ortn = SecIdentityCreateWithCertificate(kcRef, newCert, &newId);
if(ortn) {
cssmPerror("SecIdentityCreateWithCertificate", ortn);
exit(1);
}
addThing(newId, (CFTypeRef *)&signerId, &signerArray);
CFRelease(newCert);
break;
}
case 'C':
{
SecCertificateRef newCert = readCertFile(optarg);
if(newCert == NULL) {
exit(1);
}
addThing(newCert, (CFTypeRef *)&generalCert, &generalCertArray);
break;
}
case 'c':
parseSignerCert = true;
break;
case 'v':
if(!strcmp(optarg, "sign")) {
vfyOp = CTV_Sign;
}
else if(!strcmp(optarg, "encr")) {
vfyOp = CTV_Envelop;
}
else if(!strcmp(optarg, "signEnv")) {
vfyOp = CTV_SignEnvelop;
}
else {
usage(argv);
}
break;
case 'e':
switch(optarg[0]) {
case 'a':
eContentType = &CSSMOID_PKINIT_AUTH_DATA;
break;
case 'r':
eContentType = &CSSMOID_PKINIT_RKEY_DATA;
break;
default:
usage(argv);
}
break;
case 'E':
switch(optarg[0]) {
case 'a':
eContentVfy = &CSSMOID_PKINIT_AUTH_DATA;
break;
case 'r':
eContentVfy = &CSSMOID_PKINIT_RKEY_DATA;
break;
case 'd':
eContentVfy = &CSSMOID_PKCS7_Data;
break;
default:
usage(argv);
}
break;
case 'd':
if(op != CTO_Sign) {
printf("-d only valid for op sign\n");
exit(1);
}
detachedContent = true;
break;
case 'D':
if(op != CTO_Parse) {
printf("-D only valid for op sign\n");
exit(1);
}
detachedFile = optarg;
break;
case 'l':
loopPause = true;
break;
case 'm':
multiUpdate = true;
break;
case 's':
numSignersVfy = atoi(optarg);
break;
case 'f':
certFileBase = optarg;
break;
case 'N':
numCertsVfy = atoi(optarg);
break;
case '1':
customCoder = true;
break;
case '2':
fetchSecCmsMsg = true;
break;
case 'a':
for(; *optarg; optarg++) {
switch(*optarg) {
case 'c':
signedAttrs |= kCMSAttrSmimeCapabilities;
break;
case 'e':
signedAttrs |= kCMSAttrSmimeEncryptionKeyPrefs;
break;
case 'E':
signedAttrs |= kCMSAttrSmimeMSEncryptionKeyPrefs;
break;
case 't':
signedAttrs |= kCMSAttrSigningTime;
break;
default:
usage(argv);
}
}
break;
case 'A':
anchorFile = optarg;
break;
case 'M':
manTrustEval = true;
break;
case 't':
if(!strcmp(optarg, "none")) {
chainMode = kCMSCertificateNone;
}
else if(!strcmp(optarg, "signer")) {
chainMode = kCMSCertificateSignerOnly;
}
else if(!strcmp(optarg, "chain")) {
chainMode = kCMSCertificateChain;
}
else if(!strcmp(optarg, "chainWithRoot")) {
chainMode = kCMSCertificateChainWithRoot;
}
else {
printf("***Bogus cert chain spec***\n");
usage(argv);
}
break;
case 'q':
quiet = true;
break;
case 'Z':
quiet = true;
silent = true;
break;
default:
case '?':
usage(argv);
}
}
if(optind != argc) {
usage(argv);
}
unsigned char *inData = NULL;
unsigned inDataLen = 0;
unsigned char *detachedData = NULL;
unsigned detachedDataLen = 0;
CFDataRef outData = NULL;
CFIndex byteCount = 0;
if(!silent) {
testStartBanner((char *)"newCmsTool", argc, argv);
}
if(inFileName) {
if(readFile(inFileName, &inData, &inDataLen)) {
fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName);
exit(1);
}
}
if(detachedFile) {
if(readFile(detachedFile, &detachedData, &detachedDataLen)) {
fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile);
exit(1);
}
}
if(useIdPicker) {
ortn = sslSimpleIdentPicker(kcRef, &signerId);
if(ortn) {
fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n");
exit(1);
}
}
if(anchorFile) {
SecCertificateRef secCert = readCertFile(anchorFile);
if(secCert == NULL) {
exit(1);
}
anchorArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(anchorArray, secCert);
CFRelease(secCert);
}
CFArrayRef originalSearchList = NULL;
if(kcRef != NULL) {
ortn = SecKeychainCopySearchList(&originalSearchList);
if(ortn) {
cssmPerror("SecKeychainCopySearchList", ortn);
exit(1);
}
CFMutableArrayRef newList = CFArrayCreateMutableCopy(
NULL, 0, originalSearchList);
CFArrayAppendValue(newList, kcRef);
ortn = SecKeychainSetSearchList(newList);
if(ortn) {
cssmPerror("SecKeychainSetSearchList", ortn);
exit(1);
}
}
do {
switch(op) {
case CTO_Sign:
ortn = doSign((signerArray != NULL) ?
(CFTypeRef)signerArray : (CFTypeRef)signerId,
inData, inDataLen,
multiUpdate, detachedContent, eContentType, signedAttrs,
(generalCertArray != NULL) ?
(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
customCoder, fetchSecCmsMsg, chainMode, quiet,
&outData);
break;
case CTO_Envelop:
ortn = doEncrypt(recipArray ?
(CFTypeRef)recipArray : (CFTypeRef)recipCert,
inData, inDataLen,
multiUpdate, &outData);
break;
case CTO_SignEnvelop:
ortn = doSignEncrypt(recipArray ?
(CFTypeRef)recipArray : (CFTypeRef)recipCert,
signerArray ?
(CFTypeRef)signerArray : (CFTypeRef)signerId,
eContentType, signedAttrs,
inData, inDataLen, multiUpdate,
(generalCertArray != NULL) ?
(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
&outData);
break;
case CTO_CertsOnly:
ortn = makeCertBag((generalCertArray != NULL) ?
(CFTypeRef)generalCertArray : (CFTypeRef)generalCert,
&outData);
break;
case CTO_Parse:
ortn = doParse(inData, inDataLen,
detachedData, detachedDataLen,
multiUpdate,
vfyOp, eContentVfy, numSignersVfy, numCertsVfy,
parseSignerCert, certFileBase, customCoder,
manTrustEval, anchorArray,
quiet, &outData);
break;
}
if(loopPause) {
if(outData) {
printf("...generated %u bytes of data.\n",
(unsigned)CFDataGetLength(outData));
}
fpurge(stdin);
printf("q to quit, anything else to loop again: ");
char resp = getchar();
if(resp == 'q') {
break;
}
else {
CFRELEASE(outData);
outData = NULL;
}
}
} while (loopPause);
if(originalSearchList) {
ortn = SecKeychainSetSearchList(originalSearchList);
if(ortn) {
cssmPerror("SecKeychainSetSearchList", ortn);
}
}
if(ortn) {
goto errOut;
}
byteCount = outData ? CFDataGetLength(outData) : 0;
if(outData && outFileName) {
if(writeFile(outFileName, CFDataGetBytePtr(outData), byteCount)) {
fprintf(stderr, "***Error writing to %s.\n", outFileName);
ortn = -1;
}
else {
if(!quiet) {
fprintf(stderr, "...wrote %u bytes to %s.\n",
(unsigned)byteCount, outFileName);
}
}
}
else if(byteCount) {
fprintf(stderr, "...generated %u bytes but no place to write it.\n",
(unsigned)byteCount);
}
else if(outFileName) {
fprintf(stderr, "...nothing to write to file %s.\n", outFileName);
ortn = -1;
}
errOut:
return ortn;
}
SecKeychainRef keychain_open(const char *name)
{
SecKeychainRef keychain = NULL;
OSStatus result;
if (name && name[0] != '/')
{
CFArrayRef dynamic = NULL;
result = SecKeychainCopyDomainSearchList(
kSecPreferencesDomainDynamic, &dynamic);
if (result)
{
cssmPerror("SecKeychainCopyDomainSearchList", result);
return NULL;
}
else
{
uint32_t i;
uint32_t count = dynamic ? CFArrayGetCount(dynamic) : 0;
for (i = 0; i < count; ++i)
{
char pathName[PATH_MAX];
UInt32 ioPathLength = sizeof(pathName);
bzero(pathName, ioPathLength);
keychain = (SecKeychainRef)CFArrayGetValueAtIndex(dynamic, i);
result = SecKeychainGetPath(keychain, &ioPathLength, pathName);
if (result)
{
cssmPerror("SecKeychainCopyDomainSearchList", result);
return NULL;
}
if (!strncmp(pathName, name, ioPathLength))
{
CFRetain(keychain);
CFRelease(dynamic);
return keychain;
}
}
CFRelease(dynamic);
}
}
result = SecKeychainOpen(name, &keychain);
if (result)
{
cssmPerror("SecKeychainOpen", result);
}
return keychain;
}