1/* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34/* 35 * Modifications Copyright (c) 2003-2009 Apple Inc. All Rights Reserved. 36 * 37 * cmsutil -- A command to work with CMS data 38 */ 39 40#include "security.h" 41#include "keychain_utilities.h" 42#include "identity_find.h" 43 44#include <Security/SecCmsBase.h> 45#include <Security/SecCmsContentInfo.h> 46#include <Security/SecCmsDecoder.h> 47#include <Security/SecCmsDigestContext.h> 48#include <Security/SecCmsDigestedData.h> 49#include <Security/SecCmsEncoder.h> 50#include <Security/SecCmsEncryptedData.h> 51#include <Security/SecCmsEnvelopedData.h> 52#include <Security/SecCmsMessage.h> 53#include <Security/SecCmsRecipientInfo.h> 54#include <Security/SecCmsSignedData.h> 55#include <Security/SecCmsSignerInfo.h> 56#include <Security/SecSMIME.h> 57#include <Security/tsaSupport.h> 58 59#include <Security/oidsalg.h> 60#include <Security/SecPolicy.h> 61#include <Security/SecKeychain.h> 62#include <Security/SecKeychainSearch.h> 63#include <Security/SecIdentity.h> 64#include <Security/SecIdentitySearch.h> 65#include <CoreFoundation/CFString.h> 66#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 67 68#include <stdio.h> 69#include <stdarg.h> 70#include <stdio.h> 71#include <string.h> 72#include <unistd.h> 73 74// SecPolicyCopy 75#include <Security/SecPolicyPriv.h> 76// SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindBySubjectKeyID, SecCertificateFindByEmail 77#include <Security/SecCertificatePriv.h> 78// SecIdentitySearchCreateWithPolicy 79#include <Security/SecIdentitySearchPriv.h> 80 81#define SEC_CHECK0(CALL, ERROR) do { if (!(CALL)) { sec_error(ERROR); goto loser; } } while(0) 82#define SEC_CHECK(CALL, ERROR) do { rv = (CALL); if (rv) { sec_perror(ERROR, rv); goto loser; } } while(0) 83#define SEC_CHECK2(CALL, ERROR, ARG) do { rv = CALL; if (rv) \ 84 { sec_error(ERROR ": %s", ARG, sec_errstr(rv)); goto loser; } } while(0) 85 86// @@@ Remove this 87#if 1 88 89static CSSM_KEYUSE CERT_KeyUsageForCertUsage(SECCertUsage certUsage) 90{ 91 switch (certUsage) 92 { 93 case certUsageSSLClient: return CSSM_KEYUSE_SIGN; 94 case certUsageSSLServer: return CSSM_KEYUSE_SIGN; 95 case certUsageSSLServerWithStepUp: return CSSM_KEYUSE_SIGN; 96 case certUsageSSLCA: return CSSM_KEYUSE_SIGN; 97 case certUsageEmailSigner: return CSSM_KEYUSE_SIGN; 98 case certUsageEmailRecipient: return CSSM_KEYUSE_UNWRAP; 99 case certUsageObjectSigner: return CSSM_KEYUSE_SIGN; 100 case certUsageUserCertImport: return CSSM_KEYUSE_SIGN; 101 case certUsageVerifyCA: return CSSM_KEYUSE_SIGN; 102 case certUsageProtectedObjectSigner: return CSSM_KEYUSE_SIGN; 103 case certUsageStatusResponder: return CSSM_KEYUSE_SIGN; 104 case certUsageAnyCA: return CSSM_KEYUSE_SIGN; 105 default: 106 sec_error("CERT_PolicyForCertUsage %ld: unknown certUsage", certUsage); 107 return CSSM_KEYUSE_SIGN; 108 } 109} 110 111static SecPolicyRef CERT_PolicyForCertUsage(SECCertUsage certUsage, const char *emailAddress) 112{ 113 SecPolicyRef policy = NULL; 114 const CSSM_OID *policyOID; 115 OSStatus rv; 116 117 switch (certUsage) 118 { 119 case certUsageSSLClient: policyOID = &CSSMOID_APPLE_TP_SSL; break; 120 case certUsageSSLServer: policyOID = &CSSMOID_APPLE_TP_SSL; break; 121 case certUsageSSLServerWithStepUp: policyOID = &CSSMOID_APPLE_TP_SSL; break; 122 case certUsageSSLCA: policyOID = &CSSMOID_APPLE_TP_SSL; break; 123 case certUsageEmailSigner: policyOID = &CSSMOID_APPLE_TP_SMIME; break; 124 case certUsageEmailRecipient: policyOID = &CSSMOID_APPLE_TP_SMIME; break; 125 case certUsageObjectSigner: policyOID = &CSSMOID_APPLE_TP_CODE_SIGN; break; 126 case certUsageUserCertImport: policyOID = &CSSMOID_APPLE_X509_BASIC; break; 127 case certUsageVerifyCA: policyOID = &CSSMOID_APPLE_X509_BASIC; break; 128 case certUsageProtectedObjectSigner: policyOID = &CSSMOID_APPLE_ISIGN; break; 129 case certUsageStatusResponder: policyOID = &CSSMOID_APPLE_TP_REVOCATION_OCSP; break; 130 case certUsageAnyCA: policyOID = &CSSMOID_APPLE_X509_BASIC; break; 131 default: 132 sec_error("CERT_PolicyForCertUsage %ld: unknown certUsage", certUsage); 133 goto loser; 134 } 135 136 SEC_CHECK(SecPolicyCopy(CSSM_CERT_X_509v3, policyOID, &policy), "SecPolicyCopy"); 137 if (certUsage == certUsageEmailSigner || certUsage == certUsageEmailRecipient) 138 { 139 CSSM_APPLE_TP_SMIME_OPTIONS options = 140 { 141 CSSM_APPLE_TP_SMIME_OPTS_VERSION, 142 certUsage == certUsageEmailSigner 143 ? CE_KU_DigitalSignature | CE_KU_NonRepudiation 144 : CE_KU_KeyEncipherment, 145 emailAddress ? sizeof(emailAddress) : 0, 146 emailAddress 147 }; 148 CSSM_DATA value = { sizeof(options), (uint8 *)&options }; 149 SEC_CHECK(SecPolicySetValue(policy, &value), "SecPolicySetValue"); 150 } 151 152 // @@@ Need to set values for SSL and other policies. 153 return policy; 154 155loser: 156 if (policy) CFRelease(policy); 157 return NULL; 158} 159 160static SecCertificateRef CERT_FindUserCertByUsage(CFTypeRef keychainOrArray, const char *emailAddress, 161 SECCertUsage certUsage, Boolean validOnly) 162{ 163 SecKeychainSearchRef search = NULL; 164 SecCertificateRef cert = NULL; 165 SecPolicyRef policy; 166 OSStatus rv; 167 168 policy = CERT_PolicyForCertUsage(certUsage, emailAddress); 169 if (!policy) 170 goto loser; 171 172 SEC_CHECK2(SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &search), 173 "create search for certificate with email: \"%s\"", emailAddress); 174 for (;;) 175 { 176 SecKeychainItemRef item; 177 rv = SecKeychainSearchCopyNext(search, &item); 178 if (rv) 179 { 180 if (rv == errSecItemNotFound) 181 break; 182 183 sec_perror("error finding next matching certificate", rv); 184 goto loser; 185 } 186 187 cert = (SecCertificateRef)item; 188 // @@@ Check cert against policy. 189 } 190 191loser: 192 if (policy) CFRelease(policy); 193 if (search) CFRelease(search); 194 195 return cert; 196} 197 198static SecIdentityRef CERT_FindIdentityByUsage(CFTypeRef keychainOrArray, 199 const char *emailAddress, 200 SECCertUsage certUsage, 201 Boolean validOnly) 202{ 203 SecIdentitySearchRef search = NULL; 204 SecIdentityRef identity = NULL; 205 CFStringRef idString = CFStringCreateWithCString(NULL, emailAddress, kCFStringEncodingUTF8); 206 SecPolicyRef policy; 207 OSStatus rv; 208 209 policy = CERT_PolicyForCertUsage(certUsage, emailAddress); 210 if (!policy) 211 goto loser; 212 213 214 SEC_CHECK2(SecIdentitySearchCreateWithPolicy(policy, idString, 215 CERT_KeyUsageForCertUsage(certUsage), keychainOrArray, validOnly, &search), 216 "create search for identity with email: \"%s\"", emailAddress); 217 for (;;) 218 { 219 rv = SecIdentitySearchCopyNext(search, &identity); 220 if (rv) 221 { 222 if (rv == errSecItemNotFound) 223 break; 224 225 sec_perror("error finding next matching identity", rv); 226 goto loser; 227 } 228 } 229 230loser: 231 if (policy) CFRelease(policy); 232 if (search) CFRelease(search); 233 if (idString) CFRelease(idString); 234 235 return identity; 236 237 238#if 0 239 SecIdentityRef identity = NULL; 240 SecCertificateRef cert; 241 OSStatus rv; 242 243 cert = CERT_FindUserCertByUsage(keychainOrArray, emailAddress, certUsage, validOnly); 244 if (!cert) 245 goto loser; 246 247 SEC_CHECK2(SecIdentityCreateWithCertificate(keychainOrArray, cert, &identity), 248 "failed to find private key for certificate with email: \"%s\"", emailAddress); 249loser: 250 if (cert) CFRelease(cert); 251 252 return identity; 253#endif 254} 255 256static SecCertificateRef CERT_FindCertByNicknameOrEmailAddr(CFTypeRef keychainOrArray, const char *emailAddress) 257{ 258 SecCertificateRef certificate = NULL; 259 OSStatus rv; 260 261 SEC_CHECK2(SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificate), 262 "failed to find certificate with email: \"%s\"", emailAddress); 263 264loser: 265 return certificate; 266} 267 268static SecIdentityRef CERT_FindIdentityBySubjectKeyID(CFTypeRef keychainOrArray, 269 const char *subjectKeyIDString) 270{ 271 // ss will be something like "B2ACD31AC8D0DA62E7679432ADDD3398EF66948B" 272 273 SecCertificateRef certificate = NULL; 274 SecIdentityRef identityRef = NULL; 275 OSStatus rv; 276 277 CSSM_SIZE len = strlen(subjectKeyIDString)/2; 278 CSSM_DATA subjectKeyID = {0,}; 279 subjectKeyID.Length = len; 280 subjectKeyID.Data = (uint8 *)malloc(subjectKeyID.Length); 281 fromHex(subjectKeyIDString, &subjectKeyID); 282 283 SEC_CHECK2(SecCertificateFindBySubjectKeyID(keychainOrArray, &subjectKeyID, &certificate), 284 "failed to find identity with subject key ID: \"%s\"", subjectKeyIDString); 285 286 SEC_CHECK2(SecIdentityCreateWithCertificate(keychainOrArray, certificate, &identityRef), 287 "failed to find certificate with subject key ID: \"%s\"", subjectKeyIDString); 288loser: 289 return identityRef; 290} 291 292 293static OSStatus CERT_CheckCertUsage (SecCertificateRef cert,unsigned char usage) 294{ 295 return 0; 296} 297 298#endif 299 300// @@@ Eleminate usage of this header. 301//#include "cert.h" 302//#include <security_asn1/secerr.h> 303//#include "plgetopt.h" 304//#include "secitem.h" 305 306#ifdef HAVE_DODUMPSTATES 307extern int doDumpStates; 308#endif /* HAVE_DODUMPSTATES */ 309 310OSStatus SECU_FileToItem(CSSM_DATA *dst, FILE *src); 311 312 313extern void SEC_Init(void); /* XXX */ 314static int cms_verbose = 0; 315static int cms_update_single_byte = 0; 316 317/* XXX stolen from cmsarray.c 318 * nss_CMSArray_Count - count number of elements in array 319 */ 320int nss_CMSArray_Count(void **array) 321{ 322 int n = 0; 323 if (array == NULL) 324 return 0; 325 while (*array++ != NULL) 326 n++; 327 return n; 328} 329 330typedef OSStatus(update_func)(void *cx, const char *data, unsigned int len); 331 332static OSStatus do_update(update_func *update, 333 void *cx, const unsigned char *data, int len) 334{ 335 OSStatus rv = noErr; 336 if (cms_update_single_byte) 337 { 338 for (;len; --len, ++data) 339 { 340 rv = update(cx, (const char *)data, 1); 341 if (rv) 342 break; 343 } 344 } 345 else 346 rv = update(cx, (const char *)data, len); 347 348 return rv; 349} 350 351 352static OSStatus DigestFile(SecArenaPoolRef poolp, CSSM_DATA ***digests, CSSM_DATA *input, SECAlgorithmID **algids) 353{ 354 SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(algids); 355 if (digcx == NULL) 356 return paramErr; 357 358 do_update((update_func *)SecCmsDigestContextUpdate, digcx, input->Data, input->Length); 359 360 return SecCmsDigestContextFinishMultiple(digcx, poolp, digests); 361} 362 363char * 364ownpw(void *info, Boolean retry, void *arg) 365{ 366 char * passwd = NULL; 367 368 if ( (!retry) && arg ) { 369 passwd = strdup((char *)arg); 370 } 371 372 return passwd; 373} 374 375struct optionsStr { 376 PK11PasswordFunc password; 377 SECCertUsage certUsage; 378 SecKeychainRef certDBHandle; 379}; 380 381struct decodeOptionsStr { 382 struct optionsStr *options; 383 FILE *contentFile; 384 int headerLevel; 385 Boolean suppressContent; 386 SecCmsGetDecryptKeyCallback dkcb; 387 SecSymmetricKeyRef bulkkey; 388}; 389 390struct signOptionsStr { 391 struct optionsStr *options; 392 char *nickname; 393 char *encryptionKeyPreferenceNick; 394 char *subjectKeyID; 395 Boolean signingTime; 396 Boolean smimeProfile; 397 Boolean detached; 398 SECOidTag hashAlgTag; 399 Boolean wantTimestamping; 400 char *timestampingURL; 401}; 402 403struct envelopeOptionsStr { 404 struct optionsStr *options; 405 char **recipients; 406}; 407 408struct certsonlyOptionsStr { 409 struct optionsStr *options; 410 char **recipients; 411}; 412 413struct encryptOptionsStr { 414 struct optionsStr *options; 415 char **recipients; 416 SecCmsMessageRef envmsg; 417 CSSM_DATA *input; 418 FILE *outfile; 419 FILE *envFile; 420 SecSymmetricKeyRef bulkkey; 421 SECOidTag bulkalgtag; 422 int keysize; 423}; 424 425static SecCmsMessageRef decode(FILE *out, CSSM_DATA *output, CSSM_DATA *input, 426 const struct decodeOptionsStr *decodeOptions) 427{ 428 SecCmsDecoderRef dcx; 429 SecCmsMessageRef cmsg; 430 SecCmsContentInfoRef cinfo; 431 SecCmsSignedDataRef sigd = NULL; 432 SecCmsEnvelopedDataRef envd; 433 SecCmsEncryptedDataRef encd; 434 SECAlgorithmID **digestalgs; 435 int nlevels, i, nsigners, j; 436 CFStringRef signercn; 437 SecCmsSignerInfoRef si; 438 SECOidTag typetag; 439 CSSM_DATA **digests; 440 SecArenaPoolRef poolp = NULL; 441 PK11PasswordFunc pwcb; 442 void *pwcb_arg; 443 CSSM_DATA *item, sitem = { 0, }; 444 CFTypeRef policy = NULL; 445 OSStatus rv; 446 447 pwcb = (PK11PasswordFunc)((decodeOptions->options->password != NULL) ? ownpw : NULL); 448 pwcb_arg = (decodeOptions->options->password != NULL) ? 449 (void *)decodeOptions->options->password : NULL; 450 451 if (decodeOptions->contentFile) // detached content: grab content file 452 SECU_FileToItem(&sitem, decodeOptions->contentFile); 453 454 SEC_CHECK(SecCmsDecoderCreate(NULL, 455 NULL, NULL, /* content callback */ 456 pwcb, pwcb_arg, /* password callback */ 457 decodeOptions->dkcb, /* decrypt key callback */ 458 decodeOptions->bulkkey, 459 &dcx), 460 "failed to create to decoder"); 461 SEC_CHECK(do_update((update_func *)SecCmsDecoderUpdate, dcx, input->Data, input->Length), 462 "failed to add data to decoder"); 463 SEC_CHECK(SecCmsDecoderFinish(dcx, &cmsg), 464 "failed to decode message"); 465 466 if (decodeOptions->headerLevel >= 0) 467 { 468 /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/ 469 fprintf(out, "SMIME: "); 470 } 471 472 nlevels = SecCmsMessageContentLevelCount(cmsg); 473 for (i = 0; i < nlevels; i++) 474 { 475 cinfo = SecCmsMessageContentLevel(cmsg, i); 476 typetag = SecCmsContentInfoGetContentTypeTag(cinfo); 477 478 if (decodeOptions->headerLevel >= 0) 479 fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i); 480 481 switch (typetag) 482 { 483 case SEC_OID_PKCS7_SIGNED_DATA: 484 if (decodeOptions->headerLevel >= 0) 485 fprintf(out, "type=signedData; "); 486 487 SEC_CHECK0(sigd = (SecCmsSignedDataRef )SecCmsContentInfoGetContent(cinfo), 488 "problem finding signedData component"); 489 /* if we have a content file, but no digests for this signedData */ 490 if (decodeOptions->contentFile != NULL && !SecCmsSignedDataHasDigests(sigd)) 491 { 492 SEC_CHECK(SecArenaPoolCreate(1024, &poolp), "failed to create arenapool"); 493 digestalgs = SecCmsSignedDataGetDigestAlgs(sigd); 494 SEC_CHECK(DigestFile(poolp, &digests, &sitem, digestalgs), 495 "problem computing message digest"); 496 SEC_CHECK(SecCmsSignedDataSetDigests(sigd, digestalgs, digests), 497 "problem setting message digests"); 498 SecArenaPoolFree(poolp, false); 499 } 500 501 policy = CERT_PolicyForCertUsage(decodeOptions->options->certUsage, NULL); 502 // import the certificates 503 SEC_CHECK(SecCmsSignedDataImportCerts(sigd,decodeOptions->options->certDBHandle, 504 decodeOptions->options->certUsage, true /* false */), 505 "cert import failed"); 506 507 /* find out about signers */ 508 nsigners = SecCmsSignedDataSignerInfoCount(sigd); 509 if (decodeOptions->headerLevel >= 0) 510 fprintf(out, "nsigners=%d; ", nsigners); 511 if (nsigners == 0) 512 { 513 /* must be a cert transport message */ 514 OSStatus rv; 515 /* XXX workaround for bug #54014 */ 516 SecCmsSignedDataImportCerts(sigd,decodeOptions->options->certDBHandle, 517 decodeOptions->options->certUsage,true); 518 SEC_CHECK(SecCmsSignedDataVerifyCertsOnly(sigd,decodeOptions->options->certDBHandle, policy), 519 "verify certs-only failed"); 520 return cmsg; 521 } 522 523 SEC_CHECK0(SecCmsSignedDataHasDigests(sigd), "message has no digests"); 524 for (j = 0; j < nsigners; j++) 525 { 526 si = SecCmsSignedDataGetSignerInfo(sigd, j); 527 signercn = SecCmsSignerInfoGetSignerCommonName(si); 528 if (decodeOptions->headerLevel >= 0) 529 { 530 const char *px = signercn ? CFStringGetCStringPtr(signercn,kCFStringEncodingMacRoman) : "<NULL>"; 531 fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, px); 532 } 533 SecCmsSignedDataVerifySignerInfo(sigd, j, decodeOptions->options->certDBHandle, 534 policy, NULL); 535 if (decodeOptions->headerLevel >= 0) 536 fprintf(out, "signer%d.status=%s; ", j, 537 SecCmsUtilVerificationStatusToString(SecCmsSignerInfoGetVerificationStatus(si))); 538 /* XXX what do we do if we don't print headers? */ 539 } 540 break; 541 case SEC_OID_PKCS7_ENVELOPED_DATA: 542 if (decodeOptions->headerLevel >= 0) 543 fprintf(out, "type=envelopedData; "); 544 envd = (SecCmsEnvelopedDataRef )SecCmsContentInfoGetContent(cinfo); 545 break; 546 case SEC_OID_PKCS7_ENCRYPTED_DATA: 547 if (decodeOptions->headerLevel >= 0) 548 fprintf(out, "type=encryptedData; "); 549 encd = (SecCmsEncryptedDataRef )SecCmsContentInfoGetContent(cinfo); 550 break; 551 case SEC_OID_PKCS7_DATA: 552 if (decodeOptions->headerLevel >= 0) 553 fprintf(out, "type=data; "); 554 break; 555 default: 556 break; 557 } 558 if (decodeOptions->headerLevel >= 0) 559 fprintf(out, "\n"); 560 } 561 562 if (!decodeOptions->suppressContent) 563 { 564 item = decodeOptions->contentFile ? &sitem : 565 SecCmsMessageGetContent(cmsg); 566 /* Copy the data. */ 567 output->Length = item->Length; 568 output->Data = malloc(output->Length); 569 memcpy(output->Data, item->Data, output->Length); 570 } 571 572 if (policy) CFRelease(policy); 573 574 return cmsg; 575loser: 576 if (policy) CFRelease(policy); 577 if (cmsg) SecCmsMessageDestroy(cmsg); 578 return NULL; 579} 580 581/* example of a callback function to use with encoder */ 582/* 583static void 584writeout(void *arg, const char *buf, unsigned long len) 585{ 586 FILE *f = (FILE *)arg; 587 588 if (f != NULL && buf != NULL) 589 (void)fwrite(buf, len, 1, f); 590} 591*/ 592 593static SecCmsMessageRef signed_data(struct signOptionsStr *signOptions) 594{ 595 SecCmsMessageRef cmsg = NULL; 596 SecCmsContentInfoRef cinfo; 597 SecCmsSignedDataRef sigd; 598 SecCmsSignerInfoRef signerinfo; 599 SecIdentityRef identity = NULL; 600 SecCertificateRef cert = NULL, ekpcert = NULL; 601 OSStatus rv; 602 603 if (cms_verbose) 604 { 605 fprintf(stderr, "Input to signed_data:\n"); 606 if (signOptions->options->password) 607 fprintf(stderr, "password [%s]\n", "***" /*signOptions->options->password*/); 608 else 609 fprintf(stderr, "password [NULL]\n"); 610 fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage); 611 if (signOptions->options->certDBHandle) 612 fprintf(stderr, "certdb [%p]\n", signOptions->options->certDBHandle); 613 else 614 fprintf(stderr, "certdb [NULL]\n"); 615 if (signOptions->nickname) 616 fprintf(stderr, "nickname [%s]\n", signOptions->nickname); 617 else 618 fprintf(stderr, "nickname [NULL]\n"); 619 if (signOptions->subjectKeyID) 620 fprintf(stderr, "subject Key ID [%s]\n", signOptions->subjectKeyID); 621 } 622 623 if (signOptions->subjectKeyID) 624 { 625 if ((identity = CERT_FindIdentityBySubjectKeyID(signOptions->options->certDBHandle, 626 signOptions->subjectKeyID)) == NULL) 627 { 628 sec_error("could not find signing identity for subject key ID: \"%s\"", signOptions->subjectKeyID); 629 return NULL; 630 } 631 632 if (cms_verbose) 633 fprintf(stderr, "Found identity for subject key ID %s\n", signOptions->subjectKeyID); 634 } 635 else if (signOptions->nickname) 636 { 637 if ((identity = CERT_FindIdentityByUsage(signOptions->options->certDBHandle, 638 signOptions->nickname, 639 signOptions->options->certUsage, 640 false)) == NULL) 641 { 642 // look for identity by common name rather than email address 643 if ((identity = find_identity(signOptions->options->certDBHandle, 644 signOptions->nickname, 645 NULL, 646 signOptions->options->certUsage)) == NULL) 647 { 648 sec_error("could not find signing identity for name: \"%s\"", signOptions->nickname); 649 return NULL; 650 } 651 } 652 653 if (cms_verbose) 654 fprintf(stderr, "Found identity for %s\n", signOptions->nickname); 655 } 656 else 657 { 658 // no identity was specified 659 sec_error("no signing identity was specified"); 660 return NULL; 661 } 662 663 // Get the cert from the identity 664 SEC_CHECK(SecIdentityCopyCertificate(identity, &cert), 665 "SecIdentityCopyCertificate"); 666 // create the message object on its own pool 667 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message"); 668 // build chain of objects: message->signedData->data 669 SEC_CHECK0(sigd = SecCmsSignedDataCreate(cmsg), 670 "cannot create CMS signedData object"); 671 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg), 672 "message has no content info"); 673 SEC_CHECK(SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd), 674 "cannot attach CMS signedData object"); 675 SEC_CHECK0(cinfo = SecCmsSignedDataGetContentInfo(sigd), 676 "signed data has no content info"); 677 /* we're always passing data in and detaching optionally */ 678 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, signOptions->detached), 679 "cannot attach CMS data object"); 680 // create & attach signer information 681 SEC_CHECK0(signerinfo = SecCmsSignerInfoCreate(cmsg, identity, signOptions->hashAlgTag), 682 "cannot create CMS signerInfo object"); 683 if (cms_verbose) 684 fprintf(stderr,"Created CMS message, added signed data w/ signerinfo\n"); 685 686 // we want the cert chain included for this one 687 SEC_CHECK(SecCmsSignerInfoIncludeCerts(signerinfo, SecCmsCMCertChain, signOptions->options->certUsage), 688 "cannot add cert chain"); 689 690 if (cms_verbose) 691 fprintf(stderr, "imported certificate\n"); 692 693 if (signOptions->signingTime) 694 SEC_CHECK(SecCmsSignerInfoAddSigningTime(signerinfo, CFAbsoluteTimeGetCurrent()), 695 "cannot add signingTime attribute"); 696 697 if (signOptions->smimeProfile) 698 SEC_CHECK(SecCmsSignerInfoAddSMIMECaps(signerinfo), 699 "cannot add SMIMECaps attribute"); 700 701 if (signOptions->wantTimestamping) 702 { 703 CFErrorRef error = NULL; 704 SecCmsMessageSetTSAContext(cmsg, SecCmsTSAGetDefaultContext(&error)); 705 } 706 707 if (!signOptions->encryptionKeyPreferenceNick) 708 { 709 /* check signing cert for fitness as encryption cert */ 710 OSStatus FitForEncrypt = CERT_CheckCertUsage(cert, certUsageEmailRecipient); 711 712 if (noErr == FitForEncrypt) 713 { 714 /* if yes, add signing cert as EncryptionKeyPreference */ 715 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, cert, signOptions->options->certDBHandle), 716 "cannot add default SMIMEEncKeyPrefs attribute"); 717 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, cert, signOptions->options->certDBHandle), 718 "cannot add default MS SMIMEEncKeyPrefs attribute"); 719 } 720 else 721 { 722 /* this is a dual-key cert case, we need to look for the encryption 723 certificate under the same nickname as the signing cert */ 724 /* get the cert, add it to the message */ 725 if ((ekpcert = CERT_FindUserCertByUsage( 726 signOptions->options->certDBHandle, 727 signOptions->nickname, 728 certUsageEmailRecipient, 729 false)) == NULL) 730 { 731 sec_error("can find encryption cert for \"%s\"", signOptions->nickname); 732 goto loser; 733 } 734 735 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle), 736 "cannot add SMIMEEncKeyPrefs attribute"); 737 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle), 738 "cannot add MS SMIMEEncKeyPrefs attribute"); 739 SEC_CHECK(SecCmsSignedDataAddCertificate(sigd, ekpcert), 740 "cannot add encryption certificate"); 741 } 742 } 743 else if (strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) 744 { 745 /* No action */ 746 } 747 else 748 { 749 /* specific email address for encryption preferred encryption cert specified. 750 get the cert, add it to the message */ 751 if ((ekpcert = CERT_FindUserCertByUsage( 752 signOptions->options->certDBHandle, 753 signOptions->encryptionKeyPreferenceNick, 754 certUsageEmailRecipient, false)) == NULL) 755 { 756 sec_error("can find encryption cert for \"%s\"", signOptions->encryptionKeyPreferenceNick); 757 goto loser; 758 } 759 760 SEC_CHECK(SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle), 761 "cannot add SMIMEEncKeyPrefs attribute"); 762 SEC_CHECK(SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,signOptions->options->certDBHandle), 763 "cannot add MS SMIMEEncKeyPrefs attribute"); 764 SEC_CHECK(SecCmsSignedDataAddCertificate(sigd, ekpcert), 765 "cannot add encryption certificate"); 766 } 767 768 SEC_CHECK(SecCmsSignedDataAddSignerInfo(sigd, signerinfo), 769 "cannot add CMS signerInfo object"); 770 771 if (cms_verbose) 772 fprintf(stderr, "created signed-data message\n"); 773 774 if (ekpcert) CFRelease(ekpcert); 775 if (cert) CFRelease(cert); 776 if (identity) CFRelease(identity); 777 return cmsg; 778 779loser: 780 if (ekpcert) CFRelease(ekpcert); 781 if (cert) CFRelease(cert); 782 if (identity) CFRelease(identity); 783 SecCmsMessageDestroy(cmsg); 784 return NULL; 785} 786 787static SecCmsMessageRef enveloped_data(struct envelopeOptionsStr *envelopeOptions) 788{ 789 SecCmsMessageRef cmsg = NULL; 790 SecCmsContentInfoRef cinfo; 791 SecCmsEnvelopedDataRef envd; 792 SecCmsRecipientInfoRef recipientinfo; 793 SecCertificateRef *recipientcerts = NULL; 794 SecKeychainRef dbhandle; 795 SECOidTag bulkalgtag; 796 OSStatus rv; 797 int keysize, i = 0; 798 int cnt; 799 800 dbhandle = envelopeOptions->options->certDBHandle; 801 /* count the recipients */ 802 SEC_CHECK0(cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients), 803 "please name at least one recipient"); 804 805 // @@@ find the recipient's certs by email address or nickname 806 if ((recipientcerts = (SecCertificateRef *)calloc((cnt+1), sizeof(SecCertificateRef))) == NULL) 807 { 808 sec_error("failed to alloc certs array: %s", strerror(errno)); 809 goto loser; 810 } 811 812 for (i = 0; envelopeOptions->recipients[i] != NULL; ++i) 813 { 814 if ((recipientcerts[i] = 815 CERT_FindCertByNicknameOrEmailAddr(dbhandle, envelopeOptions->recipients[i])) == NULL) 816 { 817 i = 0; 818 goto loser; 819 } 820 } 821 822 recipientcerts[i] = NULL; 823 i = 0; 824 825 // find a nice bulk algorithm 826 SEC_CHECK(SecSMIMEFindBulkAlgForRecipients(recipientcerts, &bulkalgtag, &keysize), 827 "cannot find common bulk algorithm"); 828 // create the message object on its own pool 829 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message"); 830 // build chain of objects: message->envelopedData->data 831 SEC_CHECK0(envd = SecCmsEnvelopedDataCreate(cmsg, bulkalgtag, keysize), 832 "cannot create CMS envelopedData object"); 833 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg), 834 "message has no content info"); 835 SEC_CHECK(SecCmsContentInfoSetContentEnvelopedData(cmsg, cinfo, envd), 836 "cannot attach CMS envelopedData object"); 837 SEC_CHECK0(cinfo = SecCmsEnvelopedDataGetContentInfo(envd), 838 "enveloped data has no content info"); 839 // We're always passing data in, so the content is NULL 840 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false), 841 "cannot attach CMS data object"); 842 843 // create & attach recipient information 844 for (i = 0; recipientcerts[i] != NULL; i++) 845 { 846 SEC_CHECK0(recipientinfo = SecCmsRecipientInfoCreate(cmsg, recipientcerts[i]), 847 "cannot create CMS recipientInfo object"); 848 SEC_CHECK(SecCmsEnvelopedDataAddRecipient(envd, recipientinfo), 849 "cannot add CMS recipientInfo object"); 850 CFRelease(recipientcerts[i]); 851 } 852 853 if (recipientcerts) 854 free(recipientcerts); 855 856 return cmsg; 857loser: 858 if (recipientcerts) 859 { 860 for (; recipientcerts[i] != NULL; i++) 861 CFRelease(recipientcerts[i]); 862 } 863 864 if (cmsg) 865 SecCmsMessageDestroy(cmsg); 866 867 if (recipientcerts) 868 free(recipientcerts); 869 870 return NULL; 871} 872 873SecSymmetricKeyRef dkcb(void *arg, SECAlgorithmID *algid) 874{ 875 return (SecSymmetricKeyRef)arg; 876} 877 878static OSStatus get_enc_params(struct encryptOptionsStr *encryptOptions) 879{ 880 struct envelopeOptionsStr envelopeOptions; 881 OSStatus rv = paramErr; 882 SecCmsMessageRef env_cmsg = NULL; 883 SecCmsContentInfoRef cinfo; 884 int i, nlevels; 885 886 // construct an enveloped data message to obtain bulk keys 887 if (encryptOptions->envmsg) 888 env_cmsg = encryptOptions->envmsg; // get it from an old message 889 else 890 { 891 CSSM_DATA dummyOut = { 0, }; 892 CSSM_DATA dummyIn = { 0, }; 893 char str[] = "Hello!"; 894 SecArenaPoolRef tmparena = NULL; 895 896 SEC_CHECK(SecArenaPoolCreate(1024, &tmparena), "failed to create arenapool"); 897 898 dummyIn.Data = (unsigned char *)str; 899 dummyIn.Length = strlen(str); 900 envelopeOptions.options = encryptOptions->options; 901 envelopeOptions.recipients = encryptOptions->recipients; 902 env_cmsg = enveloped_data(&envelopeOptions); 903 SecCmsMessageEncode(env_cmsg, &dummyIn, tmparena, &dummyOut); 904 fwrite(dummyOut.Data, 1, dummyOut.Length,encryptOptions->envFile); 905 906 SecArenaPoolFree(tmparena, false); 907 } 908 909 // get the content info for the enveloped data 910 nlevels = SecCmsMessageContentLevelCount(env_cmsg); 911 for (i = 0; i < nlevels; i++) 912 { 913 SECOidTag typetag; 914 cinfo = SecCmsMessageContentLevel(env_cmsg, i); 915 typetag = SecCmsContentInfoGetContentTypeTag(cinfo); 916 if (typetag == SEC_OID_PKCS7_DATA) 917 { 918 // get the symmetric key 919 encryptOptions->bulkalgtag = SecCmsContentInfoGetContentEncAlgTag(cinfo); 920 encryptOptions->keysize = SecCmsContentInfoGetBulkKeySize(cinfo); 921 encryptOptions->bulkkey = SecCmsContentInfoGetBulkKey(cinfo); 922 rv = noErr; 923 break; 924 } 925 } 926 if (i == nlevels) 927 sec_error("could not retrieve enveloped data: messsage has: %ld levels", nlevels); 928 929loser: 930 if (env_cmsg) 931 SecCmsMessageDestroy(env_cmsg); 932 933 return rv; 934} 935 936static SecCmsMessageRef encrypted_data(struct encryptOptionsStr *encryptOptions) 937{ 938 OSStatus rv = paramErr; 939 SecCmsMessageRef cmsg = NULL; 940 SecCmsContentInfoRef cinfo; 941 SecCmsEncryptedDataRef encd; 942 SecCmsEncoderRef ecx = NULL; 943 SecArenaPoolRef tmppoolp = NULL; 944 CSSM_DATA derOut = { 0, }; 945 946 /* arena for output */ 947 SEC_CHECK(SecArenaPoolCreate(1024, &tmppoolp), "failed to create arenapool"); 948 // create the message object on its own pool 949 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message"); 950 // build chain of objects: message->encryptedData->data 951 SEC_CHECK0(encd = SecCmsEncryptedDataCreate(cmsg, encryptOptions->bulkalgtag, 952 encryptOptions->keysize), 953 "cannot create CMS encryptedData object"); 954 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg), 955 "message has no content info"); 956 SEC_CHECK(SecCmsContentInfoSetContentEncryptedData(cmsg, cinfo, encd), 957 "cannot attach CMS encryptedData object"); 958 SEC_CHECK0(cinfo = SecCmsEncryptedDataGetContentInfo(encd), 959 "encrypted data has no content info"); 960 /* we're always passing data in, so the content is NULL */ 961 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false), 962 "cannot attach CMS data object"); 963 SEC_CHECK(SecCmsEncoderCreate(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL, 964 dkcb, encryptOptions->bulkkey, NULL, NULL, &ecx), 965 "cannot create encoder context"); 966 SEC_CHECK(do_update((update_func *)SecCmsEncoderUpdate, ecx, encryptOptions->input->Data, 967 encryptOptions->input->Length), 968 "failed to add data to encoder"); 969 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encrypt data"); 970 fwrite(derOut.Data, derOut.Length, 1, encryptOptions->outfile); 971 /* @@@ Check and report write errors. */ 972 /* 973 if (bulkkey) 974 CFRelease(bulkkey); 975 */ 976 977 if (tmppoolp) 978 SecArenaPoolFree(tmppoolp, false); 979 return cmsg; 980loser: 981 /* 982 if (bulkkey) 983 CFRelease(bulkkey); 984 */ 985 if (tmppoolp) 986 SecArenaPoolFree(tmppoolp, false); 987 if (cmsg) 988 SecCmsMessageDestroy(cmsg); 989 990 return NULL; 991} 992 993static SecCmsMessageRef signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions) 994{ 995 SecCmsMessageRef cmsg = NULL; 996 SecCmsContentInfoRef cinfo; 997 SecCmsSignedDataRef sigd; 998 SecCertificateRef *certs = NULL; 999 SecKeychainRef dbhandle; 1000 OSStatus rv; 1001 1002 int i = 0, cnt; 1003 dbhandle = certsonlyOptions->options->certDBHandle; 1004 SEC_CHECK0(cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients), 1005 "please indicate the nickname of a certificate to sign with"); 1006 if ((certs = (SecCertificateRef *)calloc((cnt+1), sizeof(SecCertificateRef))) == NULL) 1007 { 1008 sec_error("failed to alloc certs array: %s", strerror(errno)); 1009 goto loser; 1010 } 1011 for (i=0; certsonlyOptions->recipients && certsonlyOptions->recipients[i] != NULL; i++) 1012 { 1013 if ((certs[i] = CERT_FindCertByNicknameOrEmailAddr(dbhandle,certsonlyOptions->recipients[i])) == NULL) 1014 { 1015 i=0; 1016 goto loser; 1017 } 1018 } 1019 certs[i] = NULL; 1020 i = 0; 1021 // create the message object on its own pool 1022 SEC_CHECK0(cmsg = SecCmsMessageCreate(NULL), "cannot create CMS message"); 1023 // build chain of objects: message->signedData->data 1024 SEC_CHECK0(sigd = SecCmsSignedDataCreateCertsOnly(cmsg, certs[0], true), 1025 "cannot create certs only CMS signedData object"); 1026 CFRelease(certs[0]); 1027 for (i = 1; i < cnt; ++i) 1028 { 1029 SEC_CHECK2(SecCmsSignedDataAddCertChain(sigd, certs[i]), 1030 "cannot add cert chain for \"%s\"", certsonlyOptions->recipients[i]); 1031 CFRelease(certs[i]); 1032 } 1033 1034 SEC_CHECK0(cinfo = SecCmsMessageGetContentInfo(cmsg), 1035 "message has no content info"); 1036 SEC_CHECK(SecCmsContentInfoSetContentSignedData(cmsg, cinfo, sigd), 1037 "cannot attach CMS signedData object"); 1038 SEC_CHECK0(cinfo = SecCmsSignedDataGetContentInfo(sigd), 1039 "signed data has no content info"); 1040 SEC_CHECK(SecCmsContentInfoSetContentData(cmsg, cinfo, NULL, false), 1041 "cannot attach CMS data object"); 1042 1043 if (certs) 1044 free(certs); 1045 1046 return cmsg; 1047loser: 1048 if (certs) 1049 { 1050 for (; i < cnt; ++i) 1051 CFRelease(certs[i]); 1052 1053 free(certs); 1054 } 1055 if (cmsg) SecCmsMessageDestroy(cmsg); 1056 1057 return NULL; 1058} 1059 1060typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode; 1061 1062int cms_util(int argc, char **argv) 1063{ 1064 FILE *outFile; 1065 SecCmsMessageRef cmsg = NULL; 1066 FILE *inFile; 1067 int ch; 1068 Mode mode = UNKNOWN; 1069 PK11PasswordFunc pwcb; 1070 void *pwcb_arg; 1071 struct decodeOptionsStr decodeOptions = { 0 }; 1072 struct signOptionsStr signOptions = { 0 }; 1073 struct envelopeOptionsStr envelopeOptions = { 0 }; 1074 struct certsonlyOptionsStr certsonlyOptions = { 0 }; 1075 struct encryptOptionsStr encryptOptions = { 0 }; 1076 struct optionsStr options = { 0 }; 1077 int result = 1; 1078 static char *ptrarray[128] = { 0 }; 1079 int nrecipients = 0; 1080 char *str, *tok; 1081 char *envFileName; 1082 const char *keychainName = NULL; 1083 CSSM_DATA input = { 0,}; 1084 CSSM_DATA output = { 0,}; 1085 CSSM_DATA dummy = { 0, }; 1086 CSSM_DATA envmsg = { 0, }; 1087 OSStatus rv; 1088 1089 inFile = stdin; 1090 outFile = stdout; 1091 envFileName = NULL; 1092 mode = UNKNOWN; 1093 decodeOptions.contentFile = NULL; 1094 decodeOptions.suppressContent = false; 1095 decodeOptions.headerLevel = -1; 1096 options.certUsage = certUsageEmailSigner; 1097 options.password = NULL; 1098 signOptions.nickname = NULL; 1099 signOptions.subjectKeyID = NULL; 1100 signOptions.detached = false; 1101 signOptions.signingTime = false; 1102 signOptions.smimeProfile = false; 1103 signOptions.encryptionKeyPreferenceNick = NULL; 1104 signOptions.hashAlgTag = SEC_OID_SHA1; 1105 envelopeOptions.recipients = NULL; 1106 encryptOptions.recipients = NULL; 1107 encryptOptions.envmsg = NULL; 1108 encryptOptions.envFile = NULL; 1109 encryptOptions.bulkalgtag = SEC_OID_UNKNOWN; 1110 encryptOptions.bulkkey = NULL; 1111 encryptOptions.keysize = -1; 1112 1113 // Parse command line arguments 1114 while ((ch = getopt(argc, argv, "CDEGH:N:OPSTY:Z:c:de:h:i:k:no:p:r:su:vt:")) != -1) 1115 { 1116 switch (ch) 1117 { 1118 case 'C': 1119 mode = ENCRYPT; 1120 break; 1121 case 'D': 1122 mode = DECODE; 1123 break; 1124 case 'E': 1125 mode = ENVELOPE; 1126 break; 1127 case 'G': 1128 if (mode != SIGN) { 1129 sec_error("option -G only supported with option -S"); 1130 result = 2; /* Trigger usage message. */ 1131 goto loser; 1132 } 1133 signOptions.signingTime = true; 1134 break; 1135 case 'H': 1136 if (mode != SIGN) { 1137 sec_error("option -n only supported with option -D"); 1138 result = 2; /* Trigger usage message. */ 1139 goto loser; 1140 } 1141 decodeOptions.suppressContent = true; 1142 if (!strcmp(optarg, "MD2")) 1143 signOptions.hashAlgTag = SEC_OID_MD2; 1144 else if (!strcmp(optarg, "MD4")) 1145 signOptions.hashAlgTag = SEC_OID_MD4; 1146 else if (!strcmp(optarg, "MD5")) 1147 signOptions.hashAlgTag = SEC_OID_MD5; 1148 else if (!strcmp(optarg, "SHA1")) 1149 signOptions.hashAlgTag = SEC_OID_SHA1; 1150 else if (!strcmp(optarg, "SHA256")) 1151 signOptions.hashAlgTag = SEC_OID_SHA256; 1152 else if (!strcmp(optarg, "SHA384")) 1153 signOptions.hashAlgTag = SEC_OID_SHA384; 1154 else if (!strcmp(optarg, "SHA512")) 1155 signOptions.hashAlgTag = SEC_OID_SHA512; 1156 else { 1157 sec_error("option -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512"); 1158 goto loser; 1159 } 1160 break; 1161 case 'N': 1162 if (mode != SIGN) { 1163 sec_error("option -N only supported with option -S"); 1164 result = 2; /* Trigger usage message. */ 1165 goto loser; 1166 } 1167 signOptions.nickname = strdup(optarg); 1168 break; 1169 case 'O': 1170 mode = CERTSONLY; 1171 break; 1172 case 'P': 1173 if (mode != SIGN) { 1174 sec_error("option -P only supported with option -S"); 1175 result = 2; /* Trigger usage message. */ 1176 goto loser; 1177 } 1178 signOptions.smimeProfile = true; 1179 break; 1180 case 'S': 1181 mode = SIGN; 1182 break; 1183 case 'T': 1184 if (mode != SIGN) { 1185 sec_error("option -T only supported with option -S"); 1186 result = 2; /* Trigger usage message. */ 1187 goto loser; 1188 } 1189 signOptions.detached = true; 1190 break; 1191 case 'Y': 1192 if (mode != SIGN) { 1193 sec_error("option -Y only supported with option -S"); 1194 result = 2; /* Trigger usage message. */ 1195 goto loser; 1196 } 1197 signOptions.encryptionKeyPreferenceNick = strdup(optarg); 1198 break; 1199 1200 case 'c': 1201 if (mode != DECODE) 1202 { 1203 sec_error("option -c only supported with option -D"); 1204 result = 2; /* Trigger usage message. */ 1205 goto loser; 1206 } 1207 if ((decodeOptions.contentFile = fopen(optarg, "rb")) == NULL) 1208 { 1209 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno)); 1210 result = 1; 1211 goto loser; 1212 } 1213 break; 1214 1215#ifdef HAVE_DODUMPSTATES 1216 case 'd': 1217 doDumpStates++; 1218 break; 1219#endif /* HAVE_DODUMPSTATES */ 1220 1221 case 'e': 1222 envFileName = strdup(optarg); 1223 encryptOptions.envFile = fopen(envFileName, "rb"); // PR_RDONLY, 00660); 1224 break; 1225 1226 case 'h': 1227 if (mode != DECODE) { 1228 sec_error("option -h only supported with option -D"); 1229 result = 2; /* Trigger usage message. */ 1230 goto loser; 1231 } 1232 decodeOptions.headerLevel = atoi(optarg); 1233 if (decodeOptions.headerLevel < 0) { 1234 sec_error("option -h cannot have a negative value"); 1235 goto loser; 1236 } 1237 break; 1238 case 'i': 1239 inFile = fopen(optarg,"rb"); // PR_RDONLY, 00660); 1240 if (inFile == NULL) 1241 { 1242 sec_error("unable to open \"%s\" for reading: %s", optarg, strerror(errno)); 1243 goto loser; 1244 } 1245 break; 1246 1247 case 'k': 1248 keychainName = optarg; 1249 break; 1250 1251 case 'n': 1252 if (mode != DECODE) 1253 { 1254 sec_error("option -n only supported with option -D"); 1255 result = 2; /* Trigger usage message. */ 1256 goto loser; 1257 } 1258 decodeOptions.suppressContent = true; 1259 break; 1260 case 'o': 1261 outFile = fopen(optarg, "wb"); 1262 if (outFile == NULL) 1263 { 1264 sec_error("unable to open \"%s\" for writing: %s", optarg, strerror(errno)); 1265 goto loser; 1266 } 1267 break; 1268 case 'p': 1269 if (!optarg) 1270 { 1271 sec_error("option -p must have a value"); 1272 result = 2; /* Trigger usage message. */ 1273 goto loser; 1274 } 1275 1276 options.password = (PK11PasswordFunc)ownpw;//strdup(optarg); 1277 break; 1278 1279 case 'r': 1280 if (!optarg) 1281 { 1282 sec_error("option -r must have a value"); 1283 result = 2; /* Trigger usage message. */ 1284 goto loser; 1285 } 1286 1287 envelopeOptions.recipients = ptrarray; 1288 str = (char *)optarg; 1289 do { 1290 tok = strchr(str, ','); 1291 if (tok) *tok = '\0'; 1292 envelopeOptions.recipients[nrecipients++] = strdup(str); 1293 if (tok) str = tok + 1; 1294 } while (tok); 1295 envelopeOptions.recipients[nrecipients] = NULL; 1296 encryptOptions.recipients = envelopeOptions.recipients; 1297 certsonlyOptions.recipients = envelopeOptions.recipients; 1298 break; 1299 1300 case 's': 1301 cms_update_single_byte = 1; 1302 break; 1303 1304 case 'Z': 1305 if (!optarg) 1306 { 1307 sec_error("option -Z must have a value"); 1308 result = 2; /* Trigger usage message. */ 1309 goto loser; 1310 } 1311 signOptions.subjectKeyID = strdup(optarg); 1312 1313 break; 1314 1315 case 'u': 1316 { 1317 int usageType = atoi (strdup(optarg)); 1318 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) 1319 { 1320 result = 1; 1321 goto loser; 1322 } 1323 options.certUsage = (SECCertUsage)usageType; 1324 break; 1325 } 1326 case 'v': 1327 cms_verbose = 1; 1328 break; 1329 case 't': 1330 if (optarg) 1331 signOptions.timestampingURL = strdup(optarg); 1332 signOptions.wantTimestamping = true; 1333 break; 1334 default: 1335 result = 2; /* Trigger usage message. */ 1336 goto loser; 1337 } 1338 } 1339 1340 argc -= optind; 1341 argv += optind; 1342 1343 if (argc != 0 || mode == UNKNOWN) 1344 { 1345 result = 2; /* Trigger usage message. */ 1346 goto loser; 1347 } 1348 1349 result = 0; 1350 1351 if (mode != CERTSONLY) 1352 SECU_FileToItem(&input, inFile); 1353 if (inFile != stdin) 1354 fclose(inFile); 1355 if (cms_verbose) 1356 fprintf(stderr, "received commands\n"); 1357 1358 /* Call the libsec initialization routines */ 1359 if (keychainName) 1360 { 1361 check_obsolete_keychain(keychainName); 1362 options.certDBHandle = keychain_open(keychainName); 1363 if (!options.certDBHandle) 1364 { 1365 sec_perror("SecKeychainOpen", errSecInvalidKeychain); 1366 result = 1; 1367 goto loser; 1368 } 1369 } 1370 1371 if (cms_verbose) 1372 fprintf(stderr, "Got default certdb\n"); 1373 1374 switch (mode) 1375 { 1376 case DECODE: 1377 decodeOptions.options = &options; 1378 if (encryptOptions.envFile) 1379 { 1380 /* Decoding encrypted-data, so get the bulkkey from an 1381 * enveloped-data message. 1382 */ 1383 SECU_FileToItem(&envmsg, encryptOptions.envFile); 1384 decodeOptions.options = &options; 1385 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg, &decodeOptions); 1386 if (!encryptOptions.envmsg) 1387 { 1388 sec_error("problem decoding env msg"); 1389 result = 1; 1390 break; 1391 } 1392 rv = get_enc_params(&encryptOptions); 1393 decodeOptions.dkcb = dkcb; 1394 decodeOptions.bulkkey = encryptOptions.bulkkey; 1395 } 1396 cmsg = decode(outFile, &output, &input, &decodeOptions); 1397 if (!cmsg) 1398 { 1399 sec_error("problem decoding"); 1400 result = 1; 1401 } 1402 fwrite(output.Data, output.Length, 1, outFile); 1403 break; 1404 case SIGN: 1405 signOptions.options = &options; 1406 cmsg = signed_data(&signOptions); 1407 if (!cmsg) 1408 { 1409 sec_error("problem signing"); 1410 result = 1; 1411 } 1412 break; 1413 case ENCRYPT: 1414 if (!envFileName) 1415 { 1416 sec_error("you must specify an envelope file with -e"); 1417 result = 1; 1418 goto loser; 1419 } 1420 encryptOptions.options = &options; 1421 encryptOptions.input = &input; 1422 encryptOptions.outfile = outFile; 1423 if (!encryptOptions.envFile) { 1424 encryptOptions.envFile = fopen(envFileName,"wb"); //PR_WRONLY|PR_CREATE_FILE, 00660); 1425 if (!encryptOptions.envFile) 1426 { 1427 sec_error("failed to create file %s: %s", envFileName, strerror(errno)); 1428 result = 1; 1429 goto loser; 1430 } 1431 } 1432 else 1433 { 1434 SECU_FileToItem(&envmsg, encryptOptions.envFile); 1435 decodeOptions.options = &options; 1436 encryptOptions.envmsg = decode(NULL, &dummy, &envmsg, 1437 &decodeOptions); 1438 if (encryptOptions.envmsg == NULL) 1439 { 1440 sec_error("problem decrypting env msg"); 1441 result = 1; 1442 break; 1443 } 1444 } 1445 1446 /* decode an enveloped-data message to get the bulkkey (create 1447 * a new one if neccessary) 1448 */ 1449 rv = get_enc_params(&encryptOptions); 1450 /* create the encrypted-data message */ 1451 cmsg = encrypted_data(&encryptOptions); 1452 if (!cmsg) 1453 { 1454 sec_error("problem encrypting"); 1455 result = 1; 1456 } 1457 1458 if (encryptOptions.bulkkey) 1459 { 1460 CFRelease(encryptOptions.bulkkey); 1461 encryptOptions.bulkkey = NULL; 1462 } 1463 break; 1464 case ENVELOPE: 1465 envelopeOptions.options = &options; 1466 cmsg = enveloped_data(&envelopeOptions); 1467 if (!cmsg) 1468 { 1469 sec_error("problem enveloping"); 1470 result = 1; 1471 } 1472 break; 1473 case CERTSONLY: 1474 certsonlyOptions.options = &options; 1475 cmsg = signed_data_certsonly(&certsonlyOptions); 1476 if (!cmsg) 1477 { 1478 sec_error("problem with certs-only"); 1479 result = 1; 1480 } 1481 break; 1482 case UNKNOWN: 1483 /* Already handled above. */ 1484 break; 1485 } 1486 1487 if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY) 1488 && (!result) ) 1489 { 1490 SecArenaPoolRef arena = NULL; 1491 SecCmsEncoderRef ecx; 1492 CSSM_DATA output = {}; 1493 1494 SEC_CHECK(SecArenaPoolCreate(1024, &arena), "failed to create arenapool"); 1495 pwcb = (PK11PasswordFunc)((options.password != NULL) ? ownpw : NULL); 1496 pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL; 1497 if (cms_verbose) { 1498 fprintf(stderr, "cmsg [%p]\n", cmsg); 1499 fprintf(stderr, "arena [%p]\n", arena); 1500 if (pwcb_arg) 1501 fprintf(stderr, "password [%s]\n", (char *)pwcb_arg); 1502 else 1503 fprintf(stderr, "password [NULL]\n"); 1504 } 1505 1506 SEC_CHECK(SecCmsEncoderCreate(cmsg, 1507 NULL, NULL, /* DER output callback */ 1508 &output, arena, /* destination storage */ 1509 pwcb, pwcb_arg, /* password callback */ 1510 NULL, NULL, /* decrypt key callback */ 1511 NULL, NULL, /* detached digests */ 1512 &ecx), 1513 "cannot create encoder context"); 1514 if (cms_verbose) 1515 { 1516 fprintf(stderr, "input len [%ld]\n", input.Length); 1517 { 1518 unsigned int j; 1519 for (j = 0; j < input.Length; ++j) 1520 fprintf(stderr, "%2x%c", input.Data[j], (j>0&&j%35==0)?'\n':' '); 1521 } 1522 } 1523 1524 if (input.Length > 0) { /* skip if certs-only (or other zero content) */ 1525 SEC_CHECK(SecCmsEncoderUpdate(ecx, (char *)input.Data, input.Length), 1526 "failed to add data to encoder"); 1527 } 1528 1529 SEC_CHECK(SecCmsEncoderFinish(ecx), "failed to encode data"); 1530 1531 if (cms_verbose) { 1532 fprintf(stderr, "encoding passed\n"); 1533 } 1534 1535 /*PR_Write(output.data, output.len);*/ 1536 fwrite(output.Data, output.Length, 1, outFile); 1537 if (cms_verbose) { 1538 fprintf(stderr, "wrote to file\n"); 1539 } 1540 SecArenaPoolFree(arena, false); 1541 } 1542 1543loser: 1544 if (cmsg) 1545 SecCmsMessageDestroy(cmsg); 1546 if (outFile != stdout) 1547 fclose(outFile); 1548 1549 if (decodeOptions.contentFile) 1550 fclose(decodeOptions.contentFile); 1551 1552 return result; 1553} 1554 1555 1556#pragma mark ================ Misc from NSS =================== 1557// from /security/nss/cmd/lib/secutil.c 1558 1559OSStatus 1560SECU_FileToItem(CSSM_DATA *dst, FILE *src) 1561{ 1562 const int kReadSize = 4096; 1563 size_t bytesRead, totalRead = 0; 1564 1565 do 1566 { 1567 /* Make room in dst for the new data. */ 1568 dst->Length += kReadSize; 1569 dst->Data = realloc(dst->Data, dst->Length); 1570 if (!dst->Data) 1571 return 1 /* @@@ memFullErr */; 1572 1573 bytesRead = fread (&dst->Data[totalRead], 1, kReadSize, src); 1574 totalRead += bytesRead; 1575 } while (bytesRead == kReadSize); 1576 1577 if (!feof (src)) 1578 { 1579 /* We are here, but there's no EOF. This is bad */ 1580 if (dst->Data) { 1581 free(dst->Data); 1582 dst->Data = NULL; 1583 dst->Length = 0; 1584 } 1585 return 1 /* @@@ ioErr */; 1586 } 1587 1588 /* Trim down the buffer. */ 1589 dst->Length = totalRead; 1590 dst->Data = realloc(dst->Data, totalRead); 1591 if (!dst->Data) 1592 return 1 /* @@@ memFullErr */; 1593 1594 return noErr; 1595} 1596