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