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