security/nss/cmd/smimetools/cmsutil.c

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

mercurial