cmsutil.c (60384B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* 6 * cmsutil -- A command to work with CMS data 7 */ 8 9 #include "nspr.h" 10 #include "secutil.h" 11 #include "plgetopt.h" 12 #include "secpkcs7.h" 13 #include "cert.h" 14 #include "certdb.h" 15 #include "secoid.h" 16 #include "cms.h" 17 #include "nss.h" 18 #include "smime.h" 19 #include "pk11func.h" 20 #include "sechash.h" 21 22 #if defined(XP_UNIX) 23 #include <unistd.h> 24 #endif 25 26 #if defined(_WIN32) 27 #include "fcntl.h" 28 #include "io.h" 29 #endif 30 31 #include <stdio.h> 32 #include <string.h> 33 34 char *progName = NULL; 35 static int cms_verbose = 0; 36 static secuPWData pwdata = { PW_NONE, 0 }; 37 static PK11PasswordFunc pwcb = NULL; 38 static void *pwcb_arg = NULL; 39 40 /* XXX stolen from cmsarray.c 41 * nss_CMSArray_Count - count number of elements in array 42 */ 43 int 44 nss_CMSArray_Count(void **array) 45 { 46 int n = 0; 47 if (array == NULL) 48 return 0; 49 while (*array++ != NULL) 50 n++; 51 return n; 52 } 53 54 static SECStatus 55 DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input, 56 SECAlgorithmID **algids) 57 { 58 NSSCMSDigestContext *digcx; 59 SECStatus rv; 60 61 digcx = NSS_CMSDigestContext_StartMultiple(algids); 62 if (digcx == NULL) 63 return SECFailure; 64 65 NSS_CMSDigestContext_Update(digcx, input->data, input->len); 66 67 rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests); 68 return rv; 69 } 70 71 static void 72 Usage(void) 73 { 74 fprintf(stderr, 75 "Usage: %s [-C|-D|-E|-O|-S] [<options>] [-d dbdir] [-u certusage]\n" 76 " -C create a CMS encrypted data message\n" 77 " -D decode a CMS message\n" 78 " -b decode a batch of files named in infile\n" 79 " -c content use this detached content\n" 80 " -n suppress output of content\n" 81 " -h num display num levels of CMS message info as email headers\n" 82 " -k keep decoded encryption certs in perm cert db\n" 83 " -E create a CMS enveloped data message\n" 84 " -r id,... create envelope for these recipients,\n" 85 " where id can be a certificate nickname or email address\n" 86 " -S create a CMS signed data message\n" 87 " -G include a signing time attribute\n" 88 " -H hash use hash (default:SHA256)\n" 89 " -N nick use certificate named \"nick\" for signing\n" 90 " -P include a SMIMECapabilities attribute\n" 91 " -T do not include content in CMS message\n" 92 " -Y nick include a EncryptionKeyPreference attribute with cert\n" 93 " (use \"NONE\" to omit)\n" 94 " -O create a CMS signed message containing only certificates\n" 95 " General Options:\n" 96 " -d dbdir key/cert database directory (default: ~/.netscape)\n" 97 " -e envelope enveloped data message in this file is used for bulk key\n" 98 " -i infile use infile as source of data (default: stdin)\n" 99 " -o outfile use outfile as destination of data (default: stdout)\n" 100 " -p password use password as key db password (default: prompt)\n" 101 " -f pwfile use password file to set password on all PKCS#11 tokens)\n" 102 " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n" 103 " -v print debugging information\n" 104 "\n" 105 "Cert usage codes:\n", 106 progName); 107 fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " "); 108 fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " "); 109 fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " "); 110 fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " "); 111 fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " "); 112 fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " "); 113 fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " "); 114 fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " "); 115 fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " "); 116 fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " "); 117 fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " "); 118 fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " "); 119 fprintf(stderr, "%-25s 12 - certUsageIPsec\n", " "); 120 121 exit(-1); 122 } 123 124 struct optionsStr { 125 char *pwfile; 126 char *password; 127 SECCertUsage certUsage; 128 CERTCertDBHandle *certHandle; 129 }; 130 131 struct decodeOptionsStr { 132 struct optionsStr *options; 133 SECItem content; 134 int headerLevel; 135 PRBool suppressContent; 136 NSSCMSGetDecryptKeyCallback dkcb; 137 PK11SymKey *bulkkey; 138 PRBool keepCerts; 139 }; 140 141 struct signOptionsStr { 142 struct optionsStr *options; 143 char *nickname; 144 char *encryptionKeyPreferenceNick; 145 PRBool signingTime; 146 PRBool smimeProfile; 147 PRBool detached; 148 SECOidTag hashAlgTag; 149 }; 150 151 struct envelopeOptionsStr { 152 struct optionsStr *options; 153 char **recipients; 154 }; 155 156 struct certsonlyOptionsStr { 157 struct optionsStr *options; 158 char **recipients; 159 }; 160 161 struct encryptOptionsStr { 162 struct optionsStr *options; 163 char **recipients; 164 NSSCMSMessage *envmsg; 165 SECItem *input; 166 FILE *outfile; 167 PRFileDesc *envFile; 168 PK11SymKey *bulkkey; 169 SECOidTag bulkalgtag; 170 int keysize; 171 }; 172 173 static NSSCMSMessage * 174 decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions) 175 { 176 NSSCMSDecoderContext *dcx; 177 SECStatus rv; 178 NSSCMSMessage *cmsg; 179 int nlevels, i; 180 SECItem sitem = { 0, 0, 0 }; 181 182 PORT_SetError(0); 183 dcx = NSS_CMSDecoder_Start(NULL, 184 NULL, NULL, /* content callback */ 185 pwcb, pwcb_arg, /* password callback */ 186 decodeOptions->dkcb, /* decrypt key callback */ 187 decodeOptions->bulkkey); 188 if (dcx == NULL) { 189 fprintf(stderr, "%s: failed to set up message decoder.\n", progName); 190 return NULL; 191 } 192 rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len); 193 if (rv != SECSuccess) { 194 fprintf(stderr, "%s: failed to decode message.\n", progName); 195 NSS_CMSDecoder_Cancel(dcx); 196 return NULL; 197 } 198 cmsg = NSS_CMSDecoder_Finish(dcx); 199 if (cmsg == NULL) { 200 fprintf(stderr, "%s: failed to decode message.\n", progName); 201 return NULL; 202 } 203 204 if (decodeOptions->headerLevel >= 0) { 205 /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/ 206 fprintf(out, "SMIME: "); 207 } 208 209 nlevels = NSS_CMSMessage_ContentLevelCount(cmsg); 210 for (i = 0; i < nlevels; i++) { 211 NSSCMSContentInfo *cinfo; 212 SECOidTag typetag; 213 214 cinfo = NSS_CMSMessage_ContentLevel(cmsg, i); 215 typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 216 217 if (decodeOptions->headerLevel >= 0) 218 fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i); 219 220 switch (typetag) { 221 case SEC_OID_PKCS7_SIGNED_DATA: { 222 NSSCMSSignedData *sigd = NULL; 223 SECItem **digests = NULL; 224 int nsigners; 225 int j; 226 227 if (decodeOptions->headerLevel >= 0) 228 fprintf(out, "type=signedData; "); 229 sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo); 230 if (sigd == NULL) { 231 SECU_PrintError(progName, "signedData component missing"); 232 goto loser; 233 } 234 235 /* if we have a content file, but no digests for this signedData */ 236 if (decodeOptions->content.data != NULL && 237 !NSS_CMSSignedData_HasDigests(sigd)) { 238 PLArenaPool *poolp; 239 SECAlgorithmID **digestalgs; 240 241 /* detached content: grab content file */ 242 sitem = decodeOptions->content; 243 244 if ((poolp = PORT_NewArena(1024)) == NULL) { 245 fprintf(stderr, "cmsutil: Out of memory.\n"); 246 goto loser; 247 } 248 digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd); 249 if (DigestFile(poolp, &digests, &sitem, digestalgs) != 250 SECSuccess) { 251 SECU_PrintError(progName, 252 "problem computing message digest"); 253 PORT_FreeArena(poolp, PR_FALSE); 254 goto loser; 255 } 256 if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != 257 SECSuccess) { 258 SECU_PrintError(progName, 259 "problem setting message digests"); 260 PORT_FreeArena(poolp, PR_FALSE); 261 goto loser; 262 } 263 PORT_FreeArena(poolp, PR_FALSE); 264 } 265 266 /* import the certificates */ 267 if (NSS_CMSSignedData_ImportCerts(sigd, 268 decodeOptions->options->certHandle, 269 decodeOptions->options->certUsage, 270 decodeOptions->keepCerts) != 271 SECSuccess) { 272 SECU_PrintError(progName, "cert import failed"); 273 goto loser; 274 } 275 276 /* find out about signers */ 277 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); 278 if (decodeOptions->headerLevel >= 0) 279 fprintf(out, "nsigners=%d; ", nsigners); 280 if (nsigners == 0) { 281 /* Might be a cert transport message 282 ** or might be an invalid message, such as a QA test message 283 ** or a message from an attacker. 284 */ 285 rv = NSS_CMSSignedData_VerifyCertsOnly(sigd, 286 decodeOptions->options->certHandle, 287 decodeOptions->options->certUsage); 288 if (rv != SECSuccess) { 289 fprintf(stderr, "cmsutil: Verify certs-only failed!\n"); 290 goto loser; 291 } 292 return cmsg; 293 } 294 295 /* still no digests? */ 296 if (!NSS_CMSSignedData_HasDigests(sigd)) { 297 SECU_PrintError(progName, "no message digests"); 298 goto loser; 299 } 300 301 for (j = 0; j < nsigners; j++) { 302 const char *svs; 303 NSSCMSSignerInfo *si; 304 NSSCMSVerificationStatus vs; 305 SECStatus bad; 306 307 si = NSS_CMSSignedData_GetSignerInfo(sigd, j); 308 if (decodeOptions->headerLevel >= 0) { 309 char *signercn; 310 static char empty[] = { "" }; 311 312 signercn = NSS_CMSSignerInfo_GetSignerCommonName(si); 313 if (signercn == NULL) 314 signercn = empty; 315 fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn); 316 if (signercn != empty) 317 PORT_Free(signercn); 318 } 319 bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j, 320 decodeOptions->options->certHandle, 321 decodeOptions->options->certUsage); 322 vs = NSS_CMSSignerInfo_GetVerificationStatus(si); 323 svs = NSS_CMSUtil_VerificationStatusToString(vs); 324 if (decodeOptions->headerLevel >= 0) { 325 fprintf(out, "signer%d.status=%s; ", j, svs); 326 /* goto loser ? */ 327 } else if (bad && out) { 328 fprintf(stderr, "signer %d status = %s\n", j, svs); 329 goto loser; 330 } 331 /* if the signatures validate and we asked to keep 332 * the certs, save the profiles */ 333 if (decodeOptions->keepCerts) { 334 rv = NSS_SMIMESignerInfo_SaveSMIMEProfile(si); 335 if (rv != SECSuccess) { 336 SECU_PrintError(progName, "SMIME profile import failed"); 337 goto loser; 338 } 339 } 340 } 341 } break; 342 case SEC_OID_PKCS7_ENVELOPED_DATA: { 343 NSSCMSEnvelopedData *envd; 344 if (decodeOptions->headerLevel >= 0) 345 fprintf(out, "type=envelopedData; "); 346 envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo); 347 if (envd == NULL) { 348 SECU_PrintError(progName, "envelopedData component missing"); 349 goto loser; 350 } 351 } break; 352 case SEC_OID_PKCS7_ENCRYPTED_DATA: { 353 NSSCMSEncryptedData *encd; 354 if (decodeOptions->headerLevel >= 0) 355 fprintf(out, "type=encryptedData; "); 356 encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo); 357 if (encd == NULL) { 358 SECU_PrintError(progName, "encryptedData component missing"); 359 goto loser; 360 } 361 } break; 362 case SEC_OID_PKCS7_DATA: 363 if (decodeOptions->headerLevel >= 0) 364 fprintf(out, "type=data; "); 365 break; 366 default: 367 break; 368 } 369 if (decodeOptions->headerLevel >= 0) 370 fprintf(out, "\n"); 371 } 372 373 if (!decodeOptions->suppressContent && out) { 374 SECItem *item = (sitem.data ? &sitem 375 : NSS_CMSMessage_GetContent(cmsg)); 376 if (item && item->data && item->len) { 377 fwrite(item->data, item->len, 1, out); 378 } 379 } 380 return cmsg; 381 382 loser: 383 if (cmsg) 384 NSS_CMSMessage_Destroy(cmsg); 385 return NULL; 386 } 387 388 /* example of a callback function to use with encoder */ 389 /* 390 static void 391 writeout(void *arg, const char *buf, unsigned long len) 392 { 393 FILE *f = (FILE *)arg; 394 395 if (f != NULL && buf != NULL) 396 (void)fwrite(buf, len, 1, f); 397 } 398 */ 399 400 static NSSCMSMessage * 401 signed_data(struct signOptionsStr *signOptions) 402 { 403 NSSCMSMessage *cmsg = NULL; 404 NSSCMSContentInfo *cinfo; 405 NSSCMSSignedData *sigd; 406 NSSCMSSignerInfo *signerinfo = NULL; 407 CERTCertificate *cert = NULL, *ekpcert = NULL; 408 409 if (cms_verbose) { 410 fprintf(stderr, "Input to signed_data:\n"); 411 if (signOptions->options->password) 412 fprintf(stderr, "password [%s]\n", signOptions->options->password); 413 else if (signOptions->options->pwfile) 414 fprintf(stderr, "password file [%s]\n", signOptions->options->pwfile); 415 else 416 fprintf(stderr, "password [NULL]\n"); 417 fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage); 418 if (signOptions->options->certHandle) 419 fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle); 420 else 421 fprintf(stderr, "certdb [NULL]\n"); 422 if (signOptions->nickname) 423 fprintf(stderr, "nickname [%s]\n", signOptions->nickname); 424 else 425 fprintf(stderr, "nickname [NULL]\n"); 426 } 427 if (signOptions->nickname == NULL) { 428 fprintf(stderr, 429 "ERROR: please indicate the nickname of a certificate to sign with.\n"); 430 return NULL; 431 } 432 if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle, 433 signOptions->nickname, 434 signOptions->options->certUsage, 435 PR_FALSE, 436 &pwdata)) == NULL) { 437 SECU_PrintError(progName, 438 "the corresponding cert for key \"%s\" does not exist", 439 signOptions->nickname); 440 return NULL; 441 } 442 if (cms_verbose) { 443 fprintf(stderr, "Found certificate for %s\n", signOptions->nickname); 444 } 445 /* 446 * create the message object 447 */ 448 cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ 449 if (cmsg == NULL) { 450 fprintf(stderr, "ERROR: cannot create CMS message.\n"); 451 return NULL; 452 } 453 /* 454 * build chain of objects: message->signedData->data 455 */ 456 if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) { 457 fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); 458 goto loser; 459 } 460 cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 461 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != 462 SECSuccess) { 463 fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); 464 goto loser; 465 } 466 cinfo = NSS_CMSSignedData_GetContentInfo(sigd); 467 /* we're always passing data in and detaching optionally */ 468 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, 469 signOptions->detached) != 470 SECSuccess) { 471 fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 472 goto loser; 473 } 474 /* 475 * create & attach signer information 476 */ 477 signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag); 478 if (signerinfo == NULL) { 479 fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n"); 480 goto loser; 481 } 482 if (cms_verbose) { 483 fprintf(stderr, 484 "Created CMS message, added signed data w/ signerinfo\n"); 485 } 486 signerinfo->cmsg->pwfn_arg = pwcb_arg; 487 /* we want the cert chain included for this one */ 488 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, 489 signOptions->options->certUsage) != 490 SECSuccess) { 491 fprintf(stderr, "ERROR: cannot find cert chain.\n"); 492 goto loser; 493 } 494 if (cms_verbose) { 495 fprintf(stderr, "imported certificate\n"); 496 } 497 if (signOptions->signingTime) { 498 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != 499 SECSuccess) { 500 fprintf(stderr, "ERROR: cannot add signingTime attribute.\n"); 501 goto loser; 502 } 503 } 504 if (signOptions->smimeProfile) { 505 if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { 506 fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n"); 507 goto loser; 508 } 509 } 510 511 if (!signOptions->encryptionKeyPreferenceNick) { 512 /* check signing cert for fitness as encryption cert */ 513 SECStatus FitForEncrypt = CERT_CheckCertUsage(cert, 514 certUsageEmailRecipient); 515 516 if (SECSuccess == FitForEncrypt) { 517 /* if yes, add signing cert as EncryptionKeyPreference */ 518 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, 519 signOptions->options->certHandle) != 520 SECSuccess) { 521 fprintf(stderr, 522 "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n"); 523 goto loser; 524 } 525 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert, 526 signOptions->options->certHandle) != 527 SECSuccess) { 528 fprintf(stderr, 529 "ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n"); 530 goto loser; 531 } 532 } else { 533 /* this is a dual-key cert case, we need to look for the encryption 534 certificate under the same nickname as the signing cert */ 535 /* get the cert, add it to the message */ 536 if ((ekpcert = CERT_FindUserCertByUsage( 537 signOptions->options->certHandle, 538 signOptions->nickname, 539 certUsageEmailRecipient, 540 PR_FALSE, 541 &pwdata)) == NULL) { 542 SECU_PrintError(progName, 543 "the corresponding cert for key \"%s\" does not exist", 544 signOptions->encryptionKeyPreferenceNick); 545 goto loser; 546 } 547 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 548 signOptions->options->certHandle) != 549 SECSuccess) { 550 fprintf(stderr, 551 "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); 552 goto loser; 553 } 554 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 555 signOptions->options->certHandle) != 556 SECSuccess) { 557 fprintf(stderr, 558 "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); 559 goto loser; 560 } 561 if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { 562 fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); 563 goto loser; 564 } 565 } 566 } else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) { 567 /* No action */ 568 } else { 569 /* get the cert, add it to the message */ 570 if ((ekpcert = CERT_FindUserCertByUsage( 571 signOptions->options->certHandle, 572 signOptions->encryptionKeyPreferenceNick, 573 certUsageEmailRecipient, PR_FALSE, &pwdata)) == 574 NULL) { 575 SECU_PrintError(progName, 576 "the corresponding cert for key \"%s\" does not exist", 577 signOptions->encryptionKeyPreferenceNick); 578 goto loser; 579 } 580 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 581 signOptions->options->certHandle) != 582 SECSuccess) { 583 fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); 584 goto loser; 585 } 586 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 587 signOptions->options->certHandle) != 588 SECSuccess) { 589 fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); 590 goto loser; 591 } 592 if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { 593 fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); 594 goto loser; 595 } 596 } 597 598 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { 599 fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n"); 600 goto loser; 601 } 602 signerinfo = NULL; /* sigd has adopted signerinfo */ 603 if (cms_verbose) { 604 fprintf(stderr, "created signed-data message\n"); 605 } 606 if (ekpcert) { 607 CERT_DestroyCertificate(ekpcert); 608 } 609 if (cert) { 610 CERT_DestroyCertificate(cert); 611 } 612 return cmsg; 613 loser: 614 if (ekpcert) { 615 CERT_DestroyCertificate(ekpcert); 616 } 617 if (cert) { 618 CERT_DestroyCertificate(cert); 619 } 620 if (signerinfo) { 621 NSS_CMSSignerInfo_Destroy(signerinfo); 622 } 623 NSS_CMSMessage_Destroy(cmsg); 624 return NULL; 625 } 626 627 static NSSCMSMessage * 628 enveloped_data(struct envelopeOptionsStr *envelopeOptions) 629 { 630 NSSCMSMessage *cmsg = NULL; 631 NSSCMSContentInfo *cinfo; 632 NSSCMSEnvelopedData *envd; 633 NSSCMSRecipientInfo *recipientinfo; 634 CERTCertificate **recipientcerts = NULL; 635 CERTCertDBHandle *dbhandle; 636 PLArenaPool *tmppoolp = NULL; 637 SECOidTag bulkalgtag; 638 int keysize, i = 0; 639 int cnt; 640 dbhandle = envelopeOptions->options->certHandle; 641 /* count the recipients */ 642 if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) { 643 fprintf(stderr, "ERROR: please name at least one recipient.\n"); 644 goto loser; 645 } 646 if ((tmppoolp = PORT_NewArena(1024)) == NULL) { 647 fprintf(stderr, "ERROR: out of memory.\n"); 648 goto loser; 649 } 650 /* XXX find the recipient's certs by email address or nickname */ 651 if ((recipientcerts = 652 (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp, 653 (cnt + 1) * sizeof(CERTCertificate *))) == 654 NULL) { 655 fprintf(stderr, "ERROR: out of memory.\n"); 656 goto loser; 657 } 658 for (i = 0; envelopeOptions->recipients[i] != NULL; i++) { 659 if ((recipientcerts[i] = 660 CERT_FindCertByNicknameOrEmailAddr(dbhandle, 661 envelopeOptions->recipients[i])) == 662 NULL) { 663 SECU_PrintError(progName, "cannot find certificate for \"%s\"", 664 envelopeOptions->recipients[i]); 665 i = 0; 666 goto loser; 667 } 668 } 669 recipientcerts[i] = NULL; 670 i = 0; 671 /* find a nice bulk algorithm */ 672 if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, 673 &keysize) != SECSuccess) { 674 fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n"); 675 goto loser; 676 } 677 /* 678 * create the message object 679 */ 680 cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ 681 if (cmsg == NULL) { 682 fprintf(stderr, "ERROR: cannot create CMS message.\n"); 683 goto loser; 684 } 685 /* 686 * build chain of objects: message->envelopedData->data 687 */ 688 if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) == 689 NULL) { 690 fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n"); 691 goto loser; 692 } 693 cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 694 if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != 695 SECSuccess) { 696 fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n"); 697 goto loser; 698 } 699 cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); 700 /* we're always passing data in, so the content is NULL */ 701 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != 702 SECSuccess) { 703 fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 704 goto loser; 705 } 706 /* 707 * create & attach recipient information 708 */ 709 for (i = 0; recipientcerts[i] != NULL; i++) { 710 if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, 711 recipientcerts[i])) == 712 NULL) { 713 fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n"); 714 goto loser; 715 } 716 if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) != 717 SECSuccess) { 718 fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n"); 719 goto loser; 720 } 721 CERT_DestroyCertificate(recipientcerts[i]); 722 } 723 if (tmppoolp) 724 PORT_FreeArena(tmppoolp, PR_FALSE); 725 return cmsg; 726 loser: 727 if (recipientcerts) { 728 for (; recipientcerts[i] != NULL; i++) { 729 CERT_DestroyCertificate(recipientcerts[i]); 730 } 731 } 732 if (cmsg) 733 NSS_CMSMessage_Destroy(cmsg); 734 if (tmppoolp) 735 PORT_FreeArena(tmppoolp, PR_FALSE); 736 return NULL; 737 } 738 739 PK11SymKey * 740 dkcb(void *arg, SECAlgorithmID *algid) 741 { 742 return (PK11SymKey *)arg; 743 } 744 745 static SECStatus 746 get_enc_params(struct encryptOptionsStr *encryptOptions) 747 { 748 struct envelopeOptionsStr envelopeOptions; 749 SECStatus rv = SECFailure; 750 NSSCMSMessage *env_cmsg; 751 NSSCMSContentInfo *cinfo; 752 int i, nlevels; 753 /* 754 * construct an enveloped data message to obtain bulk keys 755 */ 756 if (encryptOptions->envmsg) { 757 env_cmsg = encryptOptions->envmsg; /* get it from an old message */ 758 } else { 759 SECItem dummyOut = { 0, 0, 0 }; 760 SECItem dummyIn = { 0, 0, 0 }; 761 char str[] = "Hello!"; 762 PLArenaPool *tmparena = PORT_NewArena(1024); 763 dummyIn.data = (unsigned char *)str; 764 dummyIn.len = strlen(str); 765 envelopeOptions.options = encryptOptions->options; 766 envelopeOptions.recipients = encryptOptions->recipients; 767 env_cmsg = enveloped_data(&envelopeOptions); 768 NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena); 769 PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len); 770 PORT_FreeArena(tmparena, PR_FALSE); 771 } 772 /* 773 * get the content info for the enveloped data 774 */ 775 nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg); 776 for (i = 0; i < nlevels; i++) { 777 SECOidTag typetag; 778 cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i); 779 typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); 780 if (typetag == SEC_OID_PKCS7_DATA) { 781 /* 782 * get the symmetric key 783 */ 784 encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); 785 encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo); 786 encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); 787 rv = SECSuccess; 788 break; 789 } 790 } 791 if (i == nlevels) { 792 fprintf(stderr, "%s: could not retrieve enveloped data.", progName); 793 } 794 if (env_cmsg) 795 NSS_CMSMessage_Destroy(env_cmsg); 796 return rv; 797 } 798 799 static NSSCMSMessage * 800 encrypted_data(struct encryptOptionsStr *encryptOptions) 801 { 802 SECStatus rv = SECFailure; 803 NSSCMSMessage *cmsg = NULL; 804 NSSCMSContentInfo *cinfo; 805 NSSCMSEncryptedData *encd; 806 NSSCMSEncoderContext *ecx = NULL; 807 PLArenaPool *tmppoolp = NULL; 808 SECItem derOut = { 0, 0, 0 }; 809 /* arena for output */ 810 tmppoolp = PORT_NewArena(1024); 811 if (!tmppoolp) { 812 fprintf(stderr, "%s: out of memory.\n", progName); 813 return NULL; 814 } 815 /* 816 * create the message object 817 */ 818 cmsg = NSS_CMSMessage_Create(NULL); 819 if (cmsg == NULL) { 820 fprintf(stderr, "ERROR: cannot create CMS message.\n"); 821 goto loser; 822 } 823 /* 824 * build chain of objects: message->encryptedData->data 825 */ 826 if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag, 827 encryptOptions->keysize)) == 828 NULL) { 829 fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n"); 830 goto loser; 831 } 832 cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 833 if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd) != 834 SECSuccess) { 835 fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n"); 836 goto loser; 837 } 838 cinfo = NSS_CMSEncryptedData_GetContentInfo(encd); 839 /* we're always passing data in, so the content is NULL */ 840 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != 841 SECSuccess) { 842 fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 843 goto loser; 844 } 845 ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL, 846 dkcb, encryptOptions->bulkkey, NULL, NULL); 847 if (!ecx) { 848 fprintf(stderr, "%s: cannot create encoder context.\n", progName); 849 goto loser; 850 } 851 rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data, 852 encryptOptions->input->len); 853 if (rv) { 854 fprintf(stderr, "%s: failed to add data to encoder.\n", progName); 855 goto loser; 856 } 857 rv = NSS_CMSEncoder_Finish(ecx); 858 if (rv) { 859 fprintf(stderr, "%s: failed to encrypt data.\n", progName); 860 goto loser; 861 } 862 fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile); 863 /* 864 if (bulkkey) 865 PK11_FreeSymKey(bulkkey); 866 */ 867 if (tmppoolp) 868 PORT_FreeArena(tmppoolp, PR_FALSE); 869 return cmsg; 870 loser: 871 /* 872 if (bulkkey) 873 PK11_FreeSymKey(bulkkey); 874 */ 875 if (tmppoolp) 876 PORT_FreeArena(tmppoolp, PR_FALSE); 877 if (cmsg) 878 NSS_CMSMessage_Destroy(cmsg); 879 return NULL; 880 } 881 882 static NSSCMSMessage * 883 signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions) 884 { 885 NSSCMSMessage *cmsg = NULL; 886 NSSCMSContentInfo *cinfo; 887 NSSCMSSignedData *sigd; 888 CERTCertificate **certs = NULL; 889 CERTCertDBHandle *dbhandle; 890 PLArenaPool *tmppoolp = NULL; 891 int i = 0, cnt; 892 dbhandle = certsonlyOptions->options->certHandle; 893 if ((cnt = nss_CMSArray_Count((void **)certsonlyOptions->recipients)) == 0) { 894 fprintf(stderr, 895 "ERROR: please indicate the nickname of a certificate to sign with.\n"); 896 goto loser; 897 } 898 if (!(tmppoolp = PORT_NewArena(1024))) { 899 fprintf(stderr, "ERROR: out of memory.\n"); 900 goto loser; 901 } 902 if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) { 903 fprintf(stderr, "ERROR: out of memory.\n"); 904 goto loser; 905 } 906 for (i = 0; certsonlyOptions->recipients[i] != NULL; i++) { 907 if ((certs[i] = 908 CERT_FindCertByNicknameOrEmailAddr(dbhandle, 909 certsonlyOptions->recipients[i])) == 910 NULL) { 911 SECU_PrintError(progName, "cannot find certificate for \"%s\"", 912 certsonlyOptions->recipients[i]); 913 i = 0; 914 goto loser; 915 } 916 } 917 certs[i] = NULL; 918 i = 0; 919 /* 920 * create the message object 921 */ 922 cmsg = NSS_CMSMessage_Create(NULL); 923 if (cmsg == NULL) { 924 fprintf(stderr, "ERROR: cannot create CMS message.\n"); 925 goto loser; 926 } 927 /* 928 * build chain of objects: message->signedData->data 929 */ 930 if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE)) == 931 NULL) { 932 fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); 933 goto loser; 934 } 935 CERT_DestroyCertificate(certs[0]); 936 for (i = 1; i < cnt; i++) { 937 if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) { 938 fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n", 939 certsonlyOptions->recipients[i]); 940 goto loser; 941 } 942 CERT_DestroyCertificate(certs[i]); 943 } 944 cinfo = NSS_CMSMessage_GetContentInfo(cmsg); 945 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != 946 SECSuccess) { 947 fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); 948 goto loser; 949 } 950 cinfo = NSS_CMSSignedData_GetContentInfo(sigd); 951 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != 952 SECSuccess) { 953 fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); 954 goto loser; 955 } 956 if (tmppoolp) 957 PORT_FreeArena(tmppoolp, PR_FALSE); 958 return cmsg; 959 loser: 960 if (certs) { 961 for (; i < cnt; i++) { 962 CERT_DestroyCertificate(certs[i]); 963 } 964 } 965 if (cmsg) 966 NSS_CMSMessage_Destroy(cmsg); 967 if (tmppoolp) 968 PORT_FreeArena(tmppoolp, PR_FALSE); 969 return NULL; 970 } 971 972 static char * 973 pl_fgets(char *buf, int size, PRFileDesc *fd) 974 { 975 char *bp = buf; 976 int nb = 0; 977 ; 978 979 while (size > 1) { 980 nb = PR_Read(fd, bp, 1); 981 if (nb < 0) { 982 /* deal with error */ 983 return NULL; 984 } else if (nb == 0) { 985 /* deal with EOF */ 986 return NULL; 987 } else if (*bp == '\n') { 988 /* deal with EOL */ 989 ++bp; /* keep EOL character */ 990 break; 991 } else { 992 /* ordinary character */ 993 ++bp; 994 --size; 995 } 996 } 997 *bp = '\0'; 998 return buf; 999 } 1000 1001 typedef enum { UNKNOWN, 1002 DECODE, 1003 SIGN, 1004 ENCRYPT, 1005 ENVELOPE, 1006 CERTSONLY } Mode; 1007 1008 static int 1009 doBatchDecode(FILE *outFile, PRFileDesc *batchFile, 1010 const struct decodeOptionsStr *decodeOptions) 1011 { 1012 char *str; 1013 int exitStatus = 0; 1014 char batchLine[512]; 1015 1016 while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) { 1017 NSSCMSMessage *cmsg = NULL; 1018 PRFileDesc *inFile; 1019 int len = strlen(str); 1020 SECStatus rv; 1021 SECItem input = { 0, 0, 0 }; 1022 char cc; 1023 1024 while (len > 0 && 1025 ((cc = str[len - 1]) == '\n' || cc == '\r')) { 1026 str[--len] = '\0'; 1027 } 1028 if (!len) /* skip empty line */ 1029 continue; 1030 if (str[0] == '#') 1031 continue; /* skip comment line */ 1032 fprintf(outFile, "========== %s ==========\n", str); 1033 inFile = PR_Open(str, PR_RDONLY, 00660); 1034 if (inFile == NULL) { 1035 fprintf(outFile, "%s: unable to open \"%s\" for reading\n", 1036 progName, str); 1037 exitStatus = 1; 1038 continue; 1039 } 1040 rv = SECU_FileToItem(&input, inFile); 1041 PR_Close(inFile); 1042 if (rv != SECSuccess) { 1043 SECU_PrintError(progName, "unable to read infile"); 1044 exitStatus = 1; 1045 continue; 1046 } 1047 cmsg = decode(outFile, &input, decodeOptions); 1048 SECITEM_FreeItem(&input, PR_FALSE); 1049 if (cmsg) 1050 NSS_CMSMessage_Destroy(cmsg); 1051 else { 1052 SECU_PrintError(progName, "problem decoding"); 1053 exitStatus = 1; 1054 } 1055 } 1056 return exitStatus; 1057 } 1058 1059 /* legacy SHA2 table... 1060 * cmsutil took hash values of SHA256, SHA244, etc., the the 1061 * oid table has values of SHA-256, SHA-224. Use the follow 1062 * table to handle the old values. NOTE: no need to add new 1063 * hashes to this table, just use the actual oid table 1064 * values */ 1065 typedef struct LegacyHashNameStr { 1066 char *name; 1067 SECOidTag tag; 1068 } LegacyHashName; 1069 1070 LegacyHashName legacyHashNamesTable[] = { 1071 { "SHA1", SEC_OID_SHA1 }, 1072 { "SHA224", SEC_OID_SHA224 }, 1073 { "SHA256", SEC_OID_SHA256 }, 1074 { "SHA384", SEC_OID_SHA384 }, 1075 { "SHA512", SEC_OID_SHA512 }, 1076 }; 1077 size_t legacyHashNamesTableSize = PR_ARRAY_SIZE(legacyHashNamesTable); 1078 1079 SECOidTag 1080 CMSU_FindTagFromString(const char *cipherString) 1081 { 1082 SECOidTag tag; 1083 size_t slen; 1084 1085 /* future enhancement: accept dotted oid spec? */ 1086 slen = PORT_Strlen(cipherString); 1087 tag = SECOID_FindOIDTagFromDescripton(cipherString, slen, PR_TRUE); 1088 if (tag != SEC_OID_UNKNOWN) { 1089 return tag; 1090 } 1091 1092 if ((slen > 3) && (PORT_Strncasecmp(cipherString, "SHA", 3) == 0) && 1093 (cipherString[3] != '-')) { 1094 int i; 1095 for (i = 0; i < legacyHashNamesTableSize; i++) { 1096 if (PORT_Strcasecmp(legacyHashNamesTable[i].name, cipherString) == 0) { 1097 return legacyHashNamesTable[i].tag; 1098 } 1099 } 1100 /* not on any table, must be invalid */ 1101 } 1102 return SEC_OID_UNKNOWN; 1103 } 1104 1105 int 1106 main(int argc, char **argv) 1107 { 1108 FILE *outFile; 1109 NSSCMSMessage *cmsg = NULL; 1110 PRFileDesc *inFile; 1111 PLOptState *optstate; 1112 PLOptStatus status; 1113 Mode mode = UNKNOWN; 1114 struct decodeOptionsStr decodeOptions = { 0 }; 1115 struct signOptionsStr signOptions = { 0 }; 1116 struct envelopeOptionsStr envelopeOptions = { 0 }; 1117 struct certsonlyOptionsStr certsonlyOptions = { 0 }; 1118 struct encryptOptionsStr encryptOptions = { 0 }; 1119 struct optionsStr options = { 0 }; 1120 int exitstatus; 1121 static char *ptrarray[128] = { 0 }; 1122 int nrecipients = 0; 1123 char *str, *tok; 1124 char *envFileName; 1125 SECItem input = { 0, 0, 0 }; 1126 SECItem envmsg = { 0, 0, 0 }; 1127 SECStatus rv; 1128 PRFileDesc *contentFile = NULL; 1129 PRBool batch = PR_FALSE; 1130 1131 #ifdef NISCC_TEST 1132 const char *ev = PR_GetEnvSecure("NSS_DISABLE_ARENA_FREE_LIST"); 1133 PORT_Assert(ev); 1134 ev = PR_GetEnvSecure("NSS_STRICT_SHUTDOWN"); 1135 PORT_Assert(ev); 1136 #endif 1137 1138 SECOID_Init(); 1139 1140 progName = strrchr(argv[0], '/'); 1141 if (!progName) 1142 progName = strrchr(argv[0], '\\'); 1143 progName = progName ? progName + 1 : argv[0]; 1144 1145 inFile = PR_STDIN; 1146 outFile = stdout; 1147 envFileName = NULL; 1148 mode = UNKNOWN; 1149 decodeOptions.content.data = NULL; 1150 decodeOptions.content.len = 0; 1151 decodeOptions.suppressContent = PR_FALSE; 1152 decodeOptions.headerLevel = -1; 1153 decodeOptions.keepCerts = PR_FALSE; 1154 options.certUsage = certUsageEmailSigner; 1155 options.password = NULL; 1156 options.pwfile = NULL; 1157 signOptions.nickname = NULL; 1158 signOptions.detached = PR_FALSE; 1159 signOptions.signingTime = PR_FALSE; 1160 signOptions.smimeProfile = PR_FALSE; 1161 signOptions.encryptionKeyPreferenceNick = NULL; 1162 signOptions.hashAlgTag = SEC_OID_SHA256; 1163 envelopeOptions.recipients = NULL; 1164 encryptOptions.recipients = NULL; 1165 encryptOptions.envmsg = NULL; 1166 encryptOptions.envFile = NULL; 1167 encryptOptions.bulkalgtag = SEC_OID_UNKNOWN; 1168 encryptOptions.bulkkey = NULL; 1169 encryptOptions.keysize = -1; 1170 1171 /* 1172 * Parse command line arguments 1173 */ 1174 optstate = PL_CreateOptState(argc, argv, 1175 "CDEGH:N:OPSTY:bc:d:e:f:h:i:kno:p:r:s:u:v"); 1176 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { 1177 switch (optstate->option) { 1178 case 'C': 1179 mode = ENCRYPT; 1180 break; 1181 case 'D': 1182 mode = DECODE; 1183 break; 1184 case 'E': 1185 mode = ENVELOPE; 1186 break; 1187 case 'G': 1188 if (mode != SIGN) { 1189 fprintf(stderr, 1190 "%s: option -G only supported with option -S.\n", 1191 progName); 1192 Usage(); 1193 exit(1); 1194 } 1195 signOptions.signingTime = PR_TRUE; 1196 break; 1197 case 'H': 1198 if (mode != SIGN) { 1199 fprintf(stderr, 1200 "%s: option -H only supported with option -S.\n", 1201 progName); 1202 Usage(); 1203 exit(1); 1204 } 1205 decodeOptions.suppressContent = PR_TRUE; 1206 /* lookup hash value from our oid table and make sure it's a hash 1207 * using HASH_ functions */ 1208 signOptions.hashAlgTag = CMSU_FindTagFromString(optstate->value); 1209 if (HASH_GetHashTypeByOidTag(signOptions.hashAlgTag) == HASH_AlgNULL) { 1210 char *comma = ""; 1211 int i; 1212 /* it wasn't, use the HASH_ functions to find the valid values 1213 * and print it as an error */ 1214 fprintf(stderr, 1215 "%s: -H requires one of ", progName); 1216 for (i = HASH_AlgNULL + 1; PR_TRUE; i++) { 1217 SECOidTag hashTag = HASH_GetHashOidTagByHashType(i); 1218 if (hashTag == SEC_OID_UNKNOWN) { 1219 fprintf(stderr, "\n"); 1220 exit(1); 1221 } 1222 fprintf(stderr, "%s%s", comma, SECOID_FindOIDTagDescription(hashTag)); 1223 comma = ","; 1224 } 1225 /* NOT REACHED */ 1226 } 1227 break; 1228 case 'N': 1229 if (mode != SIGN) { 1230 fprintf(stderr, 1231 "%s: option -N only supported with option -S.\n", 1232 progName); 1233 Usage(); 1234 exit(1); 1235 } 1236 signOptions.nickname = PORT_Strdup(optstate->value); 1237 break; 1238 case 'O': 1239 mode = CERTSONLY; 1240 break; 1241 case 'P': 1242 if (mode != SIGN) { 1243 fprintf(stderr, 1244 "%s: option -P only supported with option -S.\n", 1245 progName); 1246 Usage(); 1247 exit(1); 1248 } 1249 signOptions.smimeProfile = PR_TRUE; 1250 break; 1251 case 'S': 1252 mode = SIGN; 1253 break; 1254 case 'T': 1255 if (mode != SIGN) { 1256 fprintf(stderr, 1257 "%s: option -T only supported with option -S.\n", 1258 progName); 1259 Usage(); 1260 exit(1); 1261 } 1262 signOptions.detached = PR_TRUE; 1263 break; 1264 case 'Y': 1265 if (mode != SIGN) { 1266 fprintf(stderr, 1267 "%s: option -Y only supported with option -S.\n", 1268 progName); 1269 Usage(); 1270 exit(1); 1271 } 1272 signOptions.encryptionKeyPreferenceNick = strdup(optstate->value); 1273 break; 1274 1275 case 'b': 1276 if (mode != DECODE) { 1277 fprintf(stderr, 1278 "%s: option -b only supported with option -D.\n", 1279 progName); 1280 Usage(); 1281 exit(1); 1282 } 1283 batch = PR_TRUE; 1284 break; 1285 1286 case 'c': 1287 if (mode != DECODE) { 1288 fprintf(stderr, 1289 "%s: option -c only supported with option -D.\n", 1290 progName); 1291 Usage(); 1292 exit(1); 1293 } 1294 contentFile = PR_Open(optstate->value, PR_RDONLY, 006600); 1295 if (contentFile == NULL) { 1296 fprintf(stderr, "%s: unable to open \"%s\" for reading.\n", 1297 progName, optstate->value); 1298 exit(1); 1299 } 1300 1301 rv = SECU_FileToItem(&decodeOptions.content, contentFile); 1302 PR_Close(contentFile); 1303 if (rv != SECSuccess) { 1304 SECU_PrintError(progName, "problem reading content file"); 1305 exit(1); 1306 } 1307 if (!decodeOptions.content.data) { 1308 /* file was zero length */ 1309 decodeOptions.content.data = (unsigned char *)PORT_Strdup(""); 1310 decodeOptions.content.len = 0; 1311 } 1312 1313 break; 1314 case 'd': 1315 SECU_ConfigDirectory(optstate->value); 1316 break; 1317 case 'e': 1318 envFileName = PORT_Strdup(optstate->value); 1319 encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660); 1320 break; 1321 1322 case 'h': 1323 if (mode != DECODE) { 1324 fprintf(stderr, 1325 "%s: option -h only supported with option -D.\n", 1326 progName); 1327 Usage(); 1328 exit(1); 1329 } 1330 decodeOptions.headerLevel = atoi(optstate->value); 1331 if (decodeOptions.headerLevel < 0) { 1332 fprintf(stderr, "option -h cannot have a negative value.\n"); 1333 exit(1); 1334 } 1335 break; 1336 case 'i': 1337 if (!optstate->value) { 1338 fprintf(stderr, "-i option requires filename argument\n"); 1339 exit(1); 1340 } 1341 inFile = PR_Open(optstate->value, PR_RDONLY, 00660); 1342 if (inFile == NULL) { 1343 fprintf(stderr, "%s: unable to open \"%s\" for reading\n", 1344 progName, optstate->value); 1345 exit(1); 1346 } 1347 break; 1348 1349 case 'k': 1350 if (mode != DECODE) { 1351 fprintf(stderr, 1352 "%s: option -k only supported with option -D.\n", 1353 progName); 1354 Usage(); 1355 exit(1); 1356 } 1357 decodeOptions.keepCerts = PR_TRUE; 1358 break; 1359 1360 case 'n': 1361 if (mode != DECODE) { 1362 fprintf(stderr, 1363 "%s: option -n only supported with option -D.\n", 1364 progName); 1365 Usage(); 1366 exit(1); 1367 } 1368 decodeOptions.suppressContent = PR_TRUE; 1369 break; 1370 case 'o': 1371 outFile = fopen(optstate->value, "wb"); 1372 if (outFile == NULL) { 1373 fprintf(stderr, "%s: unable to open \"%s\" for writing\n", 1374 progName, optstate->value); 1375 exit(1); 1376 } 1377 break; 1378 case 'p': 1379 if (!optstate->value) { 1380 fprintf(stderr, "%s: option -p must have a value.\n", progName); 1381 Usage(); 1382 exit(1); 1383 } 1384 1385 options.password = strdup(optstate->value); 1386 break; 1387 1388 case 'f': 1389 if (!optstate->value) { 1390 fprintf(stderr, "%s: option -f must have a value.\n", progName); 1391 Usage(); 1392 exit(1); 1393 } 1394 1395 options.pwfile = strdup(optstate->value); 1396 break; 1397 1398 case 'r': 1399 if (!optstate->value) { 1400 fprintf(stderr, "%s: option -r must have a value.\n", progName); 1401 Usage(); 1402 exit(1); 1403 } 1404 envelopeOptions.recipients = ptrarray; 1405 str = (char *)optstate->value; 1406 do { 1407 tok = strchr(str, ','); 1408 if (tok) 1409 *tok = '\0'; 1410 envelopeOptions.recipients[nrecipients++] = strdup(str); 1411 if (tok) 1412 str = tok + 1; 1413 } while (tok); 1414 envelopeOptions.recipients[nrecipients] = NULL; 1415 encryptOptions.recipients = envelopeOptions.recipients; 1416 certsonlyOptions.recipients = envelopeOptions.recipients; 1417 break; 1418 1419 case 'u': { 1420 int usageType; 1421 1422 usageType = atoi(strdup(optstate->value)); 1423 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) 1424 return -1; 1425 options.certUsage = (SECCertUsage)usageType; 1426 break; 1427 } 1428 case 'v': 1429 cms_verbose = 1; 1430 break; 1431 } 1432 } 1433 if (status == PL_OPT_BAD) 1434 Usage(); 1435 PL_DestroyOptState(optstate); 1436 1437 if (mode == UNKNOWN) 1438 Usage(); 1439 1440 if (mode != CERTSONLY && !batch) { 1441 rv = SECU_FileToItem(&input, inFile); 1442 if (rv != SECSuccess) { 1443 SECU_PrintError(progName, "unable to read infile"); 1444 exit(1); 1445 } 1446 } 1447 if (cms_verbose) { 1448 fprintf(stderr, "received commands\n"); 1449 } 1450 1451 /* Call the NSS initialization routines */ 1452 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 1453 rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL)); 1454 if (SECSuccess != rv) { 1455 SECU_PrintError(progName, "NSS_Init failed"); 1456 exit(1); 1457 } 1458 if (cms_verbose) { 1459 fprintf(stderr, "NSS has been initialized.\n"); 1460 } 1461 options.certHandle = CERT_GetDefaultCertDB(); 1462 if (!options.certHandle) { 1463 SECU_PrintError(progName, "No default cert DB"); 1464 exit(1); 1465 } 1466 if (cms_verbose) { 1467 fprintf(stderr, "Got default certdb\n"); 1468 } 1469 if (options.password) { 1470 pwdata.source = PW_PLAINTEXT; 1471 pwdata.data = options.password; 1472 } 1473 if (options.pwfile) { 1474 pwdata.source = PW_FROMFILE; 1475 pwdata.data = options.pwfile; 1476 } 1477 pwcb = SECU_GetModulePassword; 1478 pwcb_arg = (void *)&pwdata; 1479 1480 PK11_SetPasswordFunc(&SECU_GetModulePassword); 1481 1482 #if defined(_WIN32) 1483 if (outFile == stdout) { 1484 /* If we're going to write binary data to stdout, we must put stdout 1485 ** into O_BINARY mode or else outgoing \n's will become \r\n's. 1486 */ 1487 int smrv = _setmode(_fileno(stdout), _O_BINARY); 1488 if (smrv == -1) { 1489 fprintf(stderr, 1490 "%s: Cannot change stdout to binary mode. Use -o option instead.\n", 1491 progName); 1492 return smrv; 1493 } 1494 } 1495 #endif 1496 1497 exitstatus = 0; 1498 switch (mode) { 1499 case DECODE: /* -D */ 1500 decodeOptions.options = &options; 1501 if (encryptOptions.envFile) { 1502 /* Decoding encrypted-data, so get the bulkkey from an 1503 * enveloped-data message. 1504 */ 1505 SECU_FileToItem(&envmsg, encryptOptions.envFile); 1506 decodeOptions.options = &options; 1507 encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); 1508 if (!encryptOptions.envmsg) { 1509 SECU_PrintError(progName, "problem decoding env msg"); 1510 exitstatus = 1; 1511 break; 1512 } 1513 rv = get_enc_params(&encryptOptions); 1514 decodeOptions.dkcb = dkcb; 1515 decodeOptions.bulkkey = encryptOptions.bulkkey; 1516 } 1517 if (!batch) { 1518 cmsg = decode(outFile, &input, &decodeOptions); 1519 if (!cmsg) { 1520 SECU_PrintError(progName, "problem decoding"); 1521 exitstatus = 1; 1522 } 1523 } else { 1524 exitstatus = doBatchDecode(outFile, inFile, &decodeOptions); 1525 } 1526 break; 1527 case SIGN: /* -S */ 1528 signOptions.options = &options; 1529 cmsg = signed_data(&signOptions); 1530 if (!cmsg) { 1531 SECU_PrintError(progName, "problem signing"); 1532 exitstatus = 1; 1533 } 1534 break; 1535 case ENCRYPT: /* -C */ 1536 if (!envFileName) { 1537 fprintf(stderr, "%s: you must specify an envelope file with -e.\n", 1538 progName); 1539 exit(1); 1540 } 1541 encryptOptions.options = &options; 1542 encryptOptions.input = &input; 1543 encryptOptions.outfile = outFile; 1544 /* decode an enveloped-data message to get the bulkkey (create 1545 * a new one if neccessary) 1546 */ 1547 if (!encryptOptions.envFile) { 1548 encryptOptions.envFile = PR_Open(envFileName, 1549 PR_WRONLY | PR_CREATE_FILE, 00660); 1550 if (!encryptOptions.envFile) { 1551 fprintf(stderr, "%s: failed to create file %s.\n", progName, 1552 envFileName); 1553 exit(1); 1554 } 1555 } else { 1556 SECU_FileToItem(&envmsg, encryptOptions.envFile); 1557 decodeOptions.options = &options; 1558 encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); 1559 if (encryptOptions.envmsg == NULL) { 1560 SECU_PrintError(progName, "problem decrypting env msg"); 1561 exitstatus = 1; 1562 break; 1563 } 1564 } 1565 rv = get_enc_params(&encryptOptions); 1566 /* create the encrypted-data message */ 1567 cmsg = encrypted_data(&encryptOptions); 1568 if (!cmsg) { 1569 SECU_PrintError(progName, "problem encrypting"); 1570 exitstatus = 1; 1571 } 1572 if (encryptOptions.bulkkey) { 1573 PK11_FreeSymKey(encryptOptions.bulkkey); 1574 encryptOptions.bulkkey = NULL; 1575 } 1576 break; 1577 case ENVELOPE: /* -E */ 1578 envelopeOptions.options = &options; 1579 cmsg = enveloped_data(&envelopeOptions); 1580 if (!cmsg) { 1581 SECU_PrintError(progName, "problem enveloping"); 1582 exitstatus = 1; 1583 } 1584 break; 1585 case CERTSONLY: /* -O */ 1586 certsonlyOptions.options = &options; 1587 cmsg = signed_data_certsonly(&certsonlyOptions); 1588 if (!cmsg) { 1589 SECU_PrintError(progName, "problem with certs-only"); 1590 exitstatus = 1; 1591 } 1592 break; 1593 default: 1594 fprintf(stderr, "One of options -D, -S or -E must be set.\n"); 1595 Usage(); 1596 exitstatus = 1; 1597 } 1598 1599 if (signOptions.nickname) { 1600 PORT_Free(signOptions.nickname); 1601 } 1602 1603 if ((mode == SIGN || mode == ENVELOPE || mode == CERTSONLY) && 1604 (!exitstatus)) { 1605 PLArenaPool *arena = PORT_NewArena(1024); 1606 NSSCMSEncoderContext *ecx; 1607 SECItem output = { 0, 0, 0 }; 1608 1609 if (!arena) { 1610 fprintf(stderr, "%s: out of memory.\n", progName); 1611 exit(1); 1612 } 1613 1614 if (cms_verbose) { 1615 fprintf(stderr, "cmsg [%p]\n", cmsg); 1616 fprintf(stderr, "arena [%p]\n", arena); 1617 if (pwcb_arg && (PW_PLAINTEXT == ((secuPWData *)pwcb_arg)->source)) 1618 fprintf(stderr, "password [%s]\n", 1619 ((secuPWData *)pwcb_arg)->data); 1620 else 1621 fprintf(stderr, "password [NULL]\n"); 1622 } 1623 ecx = NSS_CMSEncoder_Start(cmsg, 1624 NULL, NULL, /* DER output callback */ 1625 &output, arena, /* destination storage */ 1626 pwcb, pwcb_arg, /* password callback */ 1627 NULL, NULL, /* decrypt key callback */ 1628 NULL, NULL); /* detached digests */ 1629 if (!ecx) { 1630 fprintf(stderr, "%s: cannot create encoder context.\n", progName); 1631 exitstatus = 1; 1632 goto loser; 1633 } 1634 if (cms_verbose) { 1635 fprintf(stderr, "input len [%d]\n", input.len); 1636 { 1637 unsigned int j; 1638 for (j = 0; j < input.len; j++) 1639 fprintf(stderr, "%2x%c", input.data[j], (j > 0 && j % 35 == 0) ? '\n' : ' '); 1640 } 1641 } 1642 if (input.len > 0) { /* skip if certs-only (or other zero content) */ 1643 rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len); 1644 if (rv) { 1645 fprintf(stderr, 1646 "%s: failed to add data to encoder.\n", progName); 1647 (void)NSS_CMSEncoder_Finish(ecx); 1648 PORT_FreeArena(arena, PR_FALSE); 1649 exitstatus = 1; 1650 goto loser; 1651 } 1652 } 1653 rv = NSS_CMSEncoder_Finish(ecx); 1654 if (rv) { 1655 SECU_PrintError(progName, "failed to encode data"); 1656 PORT_FreeArena(arena, PR_FALSE); 1657 exitstatus = 1; 1658 goto loser; 1659 } 1660 1661 if (cms_verbose) { 1662 fprintf(stderr, "encoding passed\n"); 1663 } 1664 fwrite(output.data, output.len, 1, outFile); 1665 if (cms_verbose) { 1666 fprintf(stderr, "wrote to file\n"); 1667 } 1668 PORT_FreeArena(arena, PR_FALSE); 1669 } 1670 loser: 1671 if (cmsg) 1672 NSS_CMSMessage_Destroy(cmsg); 1673 if (outFile != stdout) 1674 fclose(outFile); 1675 1676 if (inFile != PR_STDIN) { 1677 PR_Close(inFile); 1678 } 1679 if (envFileName) { 1680 PORT_Free(envFileName); 1681 } 1682 if (encryptOptions.envFile) { 1683 PR_Close(encryptOptions.envFile); 1684 } 1685 1686 SECITEM_FreeItem(&decodeOptions.content, PR_FALSE); 1687 SECITEM_FreeItem(&envmsg, PR_FALSE); 1688 SECITEM_FreeItem(&input, PR_FALSE); 1689 if (NSS_Shutdown() != SECSuccess) { 1690 SECU_PrintError(progName, "NSS_Shutdown failed"); 1691 exitstatus = 1; 1692 } 1693 PR_Cleanup(); 1694 return exitstatus; 1695 }