Back to index

lightning-sunbird  0.9+nobinonly
cmsutil.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * cmsutil -- A command to work with CMS data
00039  *
00040  * $Id: cmsutil.c,v 1.52 2004/10/22 22:39:47 julien.pierre.bugs%sun.com Exp $
00041  */
00042 
00043 #include "nspr.h"
00044 #include "secutil.h"
00045 #include "plgetopt.h"
00046 #include "secpkcs7.h"
00047 #include "cert.h"
00048 #include "certdb.h"
00049 #include "secoid.h"
00050 #include "cms.h"
00051 #include "nss.h"
00052 #include "smime.h"
00053 #include "pk11func.h"
00054 
00055 #if defined(XP_UNIX)
00056 #include <unistd.h>
00057 #endif
00058 
00059 #if defined(_WIN32)
00060 #include "fcntl.h"
00061 #include "io.h"
00062 #endif
00063 
00064 #include <stdio.h>
00065 #include <string.h>
00066 
00067 char *progName = NULL;
00068 static int cms_verbose = 0;
00069 static secuPWData pwdata = { PW_NONE, 0 };
00070 static PK11PasswordFunc pwcb = NULL;
00071 static void *pwcb_arg = NULL;
00072 
00073 
00074 /* XXX stolen from cmsarray.c
00075  * nss_CMSArray_Count - count number of elements in array
00076  */
00077 int
00078 nss_CMSArray_Count(void **array)
00079 {
00080     int n = 0;
00081     if (array == NULL)
00082        return 0;
00083     while (*array++ != NULL)
00084        n++;
00085     return n;
00086 }
00087 
00088 static SECStatus
00089 DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
00090            SECAlgorithmID **algids)
00091 {
00092     NSSCMSDigestContext *digcx;
00093     SECStatus rv;
00094 
00095     digcx = NSS_CMSDigestContext_StartMultiple(algids);
00096     if (digcx == NULL)
00097        return SECFailure;
00098 
00099     NSS_CMSDigestContext_Update(digcx, input->data, input->len);
00100 
00101     rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
00102     return rv;
00103 }
00104 
00105 
00106 static void
00107 Usage(char *progName)
00108 {
00109     fprintf(stderr, 
00110 "Usage:  %s [-C|-D|-E|-O|-S] [<options>] [-d dbdir] [-u certusage]\n"
00111 " -C            create a CMS encrypted data message\n"
00112 " -D            decode a CMS message\n"
00113 "  -b           decode a batch of files named in infile\n"
00114 "  -c content   use this detached content\n"
00115 "  -n           suppress output of content\n"
00116 "  -h num       display num levels of CMS message info as email headers\n"
00117 "  -k           keep decoded encryption certs in perm cert db\n"
00118 " -E            create a CMS enveloped data message\n"
00119 "  -r id,...    create envelope for these recipients,\n"
00120 "               where id can be a certificate nickname or email address\n"
00121 " -S            create a CMS signed data message\n"
00122 "  -G           include a signing time attribute\n"
00123 "  -H hash      use hash (default:SHA1)\n"
00124 "  -N nick      use certificate named \"nick\" for signing\n"
00125 "  -P           include a SMIMECapabilities attribute\n"
00126 "  -T           do not include content in CMS message\n"
00127 "  -Y nick      include a EncryptionKeyPreference attribute with cert\n"
00128 "                 (use \"NONE\" to omit)\n"
00129 " -O            create a CMS signed message containing only certificates\n"
00130 " General Options:\n"
00131 " -d dbdir      key/cert database directory (default: ~/.netscape)\n"
00132 " -e envelope   enveloped data message in this file is used for bulk key\n"
00133 " -i infile     use infile as source of data (default: stdin)\n"
00134 " -o outfile    use outfile as destination of data (default: stdout)\n"
00135 " -p password   use password as key db password (default: prompt)\n"
00136 " -u certusage  set type of certificate usage (default: certUsageEmailSigner)\n"
00137 " -v            print debugging information\n"
00138 "\n"
00139 "Cert usage codes:\n",
00140            progName);
00141     fprintf(stderr, "%-25s  0 - certUsageSSLClient\n", " ");
00142     fprintf(stderr, "%-25s  1 - certUsageSSLServer\n", " ");
00143     fprintf(stderr, "%-25s  2 - certUsageSSLServerWithStepUp\n", " ");
00144     fprintf(stderr, "%-25s  3 - certUsageSSLCA\n", " ");
00145     fprintf(stderr, "%-25s  4 - certUsageEmailSigner\n", " ");
00146     fprintf(stderr, "%-25s  5 - certUsageEmailRecipient\n", " ");
00147     fprintf(stderr, "%-25s  6 - certUsageObjectSigner\n", " ");
00148     fprintf(stderr, "%-25s  7 - certUsageUserCertImport\n", " ");
00149     fprintf(stderr, "%-25s  8 - certUsageVerifyCA\n", " ");
00150     fprintf(stderr, "%-25s  9 - certUsageProtectedObjectSigner\n", " ");
00151     fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
00152     fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
00153 
00154     exit(-1);
00155 }
00156 
00157 struct optionsStr {
00158     char *password;
00159     SECCertUsage certUsage;
00160     CERTCertDBHandle *certHandle;
00161 };
00162 
00163 struct decodeOptionsStr {
00164     struct optionsStr *options;
00165     SECItem            content;
00166     int headerLevel;
00167     PRBool suppressContent;
00168     NSSCMSGetDecryptKeyCallback dkcb;
00169     PK11SymKey *bulkkey;
00170     PRBool      keepCerts;
00171 };
00172 
00173 struct signOptionsStr {
00174     struct optionsStr *options;
00175     char *nickname;
00176     char *encryptionKeyPreferenceNick;
00177     PRBool signingTime;
00178     PRBool smimeProfile;
00179     PRBool detached;
00180     SECOidTag hashAlgTag;
00181 };
00182 
00183 struct envelopeOptionsStr {
00184     struct optionsStr *options;
00185     char **recipients;
00186 };
00187 
00188 struct certsonlyOptionsStr {
00189     struct optionsStr *options;
00190     char **recipients;
00191 };
00192 
00193 struct encryptOptionsStr {
00194     struct optionsStr *options;
00195     char **recipients;
00196     NSSCMSMessage *envmsg;
00197     SECItem *input;
00198     FILE *outfile;
00199     PRFileDesc *envFile;
00200     PK11SymKey *bulkkey;
00201     SECOidTag bulkalgtag;
00202     int keysize;
00203 };
00204 
00205 static NSSCMSMessage *
00206 decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions)
00207 {
00208     NSSCMSDecoderContext *dcx;
00209     NSSCMSMessage *cmsg;
00210     int nlevels, i;
00211     SECItem sitem = { 0, 0, 0 };
00212 
00213     PORT_SetError(0);
00214     dcx = NSS_CMSDecoder_Start(NULL, 
00215                                NULL, NULL,         /* content callback     */
00216                                pwcb, pwcb_arg,     /* password callback    */
00217                             decodeOptions->dkcb, /* decrypt key callback */
00218                                decodeOptions->bulkkey);
00219     (void)NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
00220     cmsg = NSS_CMSDecoder_Finish(dcx);
00221     if (cmsg == NULL) {
00222        fprintf(stderr, "%s: failed to decode message.\n", progName);
00223        return NULL;
00224     }
00225 
00226     if (decodeOptions->headerLevel >= 0) {
00227        /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/
00228        fprintf(out, "SMIME: ");
00229     }
00230 
00231     nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
00232     for (i = 0; i < nlevels; i++) {
00233        NSSCMSContentInfo *cinfo;
00234        SECOidTag typetag;
00235 
00236        cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
00237        typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00238 
00239        if (decodeOptions->headerLevel >= 0)
00240            fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i);
00241 
00242        switch (typetag) {
00243        case SEC_OID_PKCS7_SIGNED_DATA:
00244          {
00245            NSSCMSSignedData *sigd = NULL;
00246            SECItem **digests;
00247            int nsigners;
00248            int j;
00249 
00250            if (decodeOptions->headerLevel >= 0)
00251               fprintf(out, "type=signedData; ");
00252            sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
00253            if (sigd == NULL) {
00254               SECU_PrintError(progName, "signedData component missing");
00255               goto loser;
00256            }
00257 
00258            /* if we have a content file, but no digests for this signedData */
00259            if (decodeOptions->content.data != NULL && 
00260                !NSS_CMSSignedData_HasDigests(sigd)) {
00261               PLArenaPool     *poolp;
00262               SECAlgorithmID **digestalgs;
00263 
00264               /* detached content: grab content file */
00265               sitem = decodeOptions->content;
00266 
00267               if ((poolp = PORT_NewArena(1024)) == NULL) {
00268                   fprintf(stderr, "cmsutil: Out of memory.\n");
00269                   goto loser;
00270               }
00271               digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
00272               if (DigestFile (poolp, &digests, &sitem, digestalgs) 
00273                     != SECSuccess) {
00274                   SECU_PrintError(progName, 
00275                                   "problem computing message digest");
00276                   PORT_FreeArena(poolp, PR_FALSE);
00277                   goto loser;
00278               }
00279               if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) 
00280                   != SECSuccess) {
00281                   SECU_PrintError(progName, 
00282                                   "problem setting message digests");
00283                   PORT_FreeArena(poolp, PR_FALSE);
00284                   goto loser;
00285               }
00286               PORT_FreeArena(poolp, PR_FALSE);
00287            }
00288 
00289            /* import the certificates */
00290            if (NSS_CMSSignedData_ImportCerts(sigd, 
00291                                           decodeOptions->options->certHandle, 
00292                                           decodeOptions->options->certUsage, 
00293                                           decodeOptions->keepCerts) 
00294                  != SECSuccess) {
00295               SECU_PrintError(progName, "cert import failed");
00296               goto loser;
00297            }
00298 
00299            /* find out about signers */
00300            nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
00301            if (decodeOptions->headerLevel >= 0)
00302               fprintf(out, "nsigners=%d; ", nsigners);
00303            if (nsigners == 0) {
00304               /* Might be a cert transport message
00305               ** or might be an invalid message, such as a QA test message
00306               ** or a message from an attacker.
00307               */
00308               SECStatus rv;
00309               rv = NSS_CMSSignedData_VerifyCertsOnly(sigd, 
00310                                           decodeOptions->options->certHandle, 
00311                                           decodeOptions->options->certUsage);
00312               if (rv != SECSuccess) {
00313                   fprintf(stderr, "cmsutil: Verify certs-only failed!\n");
00314                   goto loser;
00315               }
00316               return cmsg;
00317            }
00318 
00319            /* still no digests? */
00320            if (!NSS_CMSSignedData_HasDigests(sigd)) {
00321               SECU_PrintError(progName, "no message digests");
00322               goto loser;
00323            }
00324 
00325            for (j = 0; j < nsigners; j++) {
00326               const char * svs;
00327               NSSCMSSignerInfo *si;
00328               NSSCMSVerificationStatus vs;
00329               SECStatus bad;
00330 
00331               si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
00332               if (decodeOptions->headerLevel >= 0) {
00333                   char *signercn;
00334                   static char empty[] = { "" };
00335 
00336                   signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
00337                   if (signercn == NULL)
00338                      signercn = empty;
00339                   fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
00340                   if (signercn != empty)
00341                       PORT_Free(signercn);
00342               }
00343               bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j, 
00344                                          decodeOptions->options->certHandle, 
00345                                          decodeOptions->options->certUsage);
00346               vs  = NSS_CMSSignerInfo_GetVerificationStatus(si);
00347               svs = NSS_CMSUtil_VerificationStatusToString(vs);
00348               if (decodeOptions->headerLevel >= 0) {
00349                   fprintf(out, "signer%d.status=%s; ", j, svs);
00350                   /* goto loser ? */
00351               } else if (bad && out) {
00352                   fprintf(stderr, "signer %d status = %s\n", j, svs);
00353                   goto loser;
00354               }
00355            }
00356          }
00357          break;
00358        case SEC_OID_PKCS7_ENVELOPED_DATA:
00359          {
00360            NSSCMSEnvelopedData *envd;
00361            if (decodeOptions->headerLevel >= 0)
00362               fprintf(out, "type=envelopedData; ");
00363            envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
00364            if (envd == NULL) {
00365               SECU_PrintError(progName, "envelopedData component missing");
00366               goto loser;
00367            }
00368          }
00369          break;
00370        case SEC_OID_PKCS7_ENCRYPTED_DATA:
00371          {
00372            NSSCMSEncryptedData *encd;
00373            if (decodeOptions->headerLevel >= 0)
00374               fprintf(out, "type=encryptedData; ");
00375            encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
00376            if (encd == NULL) {
00377               SECU_PrintError(progName, "encryptedData component missing");
00378               goto loser;
00379            }
00380          }
00381          break;
00382        case SEC_OID_PKCS7_DATA:
00383            if (decodeOptions->headerLevel >= 0)
00384               fprintf(out, "type=data; ");
00385            break;
00386        default:
00387            break;
00388        }
00389        if (decodeOptions->headerLevel >= 0)
00390            fprintf(out, "\n");
00391     }
00392 
00393     if (!decodeOptions->suppressContent && out) {
00394        SECItem *item = (sitem.data ? &sitem 
00395                                    : NSS_CMSMessage_GetContent(cmsg));
00396        if (item && item->data && item->len) {
00397            fwrite(item->data, item->len, 1, out);
00398        }
00399     }
00400     return cmsg;
00401 
00402 loser:
00403     if (cmsg)
00404        NSS_CMSMessage_Destroy(cmsg);
00405     return NULL;
00406 }
00407 
00408 /* example of a callback function to use with encoder */
00409 /*
00410 static void
00411 writeout(void *arg, const char *buf, unsigned long len)
00412 {
00413     FILE *f = (FILE *)arg;
00414 
00415     if (f != NULL && buf != NULL)
00416        (void)fwrite(buf, len, 1, f);
00417 }
00418 */
00419 
00420 static NSSCMSMessage *
00421 signed_data(struct signOptionsStr *signOptions)
00422 {
00423     NSSCMSMessage *cmsg = NULL;
00424     NSSCMSContentInfo *cinfo;
00425     NSSCMSSignedData *sigd;
00426     NSSCMSSignerInfo *signerinfo;
00427     CERTCertificate *cert= NULL, *ekpcert = NULL;
00428 
00429     if (cms_verbose) {
00430        fprintf(stderr, "Input to signed_data:\n");
00431        if (signOptions->options->password)
00432            fprintf(stderr, "password [%s]\n", signOptions->options->password);
00433        else
00434            fprintf(stderr, "password [NULL]\n");
00435        fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage);
00436        if (signOptions->options->certHandle)
00437            fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle);
00438        else
00439            fprintf(stderr, "certdb [NULL]\n");
00440        if (signOptions->nickname)
00441            fprintf(stderr, "nickname [%s]\n", signOptions->nickname);
00442        else
00443            fprintf(stderr, "nickname [NULL]\n");
00444     }
00445     if (signOptions->nickname == NULL) {
00446        fprintf(stderr, 
00447         "ERROR: please indicate the nickname of a certificate to sign with.\n");
00448        return NULL;
00449     }
00450     if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle, 
00451                                          signOptions->nickname,
00452                                          signOptions->options->certUsage,
00453                                          PR_FALSE,
00454                                          &pwdata)) == NULL) {
00455        SECU_PrintError(progName, 
00456                        "the corresponding cert for key \"%s\" does not exist",
00457                        signOptions->nickname);
00458        return NULL;
00459     }
00460     if (cms_verbose) {
00461        fprintf(stderr, "Found certificate for %s\n", signOptions->nickname);
00462     }
00463     /*
00464      * create the message object
00465      */
00466     cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
00467     if (cmsg == NULL) {
00468        fprintf(stderr, "ERROR: cannot create CMS message.\n");
00469        return NULL;
00470     }
00471     /*
00472      * build chain of objects: message->signedData->data
00473      */
00474     if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
00475        fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
00476        goto loser;
00477     }
00478     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
00479     if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) 
00480           != SECSuccess) {
00481        fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
00482        goto loser;
00483     }
00484     cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
00485     /* we're always passing data in and detaching optionally */
00486     if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, 
00487                                            signOptions->detached) 
00488           != SECSuccess) {
00489        fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
00490        goto loser;
00491     }
00492     /* 
00493      * create & attach signer information
00494      */
00495     signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag);
00496     if (signerinfo == NULL) {
00497        fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
00498        goto loser;
00499     }
00500     if (cms_verbose) {
00501        fprintf(stderr,
00502                "Created CMS message, added signed data w/ signerinfo\n");
00503     }
00504     /* we want the cert chain included for this one */
00505     if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, 
00506                                        signOptions->options->certUsage) 
00507           != SECSuccess) {
00508        fprintf(stderr, "ERROR: cannot find cert chain.\n");
00509        goto loser;
00510     }
00511     if (cms_verbose) {
00512        fprintf(stderr, "imported certificate\n");
00513     }
00514     if (signOptions->signingTime) {
00515        if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) 
00516              != SECSuccess) {
00517            fprintf(stderr, "ERROR: cannot add signingTime attribute.\n");
00518            goto loser;
00519        }
00520     }
00521     if (signOptions->smimeProfile) {
00522        if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) {
00523            fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n");
00524            goto loser;
00525        }
00526     }
00527 
00528     if (!signOptions->encryptionKeyPreferenceNick) {
00529        /* check signing cert for fitness as encryption cert */
00530         SECStatus FitForEncrypt = CERT_CheckCertUsage(cert,
00531                                                       certUsageEmailRecipient);
00532 
00533         if (SECSuccess == FitForEncrypt) {
00534             /* if yes, add signing cert as EncryptionKeyPreference */
00535             if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, 
00536                                               signOptions->options->certHandle)
00537                   != SECSuccess) {
00538                 fprintf(stderr, 
00539                     "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n");
00540                 goto loser;
00541             }
00542             if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert, 
00543                                               signOptions->options->certHandle)
00544                   != SECSuccess) {
00545                 fprintf(stderr, 
00546                     "ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n");
00547                 goto loser;
00548             }
00549         } else {
00550             /* this is a dual-key cert case, we need to look for the encryption
00551                certificate under the same nickname as the signing cert */
00552             /* get the cert, add it to the message */
00553             if ((ekpcert = CERT_FindUserCertByUsage(
00554                                               signOptions->options->certHandle,
00555                                               signOptions->nickname,
00556                                               certUsageEmailRecipient,
00557                                               PR_FALSE,
00558                                               &pwdata)) == NULL) {
00559                 SECU_PrintError(progName, 
00560                          "the corresponding cert for key \"%s\" does not exist",
00561                          signOptions->encryptionKeyPreferenceNick);
00562                 goto loser;
00563             }
00564             if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 
00565                                               signOptions->options->certHandle)
00566                   != SECSuccess) {
00567                 fprintf(stderr, 
00568                         "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
00569                 goto loser;
00570             }
00571             if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 
00572                                               signOptions->options->certHandle)
00573                   != SECSuccess) {
00574                 fprintf(stderr, 
00575                         "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
00576                 goto loser;
00577             }
00578             if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
00579                 fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
00580                 goto loser;
00581             }
00582         }
00583     } else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) {
00584         /* No action */
00585     } else {
00586        /* get the cert, add it to the message */
00587        if ((ekpcert = CERT_FindUserCertByUsage(
00588                                      signOptions->options->certHandle, 
00589                                     signOptions->encryptionKeyPreferenceNick,
00590                                      certUsageEmailRecipient, PR_FALSE, &pwdata))
00591              == NULL) {
00592            SECU_PrintError(progName, 
00593                       "the corresponding cert for key \"%s\" does not exist",
00594                        signOptions->encryptionKeyPreferenceNick);
00595            goto loser;
00596        }
00597        if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, 
00598                                             signOptions->options->certHandle)
00599              != SECSuccess) {
00600            fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n");
00601            goto loser;
00602        }
00603        if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, 
00604                                             signOptions->options->certHandle)
00605              != SECSuccess) {
00606            fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n");
00607            goto loser;
00608        }
00609        if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) {
00610            fprintf(stderr, "ERROR: cannot add encryption certificate.\n");
00611            goto loser;
00612        }
00613     }
00614 
00615     if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
00616        fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
00617        goto loser;
00618     }
00619     if (cms_verbose) {
00620        fprintf(stderr, "created signed-data message\n");
00621     }
00622     if (ekpcert) {
00623        CERT_DestroyCertificate(ekpcert);
00624     }
00625     if (cert) {
00626        CERT_DestroyCertificate(cert);
00627     }
00628     return cmsg;
00629 loser:
00630     if (ekpcert) {
00631        CERT_DestroyCertificate(ekpcert);
00632     }
00633     if (cert) {
00634        CERT_DestroyCertificate(cert);
00635     }
00636     NSS_CMSMessage_Destroy(cmsg);
00637     return NULL;
00638 }
00639 
00640 static NSSCMSMessage *
00641 enveloped_data(struct envelopeOptionsStr *envelopeOptions)
00642 {
00643     NSSCMSMessage *cmsg = NULL;
00644     NSSCMSContentInfo *cinfo;
00645     NSSCMSEnvelopedData *envd;
00646     NSSCMSRecipientInfo *recipientinfo;
00647     CERTCertificate **recipientcerts = NULL;
00648     CERTCertDBHandle *dbhandle;
00649     PLArenaPool *tmppoolp = NULL;
00650     SECOidTag bulkalgtag;
00651     int keysize, i = 0;
00652     int cnt;
00653     dbhandle = envelopeOptions->options->certHandle;
00654     /* count the recipients */
00655     if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) {
00656        fprintf(stderr, "ERROR: please name at least one recipient.\n");
00657        goto loser;
00658     }
00659     if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
00660        fprintf(stderr, "ERROR: out of memory.\n");
00661        goto loser;
00662     }
00663     /* XXX find the recipient's certs by email address or nickname */
00664     if ((recipientcerts = 
00665          (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp, 
00666                                         (cnt+1)*sizeof(CERTCertificate*)))
00667             == NULL) {
00668        fprintf(stderr, "ERROR: out of memory.\n");
00669        goto loser;
00670     }
00671     for (i=0; envelopeOptions->recipients[i] != NULL; i++) {
00672        if ((recipientcerts[i] = 
00673              CERT_FindCertByNicknameOrEmailAddr(dbhandle,  
00674                                                envelopeOptions->recipients[i]))
00675                == NULL) {
00676            SECU_PrintError(progName, "cannot find certificate for \"%s\"", 
00677                            envelopeOptions->recipients[i]);
00678            i=0;
00679            goto loser;
00680        }
00681     }
00682     recipientcerts[i] = NULL;
00683     i=0;
00684     /* find a nice bulk algorithm */
00685     if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, 
00686                                                &keysize) != SECSuccess) {
00687        fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
00688        goto loser;
00689     }
00690     /*
00691      * create the message object
00692      */
00693     cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
00694     if (cmsg == NULL) {
00695        fprintf(stderr, "ERROR: cannot create CMS message.\n");
00696        goto loser;
00697     }
00698     /*
00699      * build chain of objects: message->envelopedData->data
00700      */
00701     if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) 
00702           == NULL) {
00703        fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
00704        goto loser;
00705     }
00706     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
00707     if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) 
00708           != SECSuccess) {
00709        fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
00710        goto loser;
00711     }
00712     cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
00713     /* we're always passing data in, so the content is NULL */
00714     if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 
00715           != SECSuccess) {
00716        fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
00717        goto loser;
00718     }
00719     /* 
00720      * create & attach recipient information
00721      */
00722     for (i = 0; recipientcerts[i] != NULL; i++) {
00723        if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, 
00724                                                         recipientcerts[i])) 
00725              == NULL) {
00726            fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
00727            goto loser;
00728        }
00729        if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) 
00730              != SECSuccess) {
00731            fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
00732            goto loser;
00733        }
00734        CERT_DestroyCertificate(recipientcerts[i]);
00735     }
00736     if (tmppoolp)
00737        PORT_FreeArena(tmppoolp, PR_FALSE);
00738     return cmsg;
00739 loser:
00740     if (recipientcerts) {
00741        for (; recipientcerts[i] != NULL; i++) {
00742            CERT_DestroyCertificate(recipientcerts[i]);
00743        }
00744     }
00745     if (cmsg)
00746        NSS_CMSMessage_Destroy(cmsg);
00747     if (tmppoolp)
00748        PORT_FreeArena(tmppoolp, PR_FALSE);
00749     return NULL;
00750 }
00751 
00752 PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid)
00753 {
00754     return (PK11SymKey*)arg;
00755 }
00756 
00757 static SECStatus
00758 get_enc_params(struct encryptOptionsStr *encryptOptions)
00759 {
00760     struct envelopeOptionsStr envelopeOptions;
00761     SECStatus rv = SECFailure;
00762     NSSCMSMessage *env_cmsg;
00763     NSSCMSContentInfo *cinfo;
00764     int i, nlevels;
00765     /*
00766      * construct an enveloped data message to obtain bulk keys
00767      */
00768     if (encryptOptions->envmsg) {
00769        env_cmsg = encryptOptions->envmsg; /* get it from an old message */
00770     } else {
00771        SECItem dummyOut = { 0, 0, 0 };
00772        SECItem dummyIn  = { 0, 0, 0 };
00773        char str[] = "Hello!";
00774        PLArenaPool *tmparena = PORT_NewArena(1024);
00775        dummyIn.data = (unsigned char *)str;
00776        dummyIn.len = strlen(str);
00777        envelopeOptions.options = encryptOptions->options;
00778        envelopeOptions.recipients = encryptOptions->recipients;
00779        env_cmsg = enveloped_data(&envelopeOptions);
00780        NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena);
00781        PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len);
00782        PORT_FreeArena(tmparena, PR_FALSE);
00783     }
00784     /*
00785      * get the content info for the enveloped data 
00786      */
00787     nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg);
00788     for (i = 0; i < nlevels; i++) {
00789        SECOidTag typetag;
00790        cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i);
00791        typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
00792        if (typetag == SEC_OID_PKCS7_DATA) {
00793            /*
00794             * get the symmetric key
00795             */
00796            encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
00797            encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo);
00798            encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
00799            rv = SECSuccess;
00800            break;
00801        }
00802     }
00803     if (i == nlevels) {
00804        fprintf(stderr, "%s: could not retrieve enveloped data.", progName);
00805     }
00806     if (env_cmsg)
00807        NSS_CMSMessage_Destroy(env_cmsg);
00808     return rv;
00809 }
00810 
00811 static NSSCMSMessage *
00812 encrypted_data(struct encryptOptionsStr *encryptOptions)
00813 {
00814     SECStatus rv = SECFailure;
00815     NSSCMSMessage *cmsg = NULL;
00816     NSSCMSContentInfo *cinfo;
00817     NSSCMSEncryptedData *encd;
00818     NSSCMSEncoderContext *ecx = NULL;
00819     PLArenaPool *tmppoolp = NULL;
00820     SECItem derOut = { 0, 0, 0 };
00821     /* arena for output */
00822     tmppoolp = PORT_NewArena(1024);
00823     if (!tmppoolp) {
00824        fprintf(stderr, "%s: out of memory.\n", progName);
00825        return NULL;
00826     }
00827     /*
00828      * create the message object
00829      */
00830     cmsg = NSS_CMSMessage_Create(NULL);
00831     if (cmsg == NULL) {
00832        fprintf(stderr, "ERROR: cannot create CMS message.\n");
00833        goto loser;
00834     }
00835     /*
00836      * build chain of objects: message->encryptedData->data
00837      */
00838     if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag, 
00839                                                   encryptOptions->keysize)) 
00840            == NULL) {
00841        fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n");
00842        goto loser;
00843     }
00844     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
00845     if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd)
00846           != SECSuccess) {
00847        fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n");
00848        goto loser;
00849     }
00850     cinfo = NSS_CMSEncryptedData_GetContentInfo(encd);
00851     /* we're always passing data in, so the content is NULL */
00852     if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 
00853           != SECSuccess) {
00854        fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
00855        goto loser;
00856     }
00857     ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL,
00858                                dkcb, encryptOptions->bulkkey, NULL, NULL);
00859     if (!ecx) {
00860        fprintf(stderr, "%s: cannot create encoder context.\n", progName);
00861        goto loser;
00862     }
00863     rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data, 
00864                                     encryptOptions->input->len);
00865     if (rv) {
00866        fprintf(stderr, "%s: failed to add data to encoder.\n", progName);
00867        goto loser;
00868     }
00869     rv = NSS_CMSEncoder_Finish(ecx);
00870     if (rv) {
00871        fprintf(stderr, "%s: failed to encrypt data.\n", progName);
00872        goto loser;
00873     }
00874     fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile);
00875     /*
00876     if (bulkkey)
00877        PK11_FreeSymKey(bulkkey);
00878        */
00879     if (tmppoolp)
00880        PORT_FreeArena(tmppoolp, PR_FALSE);
00881     return cmsg;
00882 loser:
00883     /*
00884     if (bulkkey)
00885        PK11_FreeSymKey(bulkkey);
00886        */
00887     if (tmppoolp)
00888        PORT_FreeArena(tmppoolp, PR_FALSE);
00889     if (cmsg)
00890        NSS_CMSMessage_Destroy(cmsg);
00891     return NULL;
00892 }
00893 
00894 static NSSCMSMessage *
00895 signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions)
00896 {
00897     NSSCMSMessage *cmsg = NULL;
00898     NSSCMSContentInfo *cinfo;
00899     NSSCMSSignedData *sigd;
00900     CERTCertificate **certs = NULL;
00901     CERTCertDBHandle *dbhandle;
00902     PLArenaPool *tmppoolp = NULL;
00903     int i = 0, cnt;
00904     dbhandle = certsonlyOptions->options->certHandle;
00905     if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) {
00906        fprintf(stderr, 
00907         "ERROR: please indicate the nickname of a certificate to sign with.\n");
00908        goto loser;
00909     }
00910     if (!(tmppoolp = PORT_NewArena(1024))) {
00911        fprintf(stderr, "ERROR: out of memory.\n");
00912        goto loser;
00913     }
00914     if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) {
00915        fprintf(stderr, "ERROR: out of memory.\n");
00916        goto loser;
00917     }
00918     for (i=0; certsonlyOptions->recipients[i] != NULL; i++) {
00919        if ((certs[i] = 
00920              CERT_FindCertByNicknameOrEmailAddr(dbhandle,
00921                                              certsonlyOptions->recipients[i]))
00922                == NULL) {
00923            SECU_PrintError(progName, "cannot find certificate for \"%s\"", 
00924                            certsonlyOptions->recipients[i]);
00925            i=0;
00926            goto loser;
00927        }
00928     }
00929     certs[i] = NULL;
00930     i=0;
00931     /*
00932      * create the message object
00933      */
00934     cmsg = NSS_CMSMessage_Create(NULL);
00935     if (cmsg == NULL) {
00936        fprintf(stderr, "ERROR: cannot create CMS message.\n");
00937        goto loser;
00938     }
00939     /*
00940      * build chain of objects: message->signedData->data
00941      */
00942     if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE))
00943           == NULL) {
00944        fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
00945        goto loser;
00946     }
00947     CERT_DestroyCertificate(certs[0]);
00948     for (i=1; i<cnt; i++) {
00949        if (NSS_CMSSignedData_AddCertChain(sigd, certs[i])) {
00950            fprintf(stderr, "ERROR: cannot add cert chain for \"%s\".\n",
00951                    certsonlyOptions->recipients[i]);
00952            goto loser;
00953        }
00954        CERT_DestroyCertificate(certs[i]);
00955     }
00956     cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
00957     if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) 
00958           != SECSuccess) {
00959        fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
00960        goto loser;
00961     }
00962     cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
00963     if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) 
00964           != SECSuccess) {
00965        fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
00966        goto loser;
00967     }
00968     if (tmppoolp)
00969        PORT_FreeArena(tmppoolp, PR_FALSE);
00970     return cmsg;
00971 loser:
00972     if (certs) {
00973        for (; i<cnt; i++) {
00974            CERT_DestroyCertificate(certs[i]);
00975        }
00976     }
00977     if (cmsg)
00978        NSS_CMSMessage_Destroy(cmsg);
00979     if (tmppoolp)
00980        PORT_FreeArena(tmppoolp, PR_FALSE);
00981     return NULL;
00982 }
00983 
00984 static char *
00985 pl_fgets(char * buf, int size, PRFileDesc * fd)
00986 {
00987     char * bp = buf;
00988     int    nb = 0;;
00989 
00990     while (size > 1) {
00991        nb = PR_Read(fd, bp, 1);
00992        if (nb < 0) {
00993            /* deal with error */
00994            return NULL;
00995        } else if (nb == 0) {
00996            /* deal with EOF */
00997            return NULL;
00998        } else if (*bp == '\n') {
00999            /* deal with EOL */
01000            ++bp;  /* keep EOL character */
01001            break;
01002        } else {
01003            /* ordinary character */
01004            ++bp;
01005            --size;
01006        }
01007     }
01008     *bp = '\0';
01009     return buf;
01010 }
01011 
01012 typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode;
01013 
01014 static int 
01015 doBatchDecode(FILE *outFile, PRFileDesc *batchFile, 
01016               const struct decodeOptionsStr *decodeOptions)
01017 {
01018     char * str;
01019     int    exitStatus = 0;
01020     char   batchLine[512];
01021 
01022     while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) {
01023        NSSCMSMessage *cmsg = NULL;
01024        PRFileDesc *   inFile;
01025        int            len = strlen(str);
01026        SECStatus      rv;
01027        SECItem        input = {0, 0, 0};
01028        char           cc;
01029 
01030        while (len > 0 && 
01031               ((cc = str[len - 1]) == '\n' || cc == '\r')) {
01032            str[--len] = '\0';
01033        }
01034        if (!len) /* skip empty line */
01035            continue;
01036        if (str[0] == '#')
01037            continue;  /* skip comment line */
01038        fprintf(outFile, "========== %s ==========\n", str);
01039        inFile = PR_Open(str, PR_RDONLY, 00660);
01040        if (inFile == NULL) {
01041            fprintf(outFile, "%s: unable to open \"%s\" for reading\n",
01042                   progName, str);
01043            exitStatus = 1;
01044            continue;
01045        }
01046        rv = SECU_FileToItem(&input, inFile);
01047        PR_Close(inFile);
01048        if (rv != SECSuccess) {
01049            SECU_PrintError(progName, "unable to read infile");
01050            exitStatus = 1;
01051            continue;
01052        }
01053        cmsg = decode(outFile, &input, decodeOptions);
01054        SECITEM_FreeItem(&input, PR_FALSE);
01055        if (cmsg)
01056            NSS_CMSMessage_Destroy(cmsg);
01057        else {
01058            SECU_PrintError(progName, "problem decoding");
01059            exitStatus = 1;
01060        }
01061     }
01062     return exitStatus;
01063 }
01064 
01065 int
01066 main(int argc, char **argv)
01067 {
01068     FILE *outFile;
01069     NSSCMSMessage *cmsg = NULL;
01070     PRFileDesc *inFile;
01071     PLOptState *optstate;
01072     PLOptStatus status;
01073     Mode mode = UNKNOWN;
01074     struct decodeOptionsStr decodeOptions = { 0 };
01075     struct signOptionsStr signOptions = { 0 };
01076     struct envelopeOptionsStr envelopeOptions = { 0 };
01077     struct certsonlyOptionsStr certsonlyOptions = { 0 };
01078     struct encryptOptionsStr encryptOptions = { 0 };
01079     struct optionsStr options = { 0 };
01080     int exitstatus;
01081     static char *ptrarray[128] = { 0 };
01082     int nrecipients = 0;
01083     char *str, *tok;
01084     char *envFileName;
01085     SECItem input = { 0, 0, 0};
01086     SECItem envmsg = { 0, 0, 0 };
01087     SECStatus rv;
01088     PRFileDesc *contentFile = NULL;
01089     PRBool      batch = PR_FALSE;
01090 
01091 #ifdef NISCC_TEST
01092     const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST");
01093     PORT_Assert(ev); 
01094     ev = PR_GetEnv("NSS_STRICT_SHUTDOWN");
01095     PORT_Assert(ev); 
01096 #endif 
01097 
01098     progName = strrchr(argv[0], '/');
01099     if (!progName)
01100        progName = strrchr(argv[0], '\\');
01101     progName = progName ? progName+1 : argv[0];
01102 
01103     inFile = PR_STDIN;
01104     outFile = stdout;
01105     envFileName = NULL;
01106     mode = UNKNOWN;
01107     decodeOptions.content.data = NULL;
01108     decodeOptions.content.len  = 0;
01109     decodeOptions.suppressContent = PR_FALSE;
01110     decodeOptions.headerLevel = -1;
01111     decodeOptions.keepCerts = PR_FALSE;
01112     options.certUsage = certUsageEmailSigner;
01113     options.password = NULL;
01114     signOptions.nickname = NULL;
01115     signOptions.detached = PR_FALSE;
01116     signOptions.signingTime = PR_FALSE;
01117     signOptions.smimeProfile = PR_FALSE;
01118     signOptions.encryptionKeyPreferenceNick = NULL;
01119     signOptions.hashAlgTag = SEC_OID_SHA1;
01120     envelopeOptions.recipients = NULL;
01121     encryptOptions.recipients = NULL;
01122     encryptOptions.envmsg = NULL;
01123     encryptOptions.envFile = NULL;
01124     encryptOptions.bulkalgtag = SEC_OID_UNKNOWN;
01125     encryptOptions.bulkkey = NULL;
01126     encryptOptions.keysize = -1;
01127 
01128     /*
01129      * Parse command line arguments
01130      */
01131     optstate = PL_CreateOptState(argc, argv, 
01132                              "CDEGH:N:OPSTY:bc:d:e:h:i:kno:p:r:s:u:v");
01133     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
01134        switch (optstate->option) {
01135        case 'C':
01136            mode = ENCRYPT;
01137            break;
01138        case 'D':
01139            mode = DECODE;
01140            break;
01141        case 'E':
01142            mode = ENVELOPE;
01143            break;
01144        case 'G':
01145            if (mode != SIGN) {
01146               fprintf(stderr, 
01147                       "%s: option -G only supported with option -S.\n", 
01148                       progName);
01149               Usage(progName);
01150               exit(1);
01151            }
01152            signOptions.signingTime = PR_TRUE;
01153            break;
01154        case 'H':
01155            if (mode != SIGN) {
01156                fprintf(stderr,
01157                        "%s: option -H only supported with option -S.\n",
01158                        progName);
01159                Usage(progName);
01160                exit(1);
01161            }
01162            decodeOptions.suppressContent = PR_TRUE;
01163            if (!strcmp(optstate->value, "MD2"))
01164                signOptions.hashAlgTag = SEC_OID_MD2;
01165            else if (!strcmp(optstate->value, "MD4"))
01166                signOptions.hashAlgTag = SEC_OID_MD4;
01167            else if (!strcmp(optstate->value, "MD5"))
01168                signOptions.hashAlgTag = SEC_OID_MD5;
01169            else if (!strcmp(optstate->value, "SHA1"))
01170                signOptions.hashAlgTag = SEC_OID_SHA1;
01171            else if (!strcmp(optstate->value, "SHA256"))
01172                signOptions.hashAlgTag = SEC_OID_SHA256;
01173            else if (!strcmp(optstate->value, "SHA384"))
01174                signOptions.hashAlgTag = SEC_OID_SHA384;
01175            else if (!strcmp(optstate->value, "SHA512"))
01176                signOptions.hashAlgTag = SEC_OID_SHA512;
01177            else {
01178                fprintf(stderr,
01179            "%s: -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512\n",
01180                        progName);
01181                exit(1);
01182            }
01183            break;
01184        case 'N':
01185            if (mode != SIGN) {
01186               fprintf(stderr, 
01187                       "%s: option -N only supported with option -S.\n", 
01188                       progName);
01189               Usage(progName);
01190               exit(1);
01191            }
01192            signOptions.nickname = strdup(optstate->value);
01193            break;
01194        case 'O':
01195            mode = CERTSONLY;
01196            break;
01197        case 'P':
01198            if (mode != SIGN) {
01199               fprintf(stderr, 
01200                       "%s: option -P only supported with option -S.\n", 
01201                       progName);
01202               Usage(progName);
01203               exit(1);
01204            }
01205            signOptions.smimeProfile = PR_TRUE;
01206            break;
01207        case 'S':
01208            mode = SIGN;
01209            break;
01210        case 'T':
01211            if (mode != SIGN) {
01212               fprintf(stderr, 
01213                       "%s: option -T only supported with option -S.\n", 
01214                       progName);
01215               Usage(progName);
01216               exit(1);
01217            }
01218            signOptions.detached = PR_TRUE;
01219            break;
01220        case 'Y':
01221            if (mode != SIGN) {
01222               fprintf(stderr, 
01223                       "%s: option -Y only supported with option -S.\n", 
01224                       progName);
01225               Usage(progName);
01226               exit(1);
01227            }
01228            signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
01229            break;
01230 
01231        case 'b':
01232            if (mode != DECODE) {
01233               fprintf(stderr, 
01234                       "%s: option -b only supported with option -D.\n", 
01235                       progName);
01236               Usage(progName);
01237               exit(1);
01238            }
01239            batch = PR_TRUE;
01240            break;
01241 
01242        case 'c':
01243            if (mode != DECODE) {
01244               fprintf(stderr, 
01245                       "%s: option -c only supported with option -D.\n", 
01246                       progName);
01247               Usage(progName);
01248               exit(1);
01249            }
01250            contentFile = PR_Open(optstate->value, PR_RDONLY, 006600);
01251            if (contentFile == NULL) {
01252               fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
01253                      progName, optstate->value);
01254               exit(1);
01255            }
01256 
01257            rv = SECU_FileToItem(&decodeOptions.content, contentFile);
01258            PR_Close(contentFile);
01259            if (rv != SECSuccess) {
01260               SECU_PrintError(progName, "problem reading content file");
01261               exit(1);
01262            }
01263            if (!decodeOptions.content.data) {
01264               /* file was zero length */
01265               decodeOptions.content.data = (unsigned char *)PORT_Strdup("");
01266               decodeOptions.content.len  = 0;
01267            }
01268 
01269            break;
01270        case 'd':
01271            SECU_ConfigDirectory(optstate->value);
01272            break;
01273        case 'e':
01274            envFileName = strdup(optstate->value);
01275            encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660);
01276            break;
01277 
01278        case 'h':
01279            if (mode != DECODE) {
01280               fprintf(stderr, 
01281                       "%s: option -h only supported with option -D.\n", 
01282                       progName);
01283               Usage(progName);
01284               exit(1);
01285            }
01286            decodeOptions.headerLevel = atoi(optstate->value);
01287            if (decodeOptions.headerLevel < 0) {
01288               fprintf(stderr, "option -h cannot have a negative value.\n");
01289               exit(1);
01290            }
01291            break;
01292        case 'i':
01293            if (!optstate->value) {
01294                fprintf(stderr, "-i option requires filename argument\n");
01295                exit(1);
01296            }
01297            inFile = PR_Open(optstate->value, PR_RDONLY, 00660);
01298            if (inFile == NULL) {
01299               fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
01300                      progName, optstate->value);
01301               exit(1);
01302            }
01303            break;
01304 
01305        case 'k':
01306            if (mode != DECODE) {
01307               fprintf(stderr, 
01308                       "%s: option -k only supported with option -D.\n", 
01309                       progName);
01310               Usage(progName);
01311               exit(1);
01312            }
01313            decodeOptions.keepCerts = PR_TRUE;
01314            break;
01315 
01316        case 'n':
01317            if (mode != DECODE) {
01318               fprintf(stderr, 
01319                       "%s: option -n only supported with option -D.\n", 
01320                       progName);
01321               Usage(progName);
01322               exit(1);
01323            }
01324            decodeOptions.suppressContent = PR_TRUE;
01325            break;
01326        case 'o':
01327            outFile = fopen(optstate->value, "wb");
01328            if (outFile == NULL) {
01329               fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
01330                      progName, optstate->value);
01331               exit(1);
01332            }
01333            break;
01334        case 'p':
01335            if (!optstate->value) {
01336               fprintf(stderr, "%s: option -p must have a value.\n", progName);
01337               Usage(progName);
01338               exit(1);
01339            }
01340               
01341            options.password = strdup(optstate->value);
01342            break;
01343 
01344        case 'r':
01345            if (!optstate->value) {
01346               fprintf(stderr, "%s: option -r must have a value.\n", progName);
01347               Usage(progName);
01348               exit(1);
01349            }
01350            envelopeOptions.recipients = ptrarray;
01351            str = (char *)optstate->value;
01352            do {
01353               tok = strchr(str, ',');
01354               if (tok) *tok = '\0';
01355               envelopeOptions.recipients[nrecipients++] = strdup(str);
01356               if (tok) str = tok + 1;
01357            } while (tok);
01358            envelopeOptions.recipients[nrecipients] = NULL;
01359            encryptOptions.recipients = envelopeOptions.recipients;
01360            certsonlyOptions.recipients = envelopeOptions.recipients;
01361            break;
01362 
01363        case 'u': {
01364            int usageType;
01365 
01366            usageType = atoi (strdup(optstate->value));
01367            if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
01368               return -1;
01369            options.certUsage = (SECCertUsage)usageType;
01370            break;
01371          }
01372        case 'v':
01373            cms_verbose = 1;
01374            break;
01375 
01376        }
01377     }
01378     if (status == PL_OPT_BAD)
01379        Usage(progName);
01380     PL_DestroyOptState(optstate);
01381 
01382     if (mode == UNKNOWN)
01383        Usage(progName);
01384 
01385     if (mode != CERTSONLY && !batch) {
01386        rv = SECU_FileToItem(&input, inFile);
01387        if (rv != SECSuccess) {
01388            SECU_PrintError(progName, "unable to read infile");
01389            exit(1);
01390        }
01391        if (inFile != PR_STDIN) {
01392            PR_Close(inFile);
01393        }
01394     }
01395     if (cms_verbose) {
01396        fprintf(stderr, "received commands\n");
01397     }
01398 
01399     /* Call the libsec initialization routines */
01400     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
01401     rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL));
01402     if (SECSuccess != rv) {
01403        SECU_PrintError(progName, "NSS_Init failed");
01404        exit(1);
01405     }
01406     if (cms_verbose) {
01407        fprintf(stderr, "NSS has been initialized.\n");
01408     }
01409     options.certHandle = CERT_GetDefaultCertDB();
01410     if (!options.certHandle) {
01411        SECU_PrintError(progName, "No default cert DB");
01412        exit(1);
01413     }
01414     if (cms_verbose) {
01415        fprintf(stderr, "Got default certdb\n");
01416     }
01417     if (options.password)
01418     {
01419        pwdata.source = PW_PLAINTEXT;
01420        pwdata.data = options.password;
01421     }
01422     pwcb = SECU_GetModulePassword;
01423     pwcb_arg = (void *)&pwdata;
01424 
01425     PK11_SetPasswordFunc(&SECU_GetModulePassword);
01426 
01427 
01428 #if defined(_WIN32)
01429     if (outFile == stdout) {
01430        /* If we're going to write binary data to stdout, we must put stdout
01431        ** into O_BINARY mode or else outgoing \n's will become \r\n's.
01432        */
01433        int smrv = _setmode(_fileno(stdout), _O_BINARY);
01434        if (smrv == -1) {
01435            fprintf(stderr,
01436            "%s: Cannot change stdout to binary mode. Use -o option instead.\n",
01437                    progName);
01438            return smrv;
01439        }
01440     }
01441 #endif
01442 
01443     exitstatus = 0;
01444     switch (mode) {
01445     case DECODE:       /* -D */
01446        decodeOptions.options = &options;
01447        if (encryptOptions.envFile) {
01448            /* Decoding encrypted-data, so get the bulkkey from an
01449             * enveloped-data message.
01450             */
01451            SECU_FileToItem(&envmsg, encryptOptions.envFile);
01452            decodeOptions.options = &options;
01453            encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
01454            if (!encryptOptions.envmsg) {
01455               SECU_PrintError(progName, "problem decoding env msg");
01456               exitstatus = 1;
01457               break;
01458            }
01459            rv = get_enc_params(&encryptOptions);
01460            decodeOptions.dkcb = dkcb;
01461            decodeOptions.bulkkey = encryptOptions.bulkkey;
01462        }
01463        if (!batch) {
01464            cmsg = decode(outFile, &input, &decodeOptions);
01465            if (!cmsg) {
01466               SECU_PrintError(progName, "problem decoding");
01467               exitstatus = 1;
01468            }
01469        } else {
01470            exitstatus = doBatchDecode(outFile, inFile, &decodeOptions);
01471            if (inFile != PR_STDIN) {
01472               PR_Close(inFile);
01473            }
01474        }
01475        break;
01476     case SIGN:         /* -S */
01477        signOptions.options = &options;
01478        cmsg = signed_data(&signOptions);
01479        if (!cmsg) {
01480            SECU_PrintError(progName, "problem signing");
01481            exitstatus = 1;
01482        }
01483        break;
01484     case ENCRYPT:      /* -C */
01485        if (!envFileName) {
01486            fprintf(stderr, "%s: you must specify an envelope file with -e.\n",
01487                    progName);
01488            exit(1);
01489        }
01490        encryptOptions.options = &options;
01491        encryptOptions.input = &input;
01492        encryptOptions.outfile = outFile;
01493        /* decode an enveloped-data message to get the bulkkey (create
01494         * a new one if neccessary)
01495         */
01496        if (!encryptOptions.envFile) {
01497            encryptOptions.envFile = PR_Open(envFileName, 
01498                                             PR_WRONLY|PR_CREATE_FILE, 00660);
01499            if (!encryptOptions.envFile) {
01500               fprintf(stderr, "%s: failed to create file %s.\n", progName,
01501                       envFileName);
01502               exit(1);
01503            }
01504        } else {
01505            SECU_FileToItem(&envmsg, encryptOptions.envFile);
01506            decodeOptions.options = &options;
01507            encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions);
01508            if (encryptOptions.envmsg == NULL) {
01509               SECU_PrintError(progName, "problem decrypting env msg");
01510               exitstatus = 1;
01511               break;
01512            }
01513        }
01514        rv = get_enc_params(&encryptOptions);
01515        /* create the encrypted-data message */
01516        cmsg = encrypted_data(&encryptOptions);
01517        if (!cmsg) {
01518            SECU_PrintError(progName, "problem encrypting");
01519            exitstatus = 1;
01520        }
01521        if (encryptOptions.bulkkey) {
01522            PK11_FreeSymKey(encryptOptions.bulkkey);
01523            encryptOptions.bulkkey = NULL;
01524        }
01525        break;
01526     case ENVELOPE:     /* -E */
01527        envelopeOptions.options = &options;
01528        cmsg = enveloped_data(&envelopeOptions);
01529        if (!cmsg) {
01530            SECU_PrintError(progName, "problem enveloping");
01531            exitstatus = 1;
01532        }
01533        break;
01534     case CERTSONLY:    /* -O */
01535        certsonlyOptions.options = &options;
01536        cmsg = signed_data_certsonly(&certsonlyOptions);
01537        if (!cmsg) {
01538            SECU_PrintError(progName, "problem with certs-only");
01539            exitstatus = 1;
01540        }
01541        break;
01542     default:
01543        fprintf(stderr, "One of options -D, -S or -E must be set.\n");
01544        Usage(progName);
01545        exitstatus = 1;
01546     }
01547     if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY)
01548          && (!exitstatus) ) {
01549        PLArenaPool *arena = PORT_NewArena(1024);
01550        NSSCMSEncoderContext *ecx;
01551        SECItem output = { 0, 0, 0 };
01552 
01553        if (!arena) {
01554            fprintf(stderr, "%s: out of memory.\n", progName);
01555            exit(1);
01556        }
01557 
01558        if (cms_verbose) {
01559            fprintf(stderr, "cmsg [%p]\n", cmsg);
01560            fprintf(stderr, "arena [%p]\n", arena);
01561            if (pwcb_arg)
01562               fprintf(stderr, "password [%s]\n", (char *)pwcb_arg);
01563            else
01564               fprintf(stderr, "password [NULL]\n");
01565        }
01566        ecx = NSS_CMSEncoder_Start(cmsg, 
01567                                    NULL, NULL,     /* DER output callback  */
01568                                    &output, arena, /* destination storage  */
01569                                    pwcb, pwcb_arg, /* password callback    */
01570                                    NULL, NULL,     /* decrypt key callback */
01571                                    NULL, NULL );   /* detached digests    */
01572        if (!ecx) {
01573            fprintf(stderr, "%s: cannot create encoder context.\n", progName);
01574            exit(1);
01575        }
01576        if (cms_verbose) {
01577            fprintf(stderr, "input len [%d]\n", input.len);
01578            { unsigned int j; 
01579               for(j=0;j<input.len;j++)
01580             fprintf(stderr, "%2x%c", input.data[j], (j>0&&j%35==0)?'\n':' ');
01581            }
01582        }
01583        if (input.len > 0) { /* skip if certs-only (or other zero content) */
01584            rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len);
01585            if (rv) {
01586               fprintf(stderr, 
01587                       "%s: failed to add data to encoder.\n", progName);
01588               exit(1);
01589            }
01590        }
01591        rv = NSS_CMSEncoder_Finish(ecx);
01592        if (rv) {
01593             SECU_PrintError(progName, "failed to encode data");
01594            exit(1);
01595        }
01596 
01597        if (cms_verbose) {
01598            fprintf(stderr, "encoding passed\n");
01599        }
01600        fwrite(output.data, output.len, 1, outFile);
01601        if (cms_verbose) {
01602            fprintf(stderr, "wrote to file\n");
01603        }
01604        PORT_FreeArena(arena, PR_FALSE);
01605     }
01606     if (cmsg)
01607        NSS_CMSMessage_Destroy(cmsg);
01608     if (outFile != stdout)
01609        fclose(outFile);
01610 
01611     SECITEM_FreeItem(&decodeOptions.content, PR_FALSE);
01612     SECITEM_FreeItem(&envmsg, PR_FALSE);
01613     SECITEM_FreeItem(&input, PR_FALSE);
01614     if (NSS_Shutdown() != SECSuccess) {
01615        SECU_PrintError(progName, "NSS_Shutdown failed");
01616        exitstatus = 1;
01617     }
01618     PR_Cleanup();
01619     return exitstatus;
01620 }