security/nss/cmd/smimetools/cmsutil.c

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial