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(void)71 Usage(void)
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     fprintf(stderr, "%-25s 12 - certUsageIPsec\n", " ");
119 
120     exit(-1);
121 }
122 
123 struct optionsStr {
124     char *pwfile;
125     char *password;
126     SECCertUsage certUsage;
127     CERTCertDBHandle *certHandle;
128 };
129 
130 struct decodeOptionsStr {
131     struct optionsStr *options;
132     SECItem content;
133     int headerLevel;
134     PRBool suppressContent;
135     NSSCMSGetDecryptKeyCallback dkcb;
136     PK11SymKey *bulkkey;
137     PRBool keepCerts;
138 };
139 
140 struct signOptionsStr {
141     struct optionsStr *options;
142     char *nickname;
143     char *encryptionKeyPreferenceNick;
144     PRBool signingTime;
145     PRBool smimeProfile;
146     PRBool detached;
147     SECOidTag hashAlgTag;
148 };
149 
150 struct envelopeOptionsStr {
151     struct optionsStr *options;
152     char **recipients;
153 };
154 
155 struct certsonlyOptionsStr {
156     struct optionsStr *options;
157     char **recipients;
158 };
159 
160 struct encryptOptionsStr {
161     struct optionsStr *options;
162     char **recipients;
163     NSSCMSMessage *envmsg;
164     SECItem *input;
165     FILE *outfile;
166     PRFileDesc *envFile;
167     PK11SymKey *bulkkey;
168     SECOidTag bulkalgtag;
169     int keysize;
170 };
171 
172 static NSSCMSMessage *
decode(FILE * out,SECItem * input,const struct decodeOptionsStr * decodeOptions)173 decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions)
174 {
175     NSSCMSDecoderContext *dcx;
176     SECStatus rv;
177     NSSCMSMessage *cmsg;
178     int nlevels, i;
179     SECItem sitem = { 0, 0, 0 };
180 
181     PORT_SetError(0);
182     dcx = NSS_CMSDecoder_Start(NULL,
183                                NULL, NULL,          /* content callback     */
184                                pwcb, pwcb_arg,      /* password callback    */
185                                decodeOptions->dkcb, /* decrypt key callback */
186                                decodeOptions->bulkkey);
187     if (dcx == NULL) {
188         fprintf(stderr, "%s: failed to set up message decoder.\n", progName);
189         return NULL;
190     }
191     rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
192     if (rv != SECSuccess) {
193         fprintf(stderr, "%s: failed to decode message.\n", progName);
194         NSS_CMSDecoder_Cancel(dcx);
195         return NULL;
196     }
197     cmsg = NSS_CMSDecoder_Finish(dcx);
198     if (cmsg == NULL) {
199         fprintf(stderr, "%s: failed to decode message.\n", progName);
200         return NULL;
201     }
202 
203     if (decodeOptions->headerLevel >= 0) {
204         /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
205         fprintf(out, "SMIME: ");
206     }
207 
208     nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
209     for (i = 0; i < nlevels; i++) {
210         NSSCMSContentInfo *cinfo;
211         SECOidTag typetag;
212 
213         cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
214         typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
215 
216         if (decodeOptions->headerLevel >= 0)
217             fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
218 
219         switch (typetag) {
220             case SEC_OID_PKCS7_SIGNED_DATA: {
221                 NSSCMSSignedData *sigd = NULL;
222                 SECItem **digests;
223                 int nsigners;
224                 int j;
225 
226                 if (decodeOptions->headerLevel >= 0)
227                     fprintf(out, "type=signedData; ");
228                 sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
229                 if (sigd == NULL) {
230                     SECU_PrintError(progName, "signedData component missing");
231                     goto loser;
232                 }
233 
234                 /* if we have a content file, but no digests for this signedData */
235                 if (decodeOptions->content.data != NULL &&
236                     !NSS_CMSSignedData_HasDigests(sigd)) {
237                     PLArenaPool *poolp;
238                     SECAlgorithmID **digestalgs;
239 
240                     /* detached content: grab content file */
241                     sitem = decodeOptions->content;
242 
243                     if ((poolp = PORT_NewArena(1024)) == NULL) {
244                         fprintf(stderr, "cmsutil: Out of memory.\n");
245                         goto loser;
246                     }
247                     digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
248                     if (DigestFile(poolp, &digests, &sitem, digestalgs) !=
249                         SECSuccess) {
250                         SECU_PrintError(progName,
251                                         "problem computing message digest");
252                         PORT_FreeArena(poolp, PR_FALSE);
253                         goto loser;
254                     }
255                     if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) !=
256                         SECSuccess) {
257                         SECU_PrintError(progName,
258                                         "problem setting message digests");
259                         PORT_FreeArena(poolp, PR_FALSE);
260                         goto loser;
261                     }
262                     PORT_FreeArena(poolp, PR_FALSE);
263                 }
264 
265                 /* import the certificates */
266                 if (NSS_CMSSignedData_ImportCerts(sigd,
267                                                   decodeOptions->options->certHandle,
268                                                   decodeOptions->options->certUsage,
269                                                   decodeOptions->keepCerts) !=
270                     SECSuccess) {
271                     SECU_PrintError(progName, "cert import failed");
272                     goto loser;
273                 }
274 
275                 /* find out about signers */
276                 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
277                 if (decodeOptions->headerLevel >= 0)
278                     fprintf(out, "nsigners=%d; ", nsigners);
279                 if (nsigners == 0) {
280                     /* Might be a cert transport message
281                     ** or might be an invalid message, such as a QA test message
282                     ** or a message from an attacker.
283                     */
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
1372     PL_DestroyOptState(optstate);
1373 
1374     if (mode == UNKNOWN)
1375         Usage();
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();
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