michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * cmsutil -- A command to work with CMS data michael@0: */ michael@0: michael@0: #include "nspr.h" michael@0: #include "secutil.h" michael@0: #include "plgetopt.h" michael@0: #include "secpkcs7.h" michael@0: #include "cert.h" michael@0: #include "certdb.h" michael@0: #include "secoid.h" michael@0: #include "cms.h" michael@0: #include "nss.h" michael@0: #include "smime.h" michael@0: #include "pk11func.h" michael@0: michael@0: #if defined(XP_UNIX) michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(_WIN32) michael@0: #include "fcntl.h" michael@0: #include "io.h" michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: michael@0: char *progName = NULL; michael@0: static int cms_verbose = 0; michael@0: static secuPWData pwdata = { PW_NONE, 0 }; michael@0: static PK11PasswordFunc pwcb = NULL; michael@0: static void *pwcb_arg = NULL; michael@0: michael@0: michael@0: /* XXX stolen from cmsarray.c michael@0: * nss_CMSArray_Count - count number of elements in array michael@0: */ michael@0: int michael@0: nss_CMSArray_Count(void **array) michael@0: { michael@0: int n = 0; michael@0: if (array == NULL) michael@0: return 0; michael@0: while (*array++ != NULL) michael@0: n++; michael@0: return n; michael@0: } michael@0: michael@0: static SECStatus michael@0: DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input, michael@0: SECAlgorithmID **algids) michael@0: { michael@0: NSSCMSDigestContext *digcx; michael@0: SECStatus rv; michael@0: michael@0: digcx = NSS_CMSDigestContext_StartMultiple(algids); michael@0: if (digcx == NULL) michael@0: return SECFailure; michael@0: michael@0: NSS_CMSDigestContext_Update(digcx, input->data, input->len); michael@0: michael@0: rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests); michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: static void michael@0: Usage(char *progName) michael@0: { michael@0: fprintf(stderr, michael@0: "Usage: %s [-C|-D|-E|-O|-S] [] [-d dbdir] [-u certusage]\n" michael@0: " -C create a CMS encrypted data message\n" michael@0: " -D decode a CMS message\n" michael@0: " -b decode a batch of files named in infile\n" michael@0: " -c content use this detached content\n" michael@0: " -n suppress output of content\n" michael@0: " -h num display num levels of CMS message info as email headers\n" michael@0: " -k keep decoded encryption certs in perm cert db\n" michael@0: " -E create a CMS enveloped data message\n" michael@0: " -r id,... create envelope for these recipients,\n" michael@0: " where id can be a certificate nickname or email address\n" michael@0: " -S create a CMS signed data message\n" michael@0: " -G include a signing time attribute\n" michael@0: " -H hash use hash (default:SHA1)\n" michael@0: " -N nick use certificate named \"nick\" for signing\n" michael@0: " -P include a SMIMECapabilities attribute\n" michael@0: " -T do not include content in CMS message\n" michael@0: " -Y nick include a EncryptionKeyPreference attribute with cert\n" michael@0: " (use \"NONE\" to omit)\n" michael@0: " -O create a CMS signed message containing only certificates\n" michael@0: " General Options:\n" michael@0: " -d dbdir key/cert database directory (default: ~/.netscape)\n" michael@0: " -e envelope enveloped data message in this file is used for bulk key\n" michael@0: " -i infile use infile as source of data (default: stdin)\n" michael@0: " -o outfile use outfile as destination of data (default: stdout)\n" michael@0: " -p password use password as key db password (default: prompt)\n" michael@0: " -f pwfile use password file to set password on all PKCS#11 tokens)\n" michael@0: " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n" michael@0: " -v print debugging information\n" michael@0: "\n" michael@0: "Cert usage codes:\n", michael@0: progName); michael@0: fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " "); michael@0: fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " "); michael@0: fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " "); michael@0: fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " "); michael@0: fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " "); michael@0: fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " "); michael@0: fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " "); michael@0: fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " "); michael@0: fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " "); michael@0: fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " "); michael@0: fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " "); michael@0: fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " "); michael@0: michael@0: exit(-1); michael@0: } michael@0: michael@0: struct optionsStr { michael@0: char *pwfile; michael@0: char *password; michael@0: SECCertUsage certUsage; michael@0: CERTCertDBHandle *certHandle; michael@0: }; michael@0: michael@0: struct decodeOptionsStr { michael@0: struct optionsStr *options; michael@0: SECItem content; michael@0: int headerLevel; michael@0: PRBool suppressContent; michael@0: NSSCMSGetDecryptKeyCallback dkcb; michael@0: PK11SymKey *bulkkey; michael@0: PRBool keepCerts; michael@0: }; michael@0: michael@0: struct signOptionsStr { michael@0: struct optionsStr *options; michael@0: char *nickname; michael@0: char *encryptionKeyPreferenceNick; michael@0: PRBool signingTime; michael@0: PRBool smimeProfile; michael@0: PRBool detached; michael@0: SECOidTag hashAlgTag; michael@0: }; michael@0: michael@0: struct envelopeOptionsStr { michael@0: struct optionsStr *options; michael@0: char **recipients; michael@0: }; michael@0: michael@0: struct certsonlyOptionsStr { michael@0: struct optionsStr *options; michael@0: char **recipients; michael@0: }; michael@0: michael@0: struct encryptOptionsStr { michael@0: struct optionsStr *options; michael@0: char **recipients; michael@0: NSSCMSMessage *envmsg; michael@0: SECItem *input; michael@0: FILE *outfile; michael@0: PRFileDesc *envFile; michael@0: PK11SymKey *bulkkey; michael@0: SECOidTag bulkalgtag; michael@0: int keysize; michael@0: }; michael@0: michael@0: static NSSCMSMessage * michael@0: decode(FILE *out, SECItem *input, const struct decodeOptionsStr *decodeOptions) michael@0: { michael@0: NSSCMSDecoderContext *dcx; michael@0: SECStatus rv; michael@0: NSSCMSMessage *cmsg; michael@0: int nlevels, i; michael@0: SECItem sitem = { 0, 0, 0 }; michael@0: michael@0: PORT_SetError(0); michael@0: dcx = NSS_CMSDecoder_Start(NULL, michael@0: NULL, NULL, /* content callback */ michael@0: pwcb, pwcb_arg, /* password callback */ michael@0: decodeOptions->dkcb, /* decrypt key callback */ michael@0: decodeOptions->bulkkey); michael@0: if (dcx == NULL) { michael@0: fprintf(stderr, "%s: failed to set up message decoder.\n", progName); michael@0: return NULL; michael@0: } michael@0: rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len); michael@0: if (rv != SECSuccess) { michael@0: fprintf(stderr, "%s: failed to decode message.\n", progName); michael@0: NSS_CMSDecoder_Cancel(dcx); michael@0: return NULL; michael@0: } michael@0: cmsg = NSS_CMSDecoder_Finish(dcx); michael@0: if (cmsg == NULL) { michael@0: fprintf(stderr, "%s: failed to decode message.\n", progName); michael@0: return NULL; michael@0: } michael@0: michael@0: if (decodeOptions->headerLevel >= 0) { michael@0: /*fprintf(out, "SMIME: ", decodeOptions->headerLevel, i);*/ michael@0: fprintf(out, "SMIME: "); michael@0: } michael@0: michael@0: nlevels = NSS_CMSMessage_ContentLevelCount(cmsg); michael@0: for (i = 0; i < nlevels; i++) { michael@0: NSSCMSContentInfo *cinfo; michael@0: SECOidTag typetag; michael@0: michael@0: cinfo = NSS_CMSMessage_ContentLevel(cmsg, i); michael@0: typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "\tlevel=%d.%d; ", decodeOptions->headerLevel, nlevels - i); michael@0: michael@0: switch (typetag) { michael@0: case SEC_OID_PKCS7_SIGNED_DATA: michael@0: { michael@0: NSSCMSSignedData *sigd = NULL; michael@0: SECItem **digests; michael@0: int nsigners; michael@0: int j; michael@0: michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "type=signedData; "); michael@0: sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo); michael@0: if (sigd == NULL) { michael@0: SECU_PrintError(progName, "signedData component missing"); michael@0: goto loser; michael@0: } michael@0: michael@0: /* if we have a content file, but no digests for this signedData */ michael@0: if (decodeOptions->content.data != NULL && michael@0: !NSS_CMSSignedData_HasDigests(sigd)) { michael@0: PLArenaPool *poolp; michael@0: SECAlgorithmID **digestalgs; michael@0: michael@0: /* detached content: grab content file */ michael@0: sitem = decodeOptions->content; michael@0: michael@0: if ((poolp = PORT_NewArena(1024)) == NULL) { michael@0: fprintf(stderr, "cmsutil: Out of memory.\n"); michael@0: goto loser; michael@0: } michael@0: digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd); michael@0: if (DigestFile (poolp, &digests, &sitem, digestalgs) michael@0: != SECSuccess) { michael@0: SECU_PrintError(progName, michael@0: "problem computing message digest"); michael@0: PORT_FreeArena(poolp, PR_FALSE); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) michael@0: != SECSuccess) { michael@0: SECU_PrintError(progName, michael@0: "problem setting message digests"); michael@0: PORT_FreeArena(poolp, PR_FALSE); michael@0: goto loser; michael@0: } michael@0: PORT_FreeArena(poolp, PR_FALSE); michael@0: } michael@0: michael@0: /* import the certificates */ michael@0: if (NSS_CMSSignedData_ImportCerts(sigd, michael@0: decodeOptions->options->certHandle, michael@0: decodeOptions->options->certUsage, michael@0: decodeOptions->keepCerts) michael@0: != SECSuccess) { michael@0: SECU_PrintError(progName, "cert import failed"); michael@0: goto loser; michael@0: } michael@0: michael@0: /* find out about signers */ michael@0: nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "nsigners=%d; ", nsigners); michael@0: if (nsigners == 0) { michael@0: /* Might be a cert transport message michael@0: ** or might be an invalid message, such as a QA test message michael@0: ** or a message from an attacker. michael@0: */ michael@0: SECStatus rv; michael@0: rv = NSS_CMSSignedData_VerifyCertsOnly(sigd, michael@0: decodeOptions->options->certHandle, michael@0: decodeOptions->options->certUsage); michael@0: if (rv != SECSuccess) { michael@0: fprintf(stderr, "cmsutil: Verify certs-only failed!\n"); michael@0: goto loser; michael@0: } michael@0: return cmsg; michael@0: } michael@0: michael@0: /* still no digests? */ michael@0: if (!NSS_CMSSignedData_HasDigests(sigd)) { michael@0: SECU_PrintError(progName, "no message digests"); michael@0: goto loser; michael@0: } michael@0: michael@0: for (j = 0; j < nsigners; j++) { michael@0: const char * svs; michael@0: NSSCMSSignerInfo *si; michael@0: NSSCMSVerificationStatus vs; michael@0: SECStatus bad; michael@0: michael@0: si = NSS_CMSSignedData_GetSignerInfo(sigd, j); michael@0: if (decodeOptions->headerLevel >= 0) { michael@0: char *signercn; michael@0: static char empty[] = { "" }; michael@0: michael@0: signercn = NSS_CMSSignerInfo_GetSignerCommonName(si); michael@0: if (signercn == NULL) michael@0: signercn = empty; michael@0: fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn); michael@0: if (signercn != empty) michael@0: PORT_Free(signercn); michael@0: } michael@0: bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j, michael@0: decodeOptions->options->certHandle, michael@0: decodeOptions->options->certUsage); michael@0: vs = NSS_CMSSignerInfo_GetVerificationStatus(si); michael@0: svs = NSS_CMSUtil_VerificationStatusToString(vs); michael@0: if (decodeOptions->headerLevel >= 0) { michael@0: fprintf(out, "signer%d.status=%s; ", j, svs); michael@0: /* goto loser ? */ michael@0: } else if (bad && out) { michael@0: fprintf(stderr, "signer %d status = %s\n", j, svs); michael@0: goto loser; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: case SEC_OID_PKCS7_ENVELOPED_DATA: michael@0: { michael@0: NSSCMSEnvelopedData *envd; michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "type=envelopedData; "); michael@0: envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo); michael@0: if (envd == NULL) { michael@0: SECU_PrintError(progName, "envelopedData component missing"); michael@0: goto loser; michael@0: } michael@0: } michael@0: break; michael@0: case SEC_OID_PKCS7_ENCRYPTED_DATA: michael@0: { michael@0: NSSCMSEncryptedData *encd; michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "type=encryptedData; "); michael@0: encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo); michael@0: if (encd == NULL) { michael@0: SECU_PrintError(progName, "encryptedData component missing"); michael@0: goto loser; michael@0: } michael@0: } michael@0: break; michael@0: case SEC_OID_PKCS7_DATA: michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "type=data; "); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (decodeOptions->headerLevel >= 0) michael@0: fprintf(out, "\n"); michael@0: } michael@0: michael@0: if (!decodeOptions->suppressContent && out) { michael@0: SECItem *item = (sitem.data ? &sitem michael@0: : NSS_CMSMessage_GetContent(cmsg)); michael@0: if (item && item->data && item->len) { michael@0: fwrite(item->data, item->len, 1, out); michael@0: } michael@0: } michael@0: return cmsg; michael@0: michael@0: loser: michael@0: if (cmsg) michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: return NULL; michael@0: } michael@0: michael@0: /* example of a callback function to use with encoder */ michael@0: /* michael@0: static void michael@0: writeout(void *arg, const char *buf, unsigned long len) michael@0: { michael@0: FILE *f = (FILE *)arg; michael@0: michael@0: if (f != NULL && buf != NULL) michael@0: (void)fwrite(buf, len, 1, f); michael@0: } michael@0: */ michael@0: michael@0: static NSSCMSMessage * michael@0: signed_data(struct signOptionsStr *signOptions) michael@0: { michael@0: NSSCMSMessage *cmsg = NULL; michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSSignedData *sigd; michael@0: NSSCMSSignerInfo *signerinfo; michael@0: CERTCertificate *cert= NULL, *ekpcert = NULL; michael@0: michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "Input to signed_data:\n"); michael@0: if (signOptions->options->password) michael@0: fprintf(stderr, "password [%s]\n", signOptions->options->password); michael@0: else if (signOptions->options->pwfile) michael@0: fprintf(stderr, "password file [%s]\n", signOptions->options->pwfile); michael@0: else michael@0: fprintf(stderr, "password [NULL]\n"); michael@0: fprintf(stderr, "certUsage [%d]\n", signOptions->options->certUsage); michael@0: if (signOptions->options->certHandle) michael@0: fprintf(stderr, "certdb [%p]\n", signOptions->options->certHandle); michael@0: else michael@0: fprintf(stderr, "certdb [NULL]\n"); michael@0: if (signOptions->nickname) michael@0: fprintf(stderr, "nickname [%s]\n", signOptions->nickname); michael@0: else michael@0: fprintf(stderr, "nickname [NULL]\n"); michael@0: } michael@0: if (signOptions->nickname == NULL) { michael@0: fprintf(stderr, michael@0: "ERROR: please indicate the nickname of a certificate to sign with.\n"); michael@0: return NULL; michael@0: } michael@0: if ((cert = CERT_FindUserCertByUsage(signOptions->options->certHandle, michael@0: signOptions->nickname, michael@0: signOptions->options->certUsage, michael@0: PR_FALSE, michael@0: &pwdata)) == NULL) { michael@0: SECU_PrintError(progName, michael@0: "the corresponding cert for key \"%s\" does not exist", michael@0: signOptions->nickname); michael@0: return NULL; michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "Found certificate for %s\n", signOptions->nickname); michael@0: } michael@0: /* michael@0: * create the message object michael@0: */ michael@0: cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ michael@0: if (cmsg == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS message.\n"); michael@0: return NULL; michael@0: } michael@0: /* michael@0: * build chain of objects: message->signedData->data michael@0: */ michael@0: if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSSignedData_GetContentInfo(sigd); michael@0: /* we're always passing data in and detaching optionally */ michael@0: if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, michael@0: signOptions->detached) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * create & attach signer information michael@0: */ michael@0: signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, signOptions->hashAlgTag); michael@0: if (signerinfo == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n"); michael@0: goto loser; michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, michael@0: "Created CMS message, added signed data w/ signerinfo\n"); michael@0: } michael@0: /* we want the cert chain included for this one */ michael@0: if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, michael@0: signOptions->options->certUsage) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot find cert chain.\n"); michael@0: goto loser; michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "imported certificate\n"); michael@0: } michael@0: if (signOptions->signingTime) { michael@0: if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add signingTime attribute.\n"); michael@0: goto loser; michael@0: } michael@0: } michael@0: if (signOptions->smimeProfile) { michael@0: if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n"); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if (!signOptions->encryptionKeyPreferenceNick) { michael@0: /* check signing cert for fitness as encryption cert */ michael@0: SECStatus FitForEncrypt = CERT_CheckCertUsage(cert, michael@0: certUsageEmailRecipient); michael@0: michael@0: if (SECSuccess == FitForEncrypt) { michael@0: /* if yes, add signing cert as EncryptionKeyPreference */ michael@0: if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, cert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, michael@0: "ERROR: cannot add default SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, cert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, michael@0: "ERROR: cannot add default MS SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: } else { michael@0: /* this is a dual-key cert case, we need to look for the encryption michael@0: certificate under the same nickname as the signing cert */ michael@0: /* get the cert, add it to the message */ michael@0: if ((ekpcert = CERT_FindUserCertByUsage( michael@0: signOptions->options->certHandle, michael@0: signOptions->nickname, michael@0: certUsageEmailRecipient, michael@0: PR_FALSE, michael@0: &pwdata)) == NULL) { michael@0: SECU_PrintError(progName, michael@0: "the corresponding cert for key \"%s\" does not exist", michael@0: signOptions->encryptionKeyPreferenceNick); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, michael@0: "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, michael@0: "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); michael@0: goto loser; michael@0: } michael@0: } michael@0: } else if (PL_strcmp(signOptions->encryptionKeyPreferenceNick, "NONE") == 0) { michael@0: /* No action */ michael@0: } else { michael@0: /* get the cert, add it to the message */ michael@0: if ((ekpcert = CERT_FindUserCertByUsage( michael@0: signOptions->options->certHandle, michael@0: signOptions->encryptionKeyPreferenceNick, michael@0: certUsageEmailRecipient, PR_FALSE, &pwdata)) michael@0: == NULL) { michael@0: SECU_PrintError(progName, michael@0: "the corresponding cert for key \"%s\" does not exist", michael@0: signOptions->encryptionKeyPreferenceNick); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ekpcert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ekpcert, michael@0: signOptions->options->certHandle) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add MS SMIMEEncKeyPrefs attribute.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add encryption certificate.\n"); michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n"); michael@0: goto loser; michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "created signed-data message\n"); michael@0: } michael@0: if (ekpcert) { michael@0: CERT_DestroyCertificate(ekpcert); michael@0: } michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: return cmsg; michael@0: loser: michael@0: if (ekpcert) { michael@0: CERT_DestroyCertificate(ekpcert); michael@0: } michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: return NULL; michael@0: } michael@0: michael@0: static NSSCMSMessage * michael@0: enveloped_data(struct envelopeOptionsStr *envelopeOptions) michael@0: { michael@0: NSSCMSMessage *cmsg = NULL; michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSEnvelopedData *envd; michael@0: NSSCMSRecipientInfo *recipientinfo; michael@0: CERTCertificate **recipientcerts = NULL; michael@0: CERTCertDBHandle *dbhandle; michael@0: PLArenaPool *tmppoolp = NULL; michael@0: SECOidTag bulkalgtag; michael@0: int keysize, i = 0; michael@0: int cnt; michael@0: dbhandle = envelopeOptions->options->certHandle; michael@0: /* count the recipients */ michael@0: if ((cnt = nss_CMSArray_Count((void **)envelopeOptions->recipients)) == 0) { michael@0: fprintf(stderr, "ERROR: please name at least one recipient.\n"); michael@0: goto loser; michael@0: } michael@0: if ((tmppoolp = PORT_NewArena (1024)) == NULL) { michael@0: fprintf(stderr, "ERROR: out of memory.\n"); michael@0: goto loser; michael@0: } michael@0: /* XXX find the recipient's certs by email address or nickname */ michael@0: if ((recipientcerts = michael@0: (CERTCertificate **)PORT_ArenaZAlloc(tmppoolp, michael@0: (cnt+1)*sizeof(CERTCertificate*))) michael@0: == NULL) { michael@0: fprintf(stderr, "ERROR: out of memory.\n"); michael@0: goto loser; michael@0: } michael@0: for (i=0; envelopeOptions->recipients[i] != NULL; i++) { michael@0: if ((recipientcerts[i] = michael@0: CERT_FindCertByNicknameOrEmailAddr(dbhandle, michael@0: envelopeOptions->recipients[i])) michael@0: == NULL) { michael@0: SECU_PrintError(progName, "cannot find certificate for \"%s\"", michael@0: envelopeOptions->recipients[i]); michael@0: i=0; michael@0: goto loser; michael@0: } michael@0: } michael@0: recipientcerts[i] = NULL; michael@0: i=0; michael@0: /* find a nice bulk algorithm */ michael@0: if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, michael@0: &keysize) != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * create the message object michael@0: */ michael@0: cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ michael@0: if (cmsg == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS message.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * build chain of objects: message->envelopedData->data michael@0: */ michael@0: if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) michael@0: == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); michael@0: /* we're always passing data in, so the content is NULL */ michael@0: if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * create & attach recipient information michael@0: */ michael@0: for (i = 0; recipientcerts[i] != NULL; i++) { michael@0: if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, michael@0: recipientcerts[i])) michael@0: == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n"); michael@0: goto loser; michael@0: } michael@0: if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n"); michael@0: goto loser; michael@0: } michael@0: CERT_DestroyCertificate(recipientcerts[i]); michael@0: } michael@0: if (tmppoolp) michael@0: PORT_FreeArena(tmppoolp, PR_FALSE); michael@0: return cmsg; michael@0: loser: michael@0: if (recipientcerts) { michael@0: for (; recipientcerts[i] != NULL; i++) { michael@0: CERT_DestroyCertificate(recipientcerts[i]); michael@0: } michael@0: } michael@0: if (cmsg) michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: if (tmppoolp) michael@0: PORT_FreeArena(tmppoolp, PR_FALSE); michael@0: return NULL; michael@0: } michael@0: michael@0: PK11SymKey *dkcb(void *arg, SECAlgorithmID *algid) michael@0: { michael@0: return (PK11SymKey*)arg; michael@0: } michael@0: michael@0: static SECStatus michael@0: get_enc_params(struct encryptOptionsStr *encryptOptions) michael@0: { michael@0: struct envelopeOptionsStr envelopeOptions; michael@0: SECStatus rv = SECFailure; michael@0: NSSCMSMessage *env_cmsg; michael@0: NSSCMSContentInfo *cinfo; michael@0: int i, nlevels; michael@0: /* michael@0: * construct an enveloped data message to obtain bulk keys michael@0: */ michael@0: if (encryptOptions->envmsg) { michael@0: env_cmsg = encryptOptions->envmsg; /* get it from an old message */ michael@0: } else { michael@0: SECItem dummyOut = { 0, 0, 0 }; michael@0: SECItem dummyIn = { 0, 0, 0 }; michael@0: char str[] = "Hello!"; michael@0: PLArenaPool *tmparena = PORT_NewArena(1024); michael@0: dummyIn.data = (unsigned char *)str; michael@0: dummyIn.len = strlen(str); michael@0: envelopeOptions.options = encryptOptions->options; michael@0: envelopeOptions.recipients = encryptOptions->recipients; michael@0: env_cmsg = enveloped_data(&envelopeOptions); michael@0: NSS_CMSDEREncode(env_cmsg, &dummyIn, &dummyOut, tmparena); michael@0: PR_Write(encryptOptions->envFile, dummyOut.data, dummyOut.len); michael@0: PORT_FreeArena(tmparena, PR_FALSE); michael@0: } michael@0: /* michael@0: * get the content info for the enveloped data michael@0: */ michael@0: nlevels = NSS_CMSMessage_ContentLevelCount(env_cmsg); michael@0: for (i = 0; i < nlevels; i++) { michael@0: SECOidTag typetag; michael@0: cinfo = NSS_CMSMessage_ContentLevel(env_cmsg, i); michael@0: typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); michael@0: if (typetag == SEC_OID_PKCS7_DATA) { michael@0: /* michael@0: * get the symmetric key michael@0: */ michael@0: encryptOptions->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo); michael@0: encryptOptions->keysize = NSS_CMSContentInfo_GetBulkKeySize(cinfo); michael@0: encryptOptions->bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo); michael@0: rv = SECSuccess; michael@0: break; michael@0: } michael@0: } michael@0: if (i == nlevels) { michael@0: fprintf(stderr, "%s: could not retrieve enveloped data.", progName); michael@0: } michael@0: if (env_cmsg) michael@0: NSS_CMSMessage_Destroy(env_cmsg); michael@0: return rv; michael@0: } michael@0: michael@0: static NSSCMSMessage * michael@0: encrypted_data(struct encryptOptionsStr *encryptOptions) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: NSSCMSMessage *cmsg = NULL; michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSEncryptedData *encd; michael@0: NSSCMSEncoderContext *ecx = NULL; michael@0: PLArenaPool *tmppoolp = NULL; michael@0: SECItem derOut = { 0, 0, 0 }; michael@0: /* arena for output */ michael@0: tmppoolp = PORT_NewArena(1024); michael@0: if (!tmppoolp) { michael@0: fprintf(stderr, "%s: out of memory.\n", progName); michael@0: return NULL; michael@0: } michael@0: /* michael@0: * create the message object michael@0: */ michael@0: cmsg = NSS_CMSMessage_Create(NULL); michael@0: if (cmsg == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS message.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * build chain of objects: message->encryptedData->data michael@0: */ michael@0: if ((encd = NSS_CMSEncryptedData_Create(cmsg, encryptOptions->bulkalgtag, michael@0: encryptOptions->keysize)) michael@0: == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS encryptedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: if (NSS_CMSContentInfo_SetContent_EncryptedData(cmsg, cinfo, encd) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS encryptedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSEncryptedData_GetContentInfo(encd); michael@0: /* we're always passing data in, so the content is NULL */ michael@0: if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); michael@0: goto loser; michael@0: } michael@0: ecx = NSS_CMSEncoder_Start(cmsg, NULL, NULL, &derOut, tmppoolp, NULL, NULL, michael@0: dkcb, encryptOptions->bulkkey, NULL, NULL); michael@0: if (!ecx) { michael@0: fprintf(stderr, "%s: cannot create encoder context.\n", progName); michael@0: goto loser; michael@0: } michael@0: rv = NSS_CMSEncoder_Update(ecx, (char *)encryptOptions->input->data, michael@0: encryptOptions->input->len); michael@0: if (rv) { michael@0: fprintf(stderr, "%s: failed to add data to encoder.\n", progName); michael@0: goto loser; michael@0: } michael@0: rv = NSS_CMSEncoder_Finish(ecx); michael@0: if (rv) { michael@0: fprintf(stderr, "%s: failed to encrypt data.\n", progName); michael@0: goto loser; michael@0: } michael@0: fwrite(derOut.data, derOut.len, 1, encryptOptions->outfile); michael@0: /* michael@0: if (bulkkey) michael@0: PK11_FreeSymKey(bulkkey); michael@0: */ michael@0: if (tmppoolp) michael@0: PORT_FreeArena(tmppoolp, PR_FALSE); michael@0: return cmsg; michael@0: loser: michael@0: /* michael@0: if (bulkkey) michael@0: PK11_FreeSymKey(bulkkey); michael@0: */ michael@0: if (tmppoolp) michael@0: PORT_FreeArena(tmppoolp, PR_FALSE); michael@0: if (cmsg) michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: return NULL; michael@0: } michael@0: michael@0: static NSSCMSMessage * michael@0: signed_data_certsonly(struct certsonlyOptionsStr *certsonlyOptions) michael@0: { michael@0: NSSCMSMessage *cmsg = NULL; michael@0: NSSCMSContentInfo *cinfo; michael@0: NSSCMSSignedData *sigd; michael@0: CERTCertificate **certs = NULL; michael@0: CERTCertDBHandle *dbhandle; michael@0: PLArenaPool *tmppoolp = NULL; michael@0: int i = 0, cnt; michael@0: dbhandle = certsonlyOptions->options->certHandle; michael@0: if ((cnt = nss_CMSArray_Count((void**)certsonlyOptions->recipients)) == 0) { michael@0: fprintf(stderr, michael@0: "ERROR: please indicate the nickname of a certificate to sign with.\n"); michael@0: goto loser; michael@0: } michael@0: if (!(tmppoolp = PORT_NewArena(1024))) { michael@0: fprintf(stderr, "ERROR: out of memory.\n"); michael@0: goto loser; michael@0: } michael@0: if (!(certs = PORT_ArenaZNewArray(tmppoolp, CERTCertificate *, cnt + 1))) { michael@0: fprintf(stderr, "ERROR: out of memory.\n"); michael@0: goto loser; michael@0: } michael@0: for (i=0; certsonlyOptions->recipients[i] != NULL; i++) { michael@0: if ((certs[i] = michael@0: CERT_FindCertByNicknameOrEmailAddr(dbhandle, michael@0: certsonlyOptions->recipients[i])) michael@0: == NULL) { michael@0: SECU_PrintError(progName, "cannot find certificate for \"%s\"", michael@0: certsonlyOptions->recipients[i]); michael@0: i=0; michael@0: goto loser; michael@0: } michael@0: } michael@0: certs[i] = NULL; michael@0: i=0; michael@0: /* michael@0: * create the message object michael@0: */ michael@0: cmsg = NSS_CMSMessage_Create(NULL); michael@0: if (cmsg == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS message.\n"); michael@0: goto loser; michael@0: } michael@0: /* michael@0: * build chain of objects: message->signedData->data michael@0: */ michael@0: if ((sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, certs[0], PR_TRUE)) michael@0: == NULL) { michael@0: fprintf(stderr, "ERROR: cannot create CMS signedData object.\n"); michael@0: goto loser; michael@0: } michael@0: CERT_DestroyCertificate(certs[0]); michael@0: for (i=1; irecipients[i]); michael@0: goto loser; michael@0: } michael@0: CERT_DestroyCertificate(certs[i]); michael@0: } michael@0: cinfo = NSS_CMSMessage_GetContentInfo(cmsg); michael@0: if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n"); michael@0: goto loser; michael@0: } michael@0: cinfo = NSS_CMSSignedData_GetContentInfo(sigd); michael@0: if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) michael@0: != SECSuccess) { michael@0: fprintf(stderr, "ERROR: cannot attach CMS data object.\n"); michael@0: goto loser; michael@0: } michael@0: if (tmppoolp) michael@0: PORT_FreeArena(tmppoolp, PR_FALSE); michael@0: return cmsg; michael@0: loser: michael@0: if (certs) { michael@0: for (; i 1) { michael@0: nb = PR_Read(fd, bp, 1); michael@0: if (nb < 0) { michael@0: /* deal with error */ michael@0: return NULL; michael@0: } else if (nb == 0) { michael@0: /* deal with EOF */ michael@0: return NULL; michael@0: } else if (*bp == '\n') { michael@0: /* deal with EOL */ michael@0: ++bp; /* keep EOL character */ michael@0: break; michael@0: } else { michael@0: /* ordinary character */ michael@0: ++bp; michael@0: --size; michael@0: } michael@0: } michael@0: *bp = '\0'; michael@0: return buf; michael@0: } michael@0: michael@0: typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT, ENVELOPE, CERTSONLY } Mode; michael@0: michael@0: static int michael@0: doBatchDecode(FILE *outFile, PRFileDesc *batchFile, michael@0: const struct decodeOptionsStr *decodeOptions) michael@0: { michael@0: char * str; michael@0: int exitStatus = 0; michael@0: char batchLine[512]; michael@0: michael@0: while (NULL != (str = pl_fgets(batchLine, sizeof batchLine, batchFile))) { michael@0: NSSCMSMessage *cmsg = NULL; michael@0: PRFileDesc * inFile; michael@0: int len = strlen(str); michael@0: SECStatus rv; michael@0: SECItem input = {0, 0, 0}; michael@0: char cc; michael@0: michael@0: while (len > 0 && michael@0: ((cc = str[len - 1]) == '\n' || cc == '\r')) { michael@0: str[--len] = '\0'; michael@0: } michael@0: if (!len) /* skip empty line */ michael@0: continue; michael@0: if (str[0] == '#') michael@0: continue; /* skip comment line */ michael@0: fprintf(outFile, "========== %s ==========\n", str); michael@0: inFile = PR_Open(str, PR_RDONLY, 00660); michael@0: if (inFile == NULL) { michael@0: fprintf(outFile, "%s: unable to open \"%s\" for reading\n", michael@0: progName, str); michael@0: exitStatus = 1; michael@0: continue; michael@0: } michael@0: rv = SECU_FileToItem(&input, inFile); michael@0: PR_Close(inFile); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "unable to read infile"); michael@0: exitStatus = 1; michael@0: continue; michael@0: } michael@0: cmsg = decode(outFile, &input, decodeOptions); michael@0: SECITEM_FreeItem(&input, PR_FALSE); michael@0: if (cmsg) michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: else { michael@0: SECU_PrintError(progName, "problem decoding"); michael@0: exitStatus = 1; michael@0: } michael@0: } michael@0: return exitStatus; michael@0: } michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: FILE *outFile; michael@0: NSSCMSMessage *cmsg = NULL; michael@0: PRFileDesc *inFile; michael@0: PLOptState *optstate; michael@0: PLOptStatus status; michael@0: Mode mode = UNKNOWN; michael@0: struct decodeOptionsStr decodeOptions = { 0 }; michael@0: struct signOptionsStr signOptions = { 0 }; michael@0: struct envelopeOptionsStr envelopeOptions = { 0 }; michael@0: struct certsonlyOptionsStr certsonlyOptions = { 0 }; michael@0: struct encryptOptionsStr encryptOptions = { 0 }; michael@0: struct optionsStr options = { 0 }; michael@0: int exitstatus; michael@0: static char *ptrarray[128] = { 0 }; michael@0: int nrecipients = 0; michael@0: char *str, *tok; michael@0: char *envFileName; michael@0: SECItem input = { 0, 0, 0}; michael@0: SECItem envmsg = { 0, 0, 0 }; michael@0: SECStatus rv; michael@0: PRFileDesc *contentFile = NULL; michael@0: PRBool batch = PR_FALSE; michael@0: michael@0: #ifdef NISCC_TEST michael@0: const char *ev = PR_GetEnv("NSS_DISABLE_ARENA_FREE_LIST"); michael@0: PORT_Assert(ev); michael@0: ev = PR_GetEnv("NSS_STRICT_SHUTDOWN"); michael@0: PORT_Assert(ev); michael@0: #endif michael@0: michael@0: progName = strrchr(argv[0], '/'); michael@0: if (!progName) michael@0: progName = strrchr(argv[0], '\\'); michael@0: progName = progName ? progName+1 : argv[0]; michael@0: michael@0: inFile = PR_STDIN; michael@0: outFile = stdout; michael@0: envFileName = NULL; michael@0: mode = UNKNOWN; michael@0: decodeOptions.content.data = NULL; michael@0: decodeOptions.content.len = 0; michael@0: decodeOptions.suppressContent = PR_FALSE; michael@0: decodeOptions.headerLevel = -1; michael@0: decodeOptions.keepCerts = PR_FALSE; michael@0: options.certUsage = certUsageEmailSigner; michael@0: options.password = NULL; michael@0: options.pwfile = NULL; michael@0: signOptions.nickname = NULL; michael@0: signOptions.detached = PR_FALSE; michael@0: signOptions.signingTime = PR_FALSE; michael@0: signOptions.smimeProfile = PR_FALSE; michael@0: signOptions.encryptionKeyPreferenceNick = NULL; michael@0: signOptions.hashAlgTag = SEC_OID_SHA1; michael@0: envelopeOptions.recipients = NULL; michael@0: encryptOptions.recipients = NULL; michael@0: encryptOptions.envmsg = NULL; michael@0: encryptOptions.envFile = NULL; michael@0: encryptOptions.bulkalgtag = SEC_OID_UNKNOWN; michael@0: encryptOptions.bulkkey = NULL; michael@0: encryptOptions.keysize = -1; michael@0: michael@0: /* michael@0: * Parse command line arguments michael@0: */ michael@0: optstate = PL_CreateOptState(argc, argv, michael@0: "CDEGH:N:OPSTY:bc:d:e:f:h:i:kno:p:r:s:u:v"); michael@0: while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { michael@0: switch (optstate->option) { michael@0: case 'C': michael@0: mode = ENCRYPT; michael@0: break; michael@0: case 'D': michael@0: mode = DECODE; michael@0: break; michael@0: case 'E': michael@0: mode = ENVELOPE; michael@0: break; michael@0: case 'G': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -G only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: signOptions.signingTime = PR_TRUE; michael@0: break; michael@0: case 'H': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -H only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: decodeOptions.suppressContent = PR_TRUE; michael@0: if (!strcmp(optstate->value, "MD2")) michael@0: signOptions.hashAlgTag = SEC_OID_MD2; michael@0: else if (!strcmp(optstate->value, "MD4")) michael@0: signOptions.hashAlgTag = SEC_OID_MD4; michael@0: else if (!strcmp(optstate->value, "MD5")) michael@0: signOptions.hashAlgTag = SEC_OID_MD5; michael@0: else if (!strcmp(optstate->value, "SHA1")) michael@0: signOptions.hashAlgTag = SEC_OID_SHA1; michael@0: else if (!strcmp(optstate->value, "SHA256")) michael@0: signOptions.hashAlgTag = SEC_OID_SHA256; michael@0: else if (!strcmp(optstate->value, "SHA384")) michael@0: signOptions.hashAlgTag = SEC_OID_SHA384; michael@0: else if (!strcmp(optstate->value, "SHA512")) michael@0: signOptions.hashAlgTag = SEC_OID_SHA512; michael@0: else { michael@0: fprintf(stderr, michael@0: "%s: -H requires one of MD2,MD4,MD5,SHA1,SHA256,SHA384,SHA512\n", michael@0: progName); michael@0: exit(1); michael@0: } michael@0: break; michael@0: case 'N': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -N only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: signOptions.nickname = strdup(optstate->value); michael@0: break; michael@0: case 'O': michael@0: mode = CERTSONLY; michael@0: break; michael@0: case 'P': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -P only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: signOptions.smimeProfile = PR_TRUE; michael@0: break; michael@0: case 'S': michael@0: mode = SIGN; michael@0: break; michael@0: case 'T': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -T only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: signOptions.detached = PR_TRUE; michael@0: break; michael@0: case 'Y': michael@0: if (mode != SIGN) { michael@0: fprintf(stderr, michael@0: "%s: option -Y only supported with option -S.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: signOptions.encryptionKeyPreferenceNick = strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'b': michael@0: if (mode != DECODE) { michael@0: fprintf(stderr, michael@0: "%s: option -b only supported with option -D.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: batch = PR_TRUE; michael@0: break; michael@0: michael@0: case 'c': michael@0: if (mode != DECODE) { michael@0: fprintf(stderr, michael@0: "%s: option -c only supported with option -D.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: contentFile = PR_Open(optstate->value, PR_RDONLY, 006600); michael@0: if (contentFile == NULL) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for reading.\n", michael@0: progName, optstate->value); michael@0: exit(1); michael@0: } michael@0: michael@0: rv = SECU_FileToItem(&decodeOptions.content, contentFile); michael@0: PR_Close(contentFile); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "problem reading content file"); michael@0: exit(1); michael@0: } michael@0: if (!decodeOptions.content.data) { michael@0: /* file was zero length */ michael@0: decodeOptions.content.data = (unsigned char *)PORT_Strdup(""); michael@0: decodeOptions.content.len = 0; michael@0: } michael@0: michael@0: break; michael@0: case 'd': michael@0: SECU_ConfigDirectory(optstate->value); michael@0: break; michael@0: case 'e': michael@0: envFileName = strdup(optstate->value); michael@0: encryptOptions.envFile = PR_Open(envFileName, PR_RDONLY, 00660); michael@0: break; michael@0: michael@0: case 'h': michael@0: if (mode != DECODE) { michael@0: fprintf(stderr, michael@0: "%s: option -h only supported with option -D.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: decodeOptions.headerLevel = atoi(optstate->value); michael@0: if (decodeOptions.headerLevel < 0) { michael@0: fprintf(stderr, "option -h cannot have a negative value.\n"); michael@0: exit(1); michael@0: } michael@0: break; michael@0: case 'i': michael@0: if (!optstate->value) { michael@0: fprintf(stderr, "-i option requires filename argument\n"); michael@0: exit(1); michael@0: } michael@0: inFile = PR_Open(optstate->value, PR_RDONLY, 00660); michael@0: if (inFile == NULL) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for reading\n", michael@0: progName, optstate->value); michael@0: exit(1); michael@0: } michael@0: break; michael@0: michael@0: case 'k': michael@0: if (mode != DECODE) { michael@0: fprintf(stderr, michael@0: "%s: option -k only supported with option -D.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: decodeOptions.keepCerts = PR_TRUE; michael@0: break; michael@0: michael@0: case 'n': michael@0: if (mode != DECODE) { michael@0: fprintf(stderr, michael@0: "%s: option -n only supported with option -D.\n", michael@0: progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: decodeOptions.suppressContent = PR_TRUE; michael@0: break; michael@0: case 'o': michael@0: outFile = fopen(optstate->value, "wb"); michael@0: if (outFile == NULL) { michael@0: fprintf(stderr, "%s: unable to open \"%s\" for writing\n", michael@0: progName, optstate->value); michael@0: exit(1); michael@0: } michael@0: break; michael@0: case 'p': michael@0: if (!optstate->value) { michael@0: fprintf(stderr, "%s: option -p must have a value.\n", progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: michael@0: options.password = strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'f': michael@0: if (!optstate->value) { michael@0: fprintf(stderr, "%s: option -f must have a value.\n", progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: michael@0: options.pwfile = strdup(optstate->value); michael@0: break; michael@0: michael@0: case 'r': michael@0: if (!optstate->value) { michael@0: fprintf(stderr, "%s: option -r must have a value.\n", progName); michael@0: Usage(progName); michael@0: exit(1); michael@0: } michael@0: envelopeOptions.recipients = ptrarray; michael@0: str = (char *)optstate->value; michael@0: do { michael@0: tok = strchr(str, ','); michael@0: if (tok) *tok = '\0'; michael@0: envelopeOptions.recipients[nrecipients++] = strdup(str); michael@0: if (tok) str = tok + 1; michael@0: } while (tok); michael@0: envelopeOptions.recipients[nrecipients] = NULL; michael@0: encryptOptions.recipients = envelopeOptions.recipients; michael@0: certsonlyOptions.recipients = envelopeOptions.recipients; michael@0: break; michael@0: michael@0: case 'u': { michael@0: int usageType; michael@0: michael@0: usageType = atoi (strdup(optstate->value)); michael@0: if (usageType < certUsageSSLClient || usageType > certUsageAnyCA) michael@0: return -1; michael@0: options.certUsage = (SECCertUsage)usageType; michael@0: break; michael@0: } michael@0: case 'v': michael@0: cms_verbose = 1; michael@0: break; michael@0: michael@0: } michael@0: } michael@0: if (status == PL_OPT_BAD) michael@0: Usage(progName); michael@0: PL_DestroyOptState(optstate); michael@0: michael@0: if (mode == UNKNOWN) michael@0: Usage(progName); michael@0: michael@0: if (mode != CERTSONLY && !batch) { michael@0: rv = SECU_FileToItem(&input, inFile); michael@0: if (rv != SECSuccess) { michael@0: SECU_PrintError(progName, "unable to read infile"); michael@0: exit(1); michael@0: } michael@0: if (inFile != PR_STDIN) { michael@0: PR_Close(inFile); michael@0: } michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "received commands\n"); michael@0: } michael@0: michael@0: /* Call the NSS initialization routines */ michael@0: PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); michael@0: rv = NSS_InitReadWrite(SECU_ConfigDirectory(NULL)); michael@0: if (SECSuccess != rv) { michael@0: SECU_PrintError(progName, "NSS_Init failed"); michael@0: exit(1); michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "NSS has been initialized.\n"); michael@0: } michael@0: options.certHandle = CERT_GetDefaultCertDB(); michael@0: if (!options.certHandle) { michael@0: SECU_PrintError(progName, "No default cert DB"); michael@0: exit(1); michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "Got default certdb\n"); michael@0: } michael@0: if (options.password) michael@0: { michael@0: pwdata.source = PW_PLAINTEXT; michael@0: pwdata.data = options.password; michael@0: } michael@0: if (options.pwfile) michael@0: { michael@0: pwdata.source = PW_FROMFILE; michael@0: pwdata.data = options.pwfile; michael@0: } michael@0: pwcb = SECU_GetModulePassword; michael@0: pwcb_arg = (void *)&pwdata; michael@0: michael@0: PK11_SetPasswordFunc(&SECU_GetModulePassword); michael@0: michael@0: michael@0: #if defined(_WIN32) michael@0: if (outFile == stdout) { michael@0: /* If we're going to write binary data to stdout, we must put stdout michael@0: ** into O_BINARY mode or else outgoing \n's will become \r\n's. michael@0: */ michael@0: int smrv = _setmode(_fileno(stdout), _O_BINARY); michael@0: if (smrv == -1) { michael@0: fprintf(stderr, michael@0: "%s: Cannot change stdout to binary mode. Use -o option instead.\n", michael@0: progName); michael@0: return smrv; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: exitstatus = 0; michael@0: switch (mode) { michael@0: case DECODE: /* -D */ michael@0: decodeOptions.options = &options; michael@0: if (encryptOptions.envFile) { michael@0: /* Decoding encrypted-data, so get the bulkkey from an michael@0: * enveloped-data message. michael@0: */ michael@0: SECU_FileToItem(&envmsg, encryptOptions.envFile); michael@0: decodeOptions.options = &options; michael@0: encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); michael@0: if (!encryptOptions.envmsg) { michael@0: SECU_PrintError(progName, "problem decoding env msg"); michael@0: exitstatus = 1; michael@0: break; michael@0: } michael@0: rv = get_enc_params(&encryptOptions); michael@0: decodeOptions.dkcb = dkcb; michael@0: decodeOptions.bulkkey = encryptOptions.bulkkey; michael@0: } michael@0: if (!batch) { michael@0: cmsg = decode(outFile, &input, &decodeOptions); michael@0: if (!cmsg) { michael@0: SECU_PrintError(progName, "problem decoding"); michael@0: exitstatus = 1; michael@0: } michael@0: } else { michael@0: exitstatus = doBatchDecode(outFile, inFile, &decodeOptions); michael@0: if (inFile != PR_STDIN) { michael@0: PR_Close(inFile); michael@0: } michael@0: } michael@0: break; michael@0: case SIGN: /* -S */ michael@0: signOptions.options = &options; michael@0: cmsg = signed_data(&signOptions); michael@0: if (!cmsg) { michael@0: SECU_PrintError(progName, "problem signing"); michael@0: exitstatus = 1; michael@0: } michael@0: break; michael@0: case ENCRYPT: /* -C */ michael@0: if (!envFileName) { michael@0: fprintf(stderr, "%s: you must specify an envelope file with -e.\n", michael@0: progName); michael@0: exit(1); michael@0: } michael@0: encryptOptions.options = &options; michael@0: encryptOptions.input = &input; michael@0: encryptOptions.outfile = outFile; michael@0: /* decode an enveloped-data message to get the bulkkey (create michael@0: * a new one if neccessary) michael@0: */ michael@0: if (!encryptOptions.envFile) { michael@0: encryptOptions.envFile = PR_Open(envFileName, michael@0: PR_WRONLY|PR_CREATE_FILE, 00660); michael@0: if (!encryptOptions.envFile) { michael@0: fprintf(stderr, "%s: failed to create file %s.\n", progName, michael@0: envFileName); michael@0: exit(1); michael@0: } michael@0: } else { michael@0: SECU_FileToItem(&envmsg, encryptOptions.envFile); michael@0: decodeOptions.options = &options; michael@0: encryptOptions.envmsg = decode(NULL, &envmsg, &decodeOptions); michael@0: if (encryptOptions.envmsg == NULL) { michael@0: SECU_PrintError(progName, "problem decrypting env msg"); michael@0: exitstatus = 1; michael@0: break; michael@0: } michael@0: } michael@0: rv = get_enc_params(&encryptOptions); michael@0: /* create the encrypted-data message */ michael@0: cmsg = encrypted_data(&encryptOptions); michael@0: if (!cmsg) { michael@0: SECU_PrintError(progName, "problem encrypting"); michael@0: exitstatus = 1; michael@0: } michael@0: if (encryptOptions.bulkkey) { michael@0: PK11_FreeSymKey(encryptOptions.bulkkey); michael@0: encryptOptions.bulkkey = NULL; michael@0: } michael@0: break; michael@0: case ENVELOPE: /* -E */ michael@0: envelopeOptions.options = &options; michael@0: cmsg = enveloped_data(&envelopeOptions); michael@0: if (!cmsg) { michael@0: SECU_PrintError(progName, "problem enveloping"); michael@0: exitstatus = 1; michael@0: } michael@0: break; michael@0: case CERTSONLY: /* -O */ michael@0: certsonlyOptions.options = &options; michael@0: cmsg = signed_data_certsonly(&certsonlyOptions); michael@0: if (!cmsg) { michael@0: SECU_PrintError(progName, "problem with certs-only"); michael@0: exitstatus = 1; michael@0: } michael@0: break; michael@0: default: michael@0: fprintf(stderr, "One of options -D, -S or -E must be set.\n"); michael@0: Usage(progName); michael@0: exitstatus = 1; michael@0: } michael@0: if ( (mode == SIGN || mode == ENVELOPE || mode == CERTSONLY) michael@0: && (!exitstatus) ) { michael@0: PLArenaPool *arena = PORT_NewArena(1024); michael@0: NSSCMSEncoderContext *ecx; michael@0: SECItem output = { 0, 0, 0 }; michael@0: michael@0: if (!arena) { michael@0: fprintf(stderr, "%s: out of memory.\n", progName); michael@0: exit(1); michael@0: } michael@0: michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "cmsg [%p]\n", cmsg); michael@0: fprintf(stderr, "arena [%p]\n", arena); michael@0: if (pwcb_arg && (PW_PLAINTEXT == ((secuPWData*)pwcb_arg)->source)) michael@0: fprintf(stderr, "password [%s]\n", michael@0: ((secuPWData*)pwcb_arg)->data); michael@0: else michael@0: fprintf(stderr, "password [NULL]\n"); michael@0: } michael@0: ecx = NSS_CMSEncoder_Start(cmsg, michael@0: NULL, NULL, /* DER output callback */ michael@0: &output, arena, /* destination storage */ michael@0: pwcb, pwcb_arg, /* password callback */ michael@0: NULL, NULL, /* decrypt key callback */ michael@0: NULL, NULL ); /* detached digests */ michael@0: if (!ecx) { michael@0: fprintf(stderr, "%s: cannot create encoder context.\n", progName); michael@0: exit(1); michael@0: } michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "input len [%d]\n", input.len); michael@0: { unsigned int j; michael@0: for(j=0;j0&&j%35==0)?'\n':' '); michael@0: } michael@0: } michael@0: if (input.len > 0) { /* skip if certs-only (or other zero content) */ michael@0: rv = NSS_CMSEncoder_Update(ecx, (char *)input.data, input.len); michael@0: if (rv) { michael@0: fprintf(stderr, michael@0: "%s: failed to add data to encoder.\n", progName); michael@0: exit(1); michael@0: } michael@0: } michael@0: rv = NSS_CMSEncoder_Finish(ecx); michael@0: if (rv) { michael@0: SECU_PrintError(progName, "failed to encode data"); michael@0: exit(1); michael@0: } michael@0: michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "encoding passed\n"); michael@0: } michael@0: fwrite(output.data, output.len, 1, outFile); michael@0: if (cms_verbose) { michael@0: fprintf(stderr, "wrote to file\n"); michael@0: } michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: } michael@0: if (cmsg) michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: if (outFile != stdout) michael@0: fclose(outFile); michael@0: michael@0: SECITEM_FreeItem(&decodeOptions.content, PR_FALSE); michael@0: SECITEM_FreeItem(&envmsg, PR_FALSE); michael@0: SECITEM_FreeItem(&input, PR_FALSE); michael@0: if (NSS_Shutdown() != SECSuccess) { michael@0: SECU_PrintError(progName, "NSS_Shutdown failed"); michael@0: exitstatus = 1; michael@0: } michael@0: PR_Cleanup(); michael@0: return exitstatus; michael@0: }