nss: import at 3.0.1 beta 1
[mozilla-nss.git] / security / nss / cmd / smimetools / cmsutil.c
blob24ce08de08537a347aa5c5f105c4e20809c981b5
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
14 * The Original Code is the Netscape security libraries.
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.
21 * Contributor(s):
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.
35 * ***** END LICENSE BLOCK ***** */
38 * cmsutil -- A command to work with CMS data
40 * $Id: cmsutil.c,v 1.53 2006/08/05 01:19:23 julien.pierre.bugs%sun.com Exp $
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"
55 #if defined(XP_UNIX)
56 #include <unistd.h>
57 #endif
59 #if defined(_WIN32)
60 #include "fcntl.h"
61 #include "io.h"
62 #endif
64 #include <stdio.h>
65 #include <string.h>
67 char *progName = NULL;
68 static int cms_verbose = 0;
69 static secuPWData pwdata = { PW_NONE, 0 };
70 static PK11PasswordFunc pwcb = NULL;
71 static void *pwcb_arg = NULL;
74 /* XXX stolen from cmsarray.c
75 * nss_CMSArray_Count - count number of elements in array
77 int
78 nss_CMSArray_Count(void **array)
80 int n = 0;
81 if (array == NULL)
82 return 0;
83 while (*array++ != NULL)
84 n++;
85 return n;
88 static SECStatus
89 DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
90 SECAlgorithmID **algids)
92 NSSCMSDigestContext *digcx;
93 SECStatus rv;
95 digcx = NSS_CMSDigestContext_StartMultiple(algids);
96 if (digcx == NULL)
97 return SECFailure;
99 NSS_CMSDigestContext_Update(digcx, input->data, input->len);
101 rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
102 return rv;
106 static void
107 Usage(char *progName)
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 " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n"
137 " -v print debugging information\n"
138 "\n"
139 "Cert usage codes:\n",
140 progName);
141 fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " ");
142 fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " ");
143 fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " ");
144 fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " ");
145 fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " ");
146 fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " ");
147 fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " ");
148 fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " ");
149 fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " ");
150 fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " ");
151 fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
152 fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
154 exit(-1);
157 struct optionsStr {
158 char *password;
159 SECCertUsage certUsage;
160 CERTCertDBHandle *certHandle;
163 struct decodeOptionsStr {
164 struct optionsStr *options;
165 SECItem content;
166 int headerLevel;
167 PRBool suppressContent;
168 NSSCMSGetDecryptKeyCallback dkcb;
169 PK11SymKey *bulkkey;
170 PRBool keepCerts;
173 struct signOptionsStr {
174 struct optionsStr *options;
175 char *nickname;
176 char *encryptionKeyPreferenceNick;
177 PRBool signingTime;
178 PRBool smimeProfile;
179 PRBool detached;
180 SECOidTag hashAlgTag;
183 struct envelopeOptionsStr {
184 struct optionsStr *options;
185 char **recipients;
188 struct certsonlyOptionsStr {
189 struct optionsStr *options;
190 char **recipients;
193 struct encryptOptionsStr {
194 struct optionsStr *options;
195 char **recipients;
196 NSSCMSMessage *envmsg;
197 SECItem *input;
198 FILE *outfile;
199 PRFileDesc *envFile;
200 PK11SymKey *bulkkey;
201 SECOidTag bulkalgtag;
202 int keysize;
205 static NSSCMSMessage *
206 decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions)
208 NSSCMSDecoderContext *dcx;
209 SECStatus rv;
210 NSSCMSMessage *cmsg;
211 int nlevels, i;
212 SECItem sitem = { 0, 0, 0 };
214 PORT_SetError(0);
215 dcx = NSS_CMSDecoder_Start(NULL,
216 NULL, NULL, /* content callback */
217 pwcb, pwcb_arg, /* password callback */
218 decodeOptions->dkcb, /* decrypt key callback */
219 decodeOptions->bulkkey);
220 if (dcx == NULL) {
221 fprintf(stderr, "%s: failed to set up message decoder.\n", progName);
222 return NULL;
224 rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
225 if (rv != SECSuccess) {
226 fprintf(stderr, "%s: failed to decode message.\n", progName);
227 NSS_CMSDecoder_Cancel(dcx);
228 return NULL;
230 cmsg = NSS_CMSDecoder_Finish(dcx);
231 if (cmsg == NULL) {
232 fprintf(stderr, "%s: failed to decode message.\n", progName);
233 return NULL;
236 if (decodeOptions->headerLevel >= 0) {
237 /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
238 fprintf(out, "SMIME: ");
241 nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
242 for (i = 0; i < nlevels; i++) {
243 NSSCMSContentInfo *cinfo;
244 SECOidTag typetag;
246 cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
247 typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
249 if (decodeOptions->headerLevel >= 0)
250 fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
252 switch (typetag) {
253 case SEC_OID_PKCS7_SIGNED_DATA:
255 NSSCMSSignedData *sigd = NULL;
256 SECItem **digests;
257 int nsigners;
258 int j;
260 if (decodeOptions->headerLevel >= 0)
261 fprintf(out, "type=signedData; ");
262 sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
263 if (sigd == NULL) {
264 SECU_PrintError(progName, "signedData component missing");
265 goto loser;
268 /* if we have a content file, but no digests for this signedData */
269 if (decodeOptions->content.data != NULL &&
270 !NSS_CMSSignedData_HasDigests(sigd)) {
271 PLArenaPool *poolp;
272 SECAlgorithmID **digestalgs;
274 /* detached content: grab content file */
275 sitem = decodeOptions->content;
277 if ((poolp = PORT_NewArena(1024)) == NULL) {
278 fprintf(stderr, "cmsutil: Out of memory.\n");
279 goto loser;
281 digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
282 if (DigestFile (poolp, &digests, &sitem, digestalgs)
283 != SECSuccess) {
284 SECU_PrintError(progName,
285 "problem computing message digest");
286 PORT_FreeArena(poolp, PR_FALSE);
287 goto loser;
289 if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests)
290 != SECSuccess) {
291 SECU_PrintError(progName,
292 "problem setting message digests");
293 PORT_FreeArena(poolp, PR_FALSE);
294 goto loser;
296 PORT_FreeArena(poolp, PR_FALSE);
299 /* import the certificates */
300 if (NSS_CMSSignedData_ImportCerts(sigd,
301 decodeOptions->options->certHandle,
302 decodeOptions->options->certUsage,
303 decodeOptions->keepCerts)
304 != SECSuccess) {
305 SECU_PrintError(progName, "cert import failed");
306 goto loser;
309 /* find out about signers */
310 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
311 if (decodeOptions->headerLevel >= 0)
312 fprintf(out, "nsigners=%d; ", nsigners);
313 if (nsigners == 0) {
314 /* Might be a cert transport message
315 ** or might be an invalid message, such as a QA test message
316 ** or a message from an attacker.
318 SECStatus rv;
319 rv = NSS_CMSSignedData_VerifyCertsOnly(sigd,
320 decodeOptions->options->certHandle,
321 decodeOptions->options->certUsage);
322 if (rv != SECSuccess) {
323 fprintf(stderr, "cmsutil: Verify certs-only failed!\n");
324 goto loser;
326 return cmsg;
329 /* still no digests? */
330 if (!NSS_CMSSignedData_HasDigests(sigd)) {
331 SECU_PrintError(progName, "no message digests");
332 goto loser;
335 for (j = 0; j < nsigners; j++) {
336 const char * svs;
337 NSSCMSSignerInfo *si;
338 NSSCMSVerificationStatus vs;
339 SECStatus bad;
341 si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
342 if (decodeOptions->headerLevel >= 0) {
343 char *signercn;
344 static char empty[] = { "" };
346 signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
347 if (signercn == NULL)
348 signercn = empty;
349 fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
350 if (signercn != empty)
351 PORT_Free(signercn);
353 bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j,
354 decodeOptions->options->certHandle,
355 decodeOptions->options->certUsage);
356 vs = NSS_CMSSignerInfo_GetVerificationStatus(si);
357 svs = NSS_CMSUtil_VerificationStatusToString(vs);
358 if (decodeOptions->headerLevel >= 0) {
359 fprintf(out, "signer%d.status=%s; ", j, svs);
360 /* goto loser ? */
361 } else if (bad && out) {
362 fprintf(stderr, "signer %d status = %s\n", j, svs);
363 goto loser;
367 break;
368 case SEC_OID_PKCS7_ENVELOPED_DATA:
370 NSSCMSEnvelopedData *envd;
371 if (decodeOptions->headerLevel >= 0)
372 fprintf(out, "type=envelopedData; ");
373 envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
374 if (envd == NULL) {
375 SECU_PrintError(progName, "envelopedData component missing");
376 goto loser;
379 break;
380 case SEC_OID_PKCS7_ENCRYPTED_DATA:
382 NSSCMSEncryptedData *encd;
383 if (decodeOptions->headerLevel >= 0)
384 fprintf(out, "type=encryptedData; ");
385 encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
386 if (encd == NULL) {
387 SECU_PrintError(progName, "encryptedData component missing");
388 goto loser;
391 break;
392 case SEC_OID_PKCS7_DATA:
393 if (decodeOptions->headerLevel >= 0)
394 fprintf(out, "type=data; ");
395 break;
396 default:
397 break;
399 if (decodeOptions->headerLevel >= 0)
400 fprintf(out, "\n");
403 if (!decodeOptions->suppressContent && out) {
404 SECItem *item = (sitem.data ? &sitem
405 : NSS_CMSMessage_GetContent(cmsg));
406 if (item && item->data && item->len) {
407 fwrite(item->data, item->len, 1, out);
410 return cmsg;
412 loser:
413 if (cmsg)
414 NSS_CMSMessage_Destroy(cmsg);
415 return NULL;
418 /* example of a callback function to use with encoder */
420 static void
421 writeout(void *arg, const char *buf, unsigned long len)
423 FILE *f = (FILE *)arg;
425 if (f != NULL && buf != NULL)
426 (void)fwrite(buf, len, 1, f);
430 static NSSCMSMessage *
431 signed_data(struct signOptionsStr *signOptions)
433 NSSCMSMessage *cmsg = NULL;
434 NSSCMSContentInfo *cinfo;
435 NSSCMSSignedData *sigd;
436 NSSCMSSignerInfo *signerinfo;
437 CERTCertificate *cert= NULL, *ekpcert = NULL;
439 if (cms_verbose) {
440 fprintf(stderr, "Input to signed_data:\n");
441 if (signOptions->options->password)
442 fprintf(stderr, "password [%s]\n", signOptions->options->password);
443 else
444 fprintf(stderr, "password [NULL]\n");
445 fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage);
446 if (signOptions->options->certHandle)
447 fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle);
448 else
449 fprintf(stderr, "certdb [NULL]\n");
450 if (signOptions->nickname)
451 fprintf(stderr, "nickname [%s]\n", signOptions->nickname);
452 else
453 fprintf(stderr, "nickname [NULL]\n");
455 if (signOptions->nickname == NULL) {
456 fprintf(stderr,
457 "ERROR: please indicate the nickname of a certificate to sign with.\n");
458 return NULL;
460 if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle,
461 signOptions->nickname,
462 signOptions->options->certUsage,
463 PR_FALSE,
464 &pwdata)) == NULL) {
465 SECU_PrintError(progName,
466 "the corresponding cert for key \"%s\" does not exist",
467 signOptions->nickname);
468 return NULL;
470 if (cms_verbose) {
471 fprintf(stderr, "Found certificate for %s\n", signOptions->nickname);
474 * create the message object
476 cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
477 if (cmsg == NULL) {
478 fprintf(stderr, "ERROR: cannot create CMS message.\n");
479 return NULL;
482 * build chain of objects: message->signedData->data
484 if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
485 fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
486 goto loser;
488 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
489 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
490 != SECSuccess) {
491 fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
492 goto loser;
494 cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
495 /* we're always passing data in and detaching optionally */
496 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL,
497 signOptions->detached)
498 != SECSuccess) {
499 fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
500 goto loser;
503 * create & attach signer information
505 signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag);
506 if (signerinfo == NULL) {
507 fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
508 goto loser;
510 if (cms_verbose) {
511 fprintf(stderr,
512 "Created CMS message, added signed data w/ signerinfo\n");
514 /* we want the cert chain included for this one */
515 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain,
516 signOptions->options->certUsage)
517 != SECSuccess) {
518 fprintf(stderr, "ERROR: cannot find cert chain.\n");
519 goto loser;
521 if (cms_verbose) {
522 fprintf(stderr, "imported certificate\n");
524 if (signOptions->signingTime) {
525 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now())
526 != SECSuccess) {
527 fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
528 goto loser;
531 if (signOptions->smimeProfile) {
532 if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
533 fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
534 goto loser;
538 if (!signOptions->encryptionKeyPreferenceNick) {
539 /* check signing cert for fitness as encryption cert */
540 SECStatus FitForEncrypt = CERT_CheckCertUsage(cert,
541 certUsageEmailRecipient);
543 if (SECSuccess == FitForEncrypt) {
544 /* if yes, add signing cert as EncryptionKeyPreference */
545 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert,
546 signOptions->options->certHandle)
547 != SECSuccess) {
548 fprintf(stderr,
549 "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
550 goto loser;
552 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert,
553 signOptions->options->certHandle)
554 != SECSuccess) {
555 fprintf(stderr,
556 "ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n");
557 goto loser;
559 } else {
560 /* this is a dual-key cert case, we need to look for the encryption
561 certificate under the same nickname as the signing cert */
562 /* get the cert, add it to the message */
563 if ((ekpcert = CERT_FindUserCertByUsage(
564 signOptions->options->certHandle,
565 signOptions->nickname,
566 certUsageEmailRecipient,
567 PR_FALSE,
568 &pwdata)) == NULL) {
569 SECU_PrintError(progName,
570 "the corresponding cert for key \"%s\" does not exist",
571 signOptions->encryptionKeyPreferenceNick);
572 goto loser;
574 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
575 signOptions->options->certHandle)
576 != SECSuccess) {
577 fprintf(stderr,
578 "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
579 goto loser;
581 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,
582 signOptions->options->certHandle)
583 != SECSuccess) {
584 fprintf(stderr,
585 "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
586 goto loser;
588 if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
589 fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
590 goto loser;
593 } else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) {
594 /* No action */
595 } else {
596 /* get the cert, add it to the message */
597 if ((ekpcert = CERT_FindUserCertByUsage(
598 signOptions->options->certHandle,
599 signOptions->encryptionKeyPreferenceNick,
600 certUsageEmailRecipient, PR_FALSE, &pwdata))
601 == NULL) {
602 SECU_PrintError(progName,
603 "the corresponding cert for key \"%s\" does not exist",
604 signOptions->encryptionKeyPreferenceNick);
605 goto loser;
607 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert,
608 signOptions->options->certHandle)
609 != SECSuccess) {
610 fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
611 goto loser;
613 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert,
614 signOptions->options->certHandle)
615 != SECSuccess) {
616 fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
617 goto loser;
619 if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
620 fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
621 goto loser;
625 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
626 fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
627 goto loser;
629 if (cms_verbose) {
630 fprintf(stderr, "created signed-data message\n");
632 if (ekpcert) {
633 CERT_DestroyCertificate(ekpcert);
635 if (cert) {
636 CERT_DestroyCertificate(cert);
638 return cmsg;
639 loser:
640 if (ekpcert) {
641 CERT_DestroyCertificate(ekpcert);
643 if (cert) {
644 CERT_DestroyCertificate(cert);
646 NSS_CMSMessage_Destroy(cmsg);
647 return NULL;
650 static NSSCMSMessage *
651 enveloped_data(struct envelopeOptionsStr *envelopeOptions)
653 NSSCMSMessage *cmsg = NULL;
654 NSSCMSContentInfo *cinfo;
655 NSSCMSEnvelopedData *envd;
656 NSSCMSRecipientInfo *recipientinfo;
657 CERTCertificate **recipientcerts = NULL;
658 CERTCertDBHandle *dbhandle;
659 PLArenaPool *tmppoolp = NULL;
660 SECOidTag bulkalgtag;
661 int keysize, i = 0;
662 int cnt;
663 dbhandle = envelopeOptions->options->certHandle;
664 /* count the recipients */
665 if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) {
666 fprintf(stderr, "ERROR: please name at least one recipient.\n");
667 goto loser;
669 if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
670 fprintf(stderr, "ERROR: out of memory.\n");
671 goto loser;
673 /* XXX find the recipient's certs by email address or nickname */
674 if ((recipientcerts =
675 (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp,
676 (cnt+1)*sizeof(CERTCertificate*)))
677 == NULL) {
678 fprintf(stderr, "ERROR: out of memory.\n");
679 goto loser;
681 for (i=0; envelopeOptions->recipients[i] != NULL; i++) {
682 if ((recipientcerts[i] =
683 CERT_FindCertByNicknameOrEmailAddr(dbhandle,
684 envelopeOptions->recipients[i]))
685 == NULL) {
686 SECU_PrintError(progName, "cannot find certificate for \"%s\"",
687 envelopeOptions->recipients[i]);
688 i=0;
689 goto loser;
692 recipientcerts[i] = NULL;
693 i=0;
694 /* find a nice bulk algorithm */
695 if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag,
696 &keysize) != SECSuccess) {
697 fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
698 goto loser;
701 * create the message object
703 cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
704 if (cmsg == NULL) {
705 fprintf(stderr, "ERROR: cannot create CMS message.\n");
706 goto loser;
709 * build chain of objects: message->envelopedData->data
711 if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize))
712 == NULL) {
713 fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
714 goto loser;
716 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
717 if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd)
718 != SECSuccess) {
719 fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
720 goto loser;
722 cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
723 /* we're always passing data in, so the content is NULL */
724 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
725 != SECSuccess) {
726 fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
727 goto loser;
730 * create & attach recipient information
732 for (i = 0; recipientcerts[i] != NULL; i++) {
733 if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg,
734 recipientcerts[i]))
735 == NULL) {
736 fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
737 goto loser;
739 if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo)
740 != SECSuccess) {
741 fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
742 goto loser;
744 CERT_DestroyCertificate(recipientcerts[i]);
746 if (tmppoolp)
747 PORT_FreeArena(tmppoolp, PR_FALSE);
748 return cmsg;
749 loser:
750 if (recipientcerts) {
751 for (; recipientcerts[i] != NULL; i++) {
752 CERT_DestroyCertificate(recipientcerts[i]);
755 if (cmsg)
756 NSS_CMSMessage_Destroy(cmsg);
757 if (tmppoolp)
758 PORT_FreeArena(tmppoolp, PR_FALSE);
759 return NULL;
762 PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid)
764 return (PK11SymKey*)arg;
767 static SECStatus
768 get_enc_params(struct encryptOptionsStr *encryptOptions)
770 struct envelopeOptionsStr envelopeOptions;
771 SECStatus rv = SECFailure;
772 NSSCMSMessage *env_cmsg;
773 NSSCMSContentInfo *cinfo;
774 int i, nlevels;
776 * construct an enveloped data message to obtain bulk keys
778 if (encryptOptions->envmsg) {
779 env_cmsg = encryptOptions->envmsg; /* get it from an old message */
780 } else {
781 SECItem dummyOut = { 0, 0, 0 };
782 SECItem dummyIn = { 0, 0, 0 };
783 char str[] = "Hello!";
784 PLArenaPool *tmparena = PORT_NewArena(1024);
785 dummyIn.data = (unsigned char *)str;
786 dummyIn.len = strlen(str);
787 envelopeOptions.options = encryptOptions->options;
788 envelopeOptions.recipients = encryptOptions->recipients;
789 env_cmsg = enveloped_data(&envelopeOptions);
790 NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena);
791 PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len);
792 PORT_FreeArena(tmparena, PR_FALSE);
795 * get the content info for the enveloped data
797 nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg);
798 for (i = 0; i < nlevels; i++) {
799 SECOidTag typetag;
800 cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i);
801 typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
802 if (typetag == SEC_OID_PKCS7_DATA) {
804 * get the symmetric key
806 encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
807 encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo);
808 encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
809 rv = SECSuccess;
810 break;
813 if (i == nlevels) {
814 fprintf(stderr, "%s: could not retrieve enveloped data.", progName);
816 if (env_cmsg)
817 NSS_CMSMessage_Destroy(env_cmsg);
818 return rv;
821 static NSSCMSMessage *
822 encrypted_data(struct encryptOptionsStr *encryptOptions)
824 SECStatus rv = SECFailure;
825 NSSCMSMessage *cmsg = NULL;
826 NSSCMSContentInfo *cinfo;
827 NSSCMSEncryptedData *encd;
828 NSSCMSEncoderContext *ecx = NULL;
829 PLArenaPool *tmppoolp = NULL;
830 SECItem derOut = { 0, 0, 0 };
831 /* arena for output */
832 tmppoolp = PORT_NewArena(1024);
833 if (!tmppoolp) {
834 fprintf(stderr, "%s: out of memory.\n", progName);
835 return NULL;
838 * create the message object
840 cmsg = NSS_CMSMessage_Create(NULL);
841 if (cmsg == NULL) {
842 fprintf(stderr, "ERROR: cannot create CMS message.\n");
843 goto loser;
846 * build chain of objects: message->encryptedData->data
848 if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag,
849 encryptOptions->keysize))
850 == NULL) {
851 fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n");
852 goto loser;
854 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
855 if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd)
856 != SECSuccess) {
857 fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n");
858 goto loser;
860 cinfo = NSS_CMSEncryptedData_GetContentInfo(encd);
861 /* we're always passing data in, so the content is NULL */
862 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
863 != SECSuccess) {
864 fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
865 goto loser;
867 ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
868 dkcb, encryptOptions->bulkkey, NULL, NULL);
869 if (!ecx) {
870 fprintf(stderr, "%s: cannot create encoder context.\n", progName);
871 goto loser;
873 rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data,
874 encryptOptions->input->len);
875 if (rv) {
876 fprintf(stderr, "%s: failed to add data to encoder.\n", progName);
877 goto loser;
879 rv = NSS_CMSEncoder_Finish(ecx);
880 if (rv) {
881 fprintf(stderr, "%s: failed to encrypt data.\n", progName);
882 goto loser;
884 fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile);
886 if (bulkkey)
887 PK11_FreeSymKey(bulkkey);
889 if (tmppoolp)
890 PORT_FreeArena(tmppoolp, PR_FALSE);
891 return cmsg;
892 loser:
894 if (bulkkey)
895 PK11_FreeSymKey(bulkkey);
897 if (tmppoolp)
898 PORT_FreeArena(tmppoolp, PR_FALSE);
899 if (cmsg)
900 NSS_CMSMessage_Destroy(cmsg);
901 return NULL;
904 static NSSCMSMessage *
905 signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
907 NSSCMSMessage *cmsg = NULL;
908 NSSCMSContentInfo *cinfo;
909 NSSCMSSignedData *sigd;
910 CERTCertificate **certs = NULL;
911 CERTCertDBHandle *dbhandle;
912 PLArenaPool *tmppoolp = NULL;
913 int i = 0, cnt;
914 dbhandle = certsonlyOptions->options->certHandle;
915 if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) {
916 fprintf(stderr,
917 "ERROR: please indicate the nickname of a certificate to sign with.\n");
918 goto loser;
920 if (!(tmppoolp = PORT_NewArena(1024))) {
921 fprintf(stderr, "ERROR: out of memory.\n");
922 goto loser;
924 if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) {
925 fprintf(stderr, "ERROR: out of memory.\n");
926 goto loser;
928 for (i=0; certsonlyOptions->recipients[i] != NULL; i++) {
929 if ((certs[i] =
930 CERT_FindCertByNicknameOrEmailAddr(dbhandle,
931 certsonlyOptions->recipients[i]))
932 == NULL) {
933 SECU_PrintError(progName, "cannot find certificate for \"%s\"",
934 certsonlyOptions->recipients[i]);
935 i=0;
936 goto loser;
939 certs[i] = NULL;
940 i=0;
942 * create the message object
944 cmsg = NSS_CMSMessage_Create(NULL);
945 if (cmsg == NULL) {
946 fprintf(stderr, "ERROR: cannot create CMS message.\n");
947 goto loser;
950 * build chain of objects: message->signedData->data
952 if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE))
953 == NULL) {
954 fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
955 goto loser;
957 CERT_DestroyCertificate(certs[0]);
958 for (i=1; i<cnt; i++) {
959 if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) {
960 fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n",
961 certsonlyOptions->recipients[i]);
962 goto loser;
964 CERT_DestroyCertificate(certs[i]);
966 cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
967 if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
968 != SECSuccess) {
969 fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
970 goto loser;
972 cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
973 if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE)
974 != SECSuccess) {
975 fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
976 goto loser;
978 if (tmppoolp)
979 PORT_FreeArena(tmppoolp, PR_FALSE);
980 return cmsg;
981 loser:
982 if (certs) {
983 for (; i<cnt; i++) {
984 CERT_DestroyCertificate(certs[i]);
987 if (cmsg)
988 NSS_CMSMessage_Destroy(cmsg);
989 if (tmppoolp)
990 PORT_FreeArena(tmppoolp, PR_FALSE);
991 return NULL;
994 static char *
995 pl_fgets(char * buf, int size, PRFileDesc * fd)
997 char * bp = buf;
998 int nb = 0;;
1000 while (size > 1) {
1001 nb = PR_Read(fd, bp, 1);
1002 if (nb < 0) {
1003 /* deal with error */
1004 return NULL;
1005 } else if (nb == 0) {
1006 /* deal with EOF */
1007 return NULL;
1008 } else if (*bp == '\n') {
1009 /* deal with EOL */
1010 ++bp; /* keep EOL character */
1011 break;
1012 } else {
1013 /* ordinary character */
1014 ++bp;
1015 --size;
1018 *bp = '\0';
1019 return buf;
1022 typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
1024 static int
1025 doBatchDecode(FILE *outFile, PRFileDesc *batchFile,
1026 const struct decodeOptionsStr *decodeOptions)
1028 char * str;
1029 int exitStatus = 0;
1030 char batchLine[512];
1032 while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) {
1033 NSSCMSMessage *cmsg = NULL;
1034 PRFileDesc * inFile;
1035 int len = strlen(str);
1036 SECStatus rv;
1037 SECItem input = {0, 0, 0};
1038 char cc;
1040 while (len > 0 &&
1041 ((cc = str[len - 1]) == '\n' || cc == '\r')) {
1042 str[--len] = '\0';
1044 if (!len) /* skip empty line */
1045 continue;
1046 if (str[0] == '#')
1047 continue; /* skip comment line */
1048 fprintf(outFile, "========== %s ==========\n", str);
1049 inFile = PR_Open(str, PR_RDONLY, 00660);
1050 if (inFile == NULL) {
1051 fprintf(outFile, "%s: unable to open \"%s\" for reading\n",
1052 progName, str);
1053 exitStatus = 1;
1054 continue;
1056 rv = SECU_FileToItem(&input, inFile);
1057 PR_Close(inFile);
1058 if (rv != SECSuccess) {
1059 SECU_PrintError(progName, "unable to read infile");
1060 exitStatus = 1;
1061 continue;
1063 cmsg = decode(outFile, &input, decodeOptions);
1064 SECITEM_FreeItem(&input, PR_FALSE);
1065 if (cmsg)
1066 NSS_CMSMessage_Destroy(cmsg);
1067 else {
1068 SECU_PrintError(progName, "problem decoding");
1069 exitStatus = 1;
1072 return exitStatus;
1076 main(int argc, char **argv)
1078 FILE *outFile;
1079 NSSCMSMessage *cmsg = NULL;
1080 PRFileDesc *inFile;
1081 PLOptState *optstate;
1082 PLOptStatus status;
1083 Mode mode = UNKNOWN;
1084 struct decodeOptionsStr decodeOptions = { 0 };
1085 struct signOptionsStr signOptions = { 0 };
1086 struct envelopeOptionsStr envelopeOptions = { 0 };
1087 struct certsonlyOptionsStr certsonlyOptions = { 0 };
1088 struct encryptOptionsStr encryptOptions = { 0 };
1089 struct optionsStr options = { 0 };
1090 int exitstatus;
1091 static char *ptrarray[128] = { 0 };
1092 int nrecipients = 0;
1093 char *str, *tok;
1094 char *envFileName;
1095 SECItem input = { 0, 0, 0};
1096 SECItem envmsg = { 0, 0, 0 };
1097 SECStatus rv;
1098 PRFileDesc *contentFile = NULL;
1099 PRBool batch = PR_FALSE;
1101 #ifdef NISCC_TEST
1102 const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST");
1103 PORT_Assert(ev);
1104 ev = PR_GetEnv("NSS_STRICT_SHUTDOWN");
1105 PORT_Assert(ev);
1106 #endif
1108 progName = strrchr(argv[0], '/');
1109 if (!progName)
1110 progName = strrchr(argv[0], '\\');
1111 progName = progName ? progName+1 : argv[0];
1113 inFile = PR_STDIN;
1114 outFile = stdout;
1115 envFileName = NULL;
1116 mode = UNKNOWN;
1117 decodeOptions.content.data = NULL;
1118 decodeOptions.content.len = 0;
1119 decodeOptions.suppressContent = PR_FALSE;
1120 decodeOptions.headerLevel = -1;
1121 decodeOptions.keepCerts = PR_FALSE;
1122 options.certUsage = certUsageEmailSigner;
1123 options.password = NULL;
1124 signOptions.nickname = NULL;
1125 signOptions.detached = PR_FALSE;
1126 signOptions.signingTime = PR_FALSE;
1127 signOptions.smimeProfile = PR_FALSE;
1128 signOptions.encryptionKeyPreferenceNick = NULL;
1129 signOptions.hashAlgTag = SEC_OID_SHA1;
1130 envelopeOptions.recipients = NULL;
1131 encryptOptions.recipients = NULL;
1132 encryptOptions.envmsg = NULL;
1133 encryptOptions.envFile = NULL;
1134 encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
1135 encryptOptions.bulkkey = NULL;
1136 encryptOptions.keysize = -1;
1139 * Parse command line arguments
1141 optstate = PL_CreateOptState(argc, argv,
1142 "CDEGH:N:OPSTY:bc:d:e:h:i:kno:p:r:s:u:v");
1143 while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
1144 switch (optstate->option) {
1145 case 'C':
1146 mode = ENCRYPT;
1147 break;
1148 case 'D':
1149 mode = DECODE;
1150 break;
1151 case 'E':
1152 mode = ENVELOPE;
1153 break;
1154 case 'G':
1155 if (mode != SIGN) {
1156 fprintf(stderr,
1157 "%s: option -G only supported with option -S.\n",
1158 progName);
1159 Usage(progName);
1160 exit(1);
1162 signOptions.signingTime = PR_TRUE;
1163 break;
1164 case 'H':
1165 if (mode != SIGN) {
1166 fprintf(stderr,
1167 "%s: option -H only supported with option -S.\n",
1168 progName);
1169 Usage(progName);
1170 exit(1);
1172 decodeOptions.suppressContent = PR_TRUE;
1173 if (!strcmp(optstate->value, "MD2"))
1174 signOptions.hashAlgTag = SEC_OID_MD2;
1175 else if (!strcmp(optstate->value, "MD4"))
1176 signOptions.hashAlgTag = SEC_OID_MD4;
1177 else if (!strcmp(optstate->value, "MD5"))
1178 signOptions.hashAlgTag = SEC_OID_MD5;
1179 else if (!strcmp(optstate->value, "SHA1"))
1180 signOptions.hashAlgTag = SEC_OID_SHA1;
1181 else if (!strcmp(optstate->value, "SHA256"))
1182 signOptions.hashAlgTag = SEC_OID_SHA256;
1183 else if (!strcmp(optstate->value, "SHA384"))
1184 signOptions.hashAlgTag = SEC_OID_SHA384;
1185 else if (!strcmp(optstate->value, "SHA512"))
1186 signOptions.hashAlgTag = SEC_OID_SHA512;
1187 else {
1188 fprintf(stderr,
1189 "%s: -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512\n",
1190 progName);
1191 exit(1);
1193 break;
1194 case 'N':
1195 if (mode != SIGN) {
1196 fprintf(stderr,
1197 "%s: option -N only supported with option -S.\n",
1198 progName);
1199 Usage(progName);
1200 exit(1);
1202 signOptions.nickname = strdup(optstate->value);
1203 break;
1204 case 'O':
1205 mode = CERTSONLY;
1206 break;
1207 case 'P':
1208 if (mode != SIGN) {
1209 fprintf(stderr,
1210 "%s: option -P only supported with option -S.\n",
1211 progName);
1212 Usage(progName);
1213 exit(1);
1215 signOptions.smimeProfile = PR_TRUE;
1216 break;
1217 case 'S':
1218 mode = SIGN;
1219 break;
1220 case 'T':
1221 if (mode != SIGN) {
1222 fprintf(stderr,
1223 "%s: option -T only supported with option -S.\n",
1224 progName);
1225 Usage(progName);
1226 exit(1);
1228 signOptions.detached = PR_TRUE;
1229 break;
1230 case 'Y':
1231 if (mode != SIGN) {
1232 fprintf(stderr,
1233 "%s: option -Y only supported with option -S.\n",
1234 progName);
1235 Usage(progName);
1236 exit(1);
1238 signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
1239 break;
1241 case 'b':
1242 if (mode != DECODE) {
1243 fprintf(stderr,
1244 "%s: option -b only supported with option -D.\n",
1245 progName);
1246 Usage(progName);
1247 exit(1);
1249 batch = PR_TRUE;
1250 break;
1252 case 'c':
1253 if (mode != DECODE) {
1254 fprintf(stderr,
1255 "%s: option -c only supported with option -D.\n",
1256 progName);
1257 Usage(progName);
1258 exit(1);
1260 contentFile = PR_Open(optstate->value, PR_RDONLY, 006600);
1261 if (contentFile == NULL) {
1262 fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
1263 progName, optstate->value);
1264 exit(1);
1267 rv = SECU_FileToItem(&decodeOptions.content, contentFile);
1268 PR_Close(contentFile);
1269 if (rv != SECSuccess) {
1270 SECU_PrintError(progName, "problem reading content file");
1271 exit(1);
1273 if (!decodeOptions.content.data) {
1274 /* file was zero length */
1275 decodeOptions.content.data = (unsigned char *)PORT_Strdup("");
1276 decodeOptions.content.len = 0;
1279 break;
1280 case 'd':
1281 SECU_ConfigDirectory(optstate->value);
1282 break;
1283 case 'e':
1284 envFileName = strdup(optstate->value);
1285 encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660);
1286 break;
1288 case 'h':
1289 if (mode != DECODE) {
1290 fprintf(stderr,
1291 "%s: option -h only supported with option -D.\n",
1292 progName);
1293 Usage(progName);
1294 exit(1);
1296 decodeOptions.headerLevel = atoi(optstate->value);
1297 if (decodeOptions.headerLevel < 0) {
1298 fprintf(stderr, "option -h cannot have a negative value.\n");
1299 exit(1);
1301 break;
1302 case 'i':
1303 if (!optstate->value) {
1304 fprintf(stderr, "-i option requires filename argument\n");
1305 exit(1);
1307 inFile = PR_Open(optstate->value, PR_RDONLY, 00660);
1308 if (inFile == NULL) {
1309 fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
1310 progName, optstate->value);
1311 exit(1);
1313 break;
1315 case 'k':
1316 if (mode != DECODE) {
1317 fprintf(stderr,
1318 "%s: option -k only supported with option -D.\n",
1319 progName);
1320 Usage(progName);
1321 exit(1);
1323 decodeOptions.keepCerts = PR_TRUE;
1324 break;
1326 case 'n':
1327 if (mode != DECODE) {
1328 fprintf(stderr,
1329 "%s: option -n only supported with option -D.\n",
1330 progName);
1331 Usage(progName);
1332 exit(1);
1334 decodeOptions.suppressContent = PR_TRUE;
1335 break;
1336 case 'o':
1337 outFile = fopen(optstate->value, "wb");
1338 if (outFile == NULL) {
1339 fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
1340 progName, optstate->value);
1341 exit(1);
1343 break;
1344 case 'p':
1345 if (!optstate->value) {
1346 fprintf(stderr, "%s: option -p must have a value.\n", progName);
1347 Usage(progName);
1348 exit(1);
1351 options.password = strdup(optstate->value);
1352 break;
1354 case 'r':
1355 if (!optstate->value) {
1356 fprintf(stderr, "%s: option -r must have a value.\n", progName);
1357 Usage(progName);
1358 exit(1);
1360 envelopeOptions.recipients = ptrarray;
1361 str = (char *)optstate->value;
1362 do {
1363 tok = strchr(str, ',');
1364 if (tok) *tok = '\0';
1365 envelopeOptions.recipients[nrecipients++] = strdup(str);
1366 if (tok) str = tok + 1;
1367 } while (tok);
1368 envelopeOptions.recipients[nrecipients] = NULL;
1369 encryptOptions.recipients = envelopeOptions.recipients;
1370 certsonlyOptions.recipients = envelopeOptions.recipients;
1371 break;
1373 case 'u': {
1374 int usageType;
1376 usageType = atoi (strdup(optstate->value));
1377 if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
1378 return -1;
1379 options.certUsage = (SECCertUsage)usageType;
1380 break;
1382 case 'v':
1383 cms_verbose = 1;
1384 break;
1388 if (status == PL_OPT_BAD)
1389 Usage(progName);
1390 PL_DestroyOptState(optstate);
1392 if (mode == UNKNOWN)
1393 Usage(progName);
1395 if (mode != CERTSONLY && !batch) {
1396 rv = SECU_FileToItem(&input, inFile);
1397 if (rv != SECSuccess) {
1398 SECU_PrintError(progName, "unable to read infile");
1399 exit(1);
1401 if (inFile != PR_STDIN) {
1402 PR_Close(inFile);
1405 if (cms_verbose) {
1406 fprintf(stderr, "received commands\n");
1409 /* Call the libsec initialization routines */
1410 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
1411 rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL));
1412 if (SECSuccess != rv) {
1413 SECU_PrintError(progName, "NSS_Init failed");
1414 exit(1);
1416 if (cms_verbose) {
1417 fprintf(stderr, "NSS has been initialized.\n");
1419 options.certHandle = CERT_GetDefaultCertDB();
1420 if (!options.certHandle) {
1421 SECU_PrintError(progName, "No default cert DB");
1422 exit(1);
1424 if (cms_verbose) {
1425 fprintf(stderr, "Got default certdb\n");
1427 if (options.password)
1429 pwdata.source = PW_PLAINTEXT;
1430 pwdata.data = options.password;
1432 pwcb = SECU_GetModulePassword;
1433 pwcb_arg = (void *)&pwdata;
1435 PK11_SetPasswordFunc(&SECU_GetModulePassword);
1438 #if defined(_WIN32)
1439 if (outFile == stdout) {
1440 /* If we're going to write binary data to stdout, we must put stdout
1441 ** into O_BINARY mode or else outgoing \n's will become \r\n's.
1443 int smrv = _setmode(_fileno(stdout), _O_BINARY);
1444 if (smrv == -1) {
1445 fprintf(stderr,
1446 "%s: Cannot change stdout to binary mode. Use -o option instead.\n",
1447 progName);
1448 return smrv;
1451 #endif
1453 exitstatus = 0;
1454 switch (mode) {
1455 case DECODE: /* -D */
1456 decodeOptions.options = &options;
1457 if (encryptOptions.envFile) {
1458 /* Decoding encrypted-data, so get the bulkkey from an
1459 * enveloped-data message.
1461 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1462 decodeOptions.options = &options;
1463 encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
1464 if (!encryptOptions.envmsg) {
1465 SECU_PrintError(progName, "problem decoding env msg");
1466 exitstatus = 1;
1467 break;
1469 rv = get_enc_params(&encryptOptions);
1470 decodeOptions.dkcb = dkcb;
1471 decodeOptions.bulkkey = encryptOptions.bulkkey;
1473 if (!batch) {
1474 cmsg = decode(outFile, &input, &decodeOptions);
1475 if (!cmsg) {
1476 SECU_PrintError(progName, "problem decoding");
1477 exitstatus = 1;
1479 } else {
1480 exitstatus = doBatchDecode(outFile, inFile, &decodeOptions);
1481 if (inFile != PR_STDIN) {
1482 PR_Close(inFile);
1485 break;
1486 case SIGN: /* -S */
1487 signOptions.options = &options;
1488 cmsg = signed_data(&signOptions);
1489 if (!cmsg) {
1490 SECU_PrintError(progName, "problem signing");
1491 exitstatus = 1;
1493 break;
1494 case ENCRYPT: /* -C */
1495 if (!envFileName) {
1496 fprintf(stderr, "%s: you must specify an envelope file with -e.\n",
1497 progName);
1498 exit(1);
1500 encryptOptions.options = &options;
1501 encryptOptions.input = &input;
1502 encryptOptions.outfile = outFile;
1503 /* decode an enveloped-data message to get the bulkkey (create
1504 * a new one if neccessary)
1506 if (!encryptOptions.envFile) {
1507 encryptOptions.envFile = PR_Open(envFileName,
1508 PR_WRONLY|PR_CREATE_FILE, 00660);
1509 if (!encryptOptions.envFile) {
1510 fprintf(stderr, "%s: failed to create file %s.\n", progName,
1511 envFileName);
1512 exit(1);
1514 } else {
1515 SECU_FileToItem(&envmsg, encryptOptions.envFile);
1516 decodeOptions.options = &options;
1517 encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
1518 if (encryptOptions.envmsg == NULL) {
1519 SECU_PrintError(progName, "problem decrypting env msg");
1520 exitstatus = 1;
1521 break;
1524 rv = get_enc_params(&encryptOptions);
1525 /* create the encrypted-data message */
1526 cmsg = encrypted_data(&encryptOptions);
1527 if (!cmsg) {
1528 SECU_PrintError(progName, "problem encrypting");
1529 exitstatus = 1;
1531 if (encryptOptions.bulkkey) {
1532 PK11_FreeSymKey(encryptOptions.bulkkey);
1533 encryptOptions.bulkkey = NULL;
1535 break;
1536 case ENVELOPE: /* -E */
1537 envelopeOptions.options = &options;
1538 cmsg = enveloped_data(&envelopeOptions);
1539 if (!cmsg) {
1540 SECU_PrintError(progName, "problem enveloping");
1541 exitstatus = 1;
1543 break;
1544 case CERTSONLY: /* -O */
1545 certsonlyOptions.options = &options;
1546 cmsg = signed_data_certsonly(&certsonlyOptions);
1547 if (!cmsg) {
1548 SECU_PrintError(progName, "problem with certs-only");
1549 exitstatus = 1;
1551 break;
1552 default:
1553 fprintf(stderr, "One of options -D, -S or -E must be set.\n");
1554 Usage(progName);
1555 exitstatus = 1;
1557 if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
1558 && (!exitstatus) ) {
1559 PLArenaPool *arena = PORT_NewArena(1024);
1560 NSSCMSEncoderContext *ecx;
1561 SECItem output = { 0, 0, 0 };
1563 if (!arena) {
1564 fprintf(stderr, "%s: out of memory.\n", progName);
1565 exit(1);
1568 if (cms_verbose) {
1569 fprintf(stderr, "cmsg [%p]\n", cmsg);
1570 fprintf(stderr, "arena [%p]\n", arena);
1571 if (pwcb_arg)
1572 fprintf(stderr, "password [%s]\n", (char *)pwcb_arg);
1573 else
1574 fprintf(stderr, "password [NULL]\n");
1576 ecx = NSS_CMSEncoder_Start(cmsg,
1577 NULL, NULL, /* DER output callback */
1578 &output, arena, /* destination storage */
1579 pwcb, pwcb_arg, /* password callback */
1580 NULL, NULL, /* decrypt key callback */
1581 NULL, NULL ); /* detached digests */
1582 if (!ecx) {
1583 fprintf(stderr, "%s: cannot create encoder context.\n", progName);
1584 exit(1);
1586 if (cms_verbose) {
1587 fprintf(stderr, "input len [%d]\n", input.len);
1588 { unsigned int j;
1589 for(j=0;j<input.len;j++)
1590 fprintf(stderr, "%2x%c", input.data[j], (j>0&&j%35==0)?'\n':' ');
1593 if (input.len > 0) { /* skip if certs-only (or other zero content) */
1594 rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len);
1595 if (rv) {
1596 fprintf(stderr,
1597 "%s: failed to add data to encoder.\n", progName);
1598 exit(1);
1601 rv = NSS_CMSEncoder_Finish(ecx);
1602 if (rv) {
1603 SECU_PrintError(progName, "failed to encode data");
1604 exit(1);
1607 if (cms_verbose) {
1608 fprintf(stderr, "encoding passed\n");
1610 fwrite(output.data, output.len, 1, outFile);
1611 if (cms_verbose) {
1612 fprintf(stderr, "wrote to file\n");
1614 PORT_FreeArena(arena, PR_FALSE);
1616 if (cmsg)
1617 NSS_CMSMessage_Destroy(cmsg);
1618 if (outFile != stdout)
1619 fclose(outFile);
1621 SECITEM_FreeItem(&decodeOptions.content, PR_FALSE);
1622 SECITEM_FreeItem(&envmsg, PR_FALSE);
1623 SECITEM_FreeItem(&input, PR_FALSE);
1624 if (NSS_Shutdown() != SECSuccess) {
1625 SECU_PrintError(progName, "NSS_Shutdown failed");
1626 exitstatus = 1;
1628 PR_Cleanup();
1629 return exitstatus;