tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cmsutil.c (60384B)


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