PageRenderTime 130ms CodeModel.GetById 14ms app.highlight 104ms RepoModel.GetById 1ms app.codeStats 0ms

/security/nss/cmd/smimetools/cmsutil.c

http://github.com/zpao/v8monkey
C | 1651 lines | 1418 code | 87 blank | 146 comment | 355 complexity | 55fa3eda8ee5bedcc15263cdb96d07a8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file