summaryrefslogtreecommitdiff
path: root/security/nss/cmd
diff options
context:
space:
mode:
authorchrisk%netscape.com <devnull@localhost>2000-06-13 21:56:37 +0000
committerchrisk%netscape.com <devnull@localhost>2000-06-13 21:56:37 +0000
commitcd4705729f6adbb75446f795242faafbb5f1e916 (patch)
tree21662a7a130dea2cba0c2e99c0048242f97a0fc0 /security/nss/cmd
parenteaa056d41046b41fee0c3b8d6fa93714a6e5474a (diff)
downloadnss-hg-cd4705729f6adbb75446f795242faafbb5f1e916.tar.gz
Merge smimetk_branch to tip...
Diffstat (limited to 'security/nss/cmd')
-rw-r--r--security/nss/cmd/manifest.mn1
-rw-r--r--security/nss/cmd/platlibs.mk2
-rw-r--r--security/nss/cmd/smimetools/Makefile73
-rw-r--r--security/nss/cmd/smimetools/cmsutil.c841
-rw-r--r--security/nss/cmd/smimetools/manifest.mn45
-rw-r--r--security/nss/cmd/smimetools/rules.mk37
-rwxr-xr-xsecurity/nss/cmd/smimetools/smime325
7 files changed, 1324 insertions, 0 deletions
diff --git a/security/nss/cmd/manifest.mn b/security/nss/cmd/manifest.mn
index 2b96271f7..8a8b5caa1 100644
--- a/security/nss/cmd/manifest.mn
+++ b/security/nss/cmd/manifest.mn
@@ -60,6 +60,7 @@ DIRS = lib \
strsclnt \
swfort \
tstclnt \
+ smimetools \
$(NULL)
TEMPORARILY_DONT_BUILD = \
diff --git a/security/nss/cmd/platlibs.mk b/security/nss/cmd/platlibs.mk
index c32653f91..63e206806 100644
--- a/security/nss/cmd/platlibs.mk
+++ b/security/nss/cmd/platlibs.mk
@@ -43,6 +43,7 @@ ifdef MOZILLA_BSAFE_BUILD
endif
EXTRA_LIBS += \
+ $(DIST)/lib/smime.lib \
$(DIST)/lib/ssl.lib \
$(DIST)/lib/jar.lib \
$(DIST)/lib/zlib.lib \
@@ -81,6 +82,7 @@ ifdef MOZILLA_BSAFE_BUILD
CRYPTOLIB=$(DIST)/lib/libbsafe.a
endif
EXTRA_LIBS += \
+ $(DIST)/lib/libsmime.a \
$(DIST)/lib/libssl.a \
$(DIST)/lib/libjar.a \
$(DIST)/lib/libzlib.a \
diff --git a/security/nss/cmd/smimetools/Makefile b/security/nss/cmd/smimetools/Makefile
new file mode 100644
index 000000000..9e3263d43
--- /dev/null
+++ b/security/nss/cmd/smimetools/Makefile
@@ -0,0 +1,73 @@
+#! gmake
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include ../platlibs.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+include rules.mk
+
+include ../platrules.mk
diff --git a/security/nss/cmd/smimetools/cmsutil.c b/security/nss/cmd/smimetools/cmsutil.c
new file mode 100644
index 000000000..ed070f2b6
--- /dev/null
+++ b/security/nss/cmd/smimetools/cmsutil.c
@@ -0,0 +1,841 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * cmsutil -- A command to work with CMS data
+ *
+ * $Id$
+ */
+
+#include "nspr.h"
+#include "secutil.h"
+#include "plgetopt.h"
+#include "secpkcs7.h"
+#include "cert.h"
+#include "certdb.h"
+#include "cdbhdl.h"
+#include "secoid.h"
+#include "cms.h"
+#include "smime.h"
+
+#if defined(XP_UNIX)
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+extern void SEC_Init(void); /* XXX */
+
+static SECStatus
+DigestFile(PLArenaPool *poolp, SECItem ***digests, FILE *inFile, SECAlgorithmID **algids)
+{
+ NSSCMSDigestContext *digcx;
+ int nb;
+ char ibuf[4096];
+ SECStatus rv;
+
+ digcx = NSS_CMSDigestContext_StartMultiple(algids);
+ if (digcx == NULL)
+ return SECFailure;
+
+ for (;;) {
+ if (feof(inFile)) break;
+ nb = fread(ibuf, 1, sizeof(ibuf), inFile);
+ if (nb == 0) {
+ if (ferror(inFile)) {
+ PORT_SetError(SEC_ERROR_IO);
+ NSS_CMSDigestContext_Cancel(digcx);
+ return SECFailure;
+ }
+ /* eof */
+ break;
+ }
+ NSS_CMSDigestContext_Update(digcx, (const unsigned char *)ibuf, nb);
+ }
+
+ rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
+
+ return rv;
+}
+
+
+static void
+Usage(char *progName)
+{
+ fprintf(stderr, "Usage: %s [-D|-S|-E] [<options>] [-d dbdir] [-u certusage]\n", progName);
+ fprintf(stderr, " -i infile use infile as source of data (default: stdin)\n");
+ fprintf(stderr, " -o outfile use outfile as destination of data (default: stdout)\n");
+ fprintf(stderr, " -d dbdir key/cert database directory (default: ~/.netscape)\n");
+ fprintf(stderr, " -p password use password as key db password (default: prompt)\n");
+ fprintf(stderr, " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -D decode a CMS message\n");
+ fprintf(stderr, " -c content use this detached content\n");
+ fprintf(stderr, " -n suppress output of content\n");
+ fprintf(stderr, " -h num generate email headers with info about CMS message\n");
+ fprintf(stderr, " -S create a CMS signed message\n");
+ fprintf(stderr, " -N nick use certificate named \"nick\" for signing\n");
+ fprintf(stderr, " -T do not include content in CMS message\n");
+ fprintf(stderr, " -G include a signing time attribute\n");
+ fprintf(stderr, " -P include a SMIMECapabilities attribute\n");
+ fprintf(stderr, " -Y nick include a EncryptionKeyPreference attribute with cert\n");
+ fprintf(stderr, " -E create a CMS enveloped message (NYI)\n");
+ fprintf(stderr, " -r id,... create envelope for these recipients,\n");
+ fprintf(stderr, " where id can be a certificate nickname or email address\n");
+ fprintf(stderr, "\nCert usage codes:\n");
+ fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " ");
+ fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " ");
+ fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " ");
+ fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " ");
+ fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " ");
+ fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " ");
+ fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " ");
+ fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " ");
+ fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " ");
+ fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " ");
+ fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
+ fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
+
+ exit(-1);
+}
+
+static CERTCertDBHandle certHandleStatic; /* avoid having to allocate */
+
+static CERTCertDBHandle *
+OpenCertDB(char *progName)
+{
+ CERTCertDBHandle *certHandle;
+ SECStatus rv;
+
+ certHandle = &certHandleStatic;
+ rv = CERT_OpenCertDB(certHandle, PR_FALSE, SECU_CertDBNameCallback, NULL);
+ if (rv != SECSuccess) {
+ SECU_PrintError(progName, "could not open cert database");
+ return NULL;
+ }
+
+ return certHandle;
+}
+
+char *
+ownpw(PK11SlotInfo *info, PRBool retry, void *arg)
+{
+ char * passwd = NULL;
+
+ if ( (!retry) && arg ) {
+ passwd = PL_strdup((char *)arg);
+ }
+
+ return passwd;
+}
+
+struct optionsStr {
+ char *password;
+ SECCertUsage certUsage;
+ CERTCertDBHandle *certHandle;
+};
+
+struct decodeOptionsStr {
+ FILE *contentFile;
+ int headerLevel;
+ PRBool suppressContent;
+};
+
+struct signOptionsStr {
+ char *nickname;
+ char *encryptionKeyPreferenceNick;
+ PRBool signingTime;
+ PRBool smimeProfile;
+ PRBool detached;
+};
+
+struct envelopeOptionsStr {
+ char **recipients;
+};
+
+static int
+decode(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct decodeOptionsStr decodeOptions)
+{
+ NSSCMSDecoderContext *dcx;
+ NSSCMSMessage *cmsg;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSSignedData *sigd = NULL;
+ NSSCMSEnvelopedData *envd;
+ SECAlgorithmID **digestalgs;
+ unsigned char buffer[32];
+ int nlevels, i, nsigners, j;
+ char *signercn;
+ NSSCMSSignerInfo *si;
+ SECOidTag typetag;
+ SECItem **digests;
+ PLArenaPool *poolp;
+ int nb;
+ char ibuf[4096];
+ PK11PasswordFunc pwcb;
+ void *pwcb_arg;
+ SECItem *item;
+
+ pwcb = (options.password != NULL) ? ownpw : NULL;
+ pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
+
+ dcx = NSS_CMSDecoder_Start(NULL,
+ NULL, NULL, /* content callback */
+ pwcb, pwcb_arg, /* password callback */
+ NULL, NULL); /* decrypt key callback */
+
+ for (;;) {
+ if (feof(infile)) break;
+ nb = fread(ibuf, 1, sizeof(ibuf), infile);
+ if (nb == 0) {
+ if (ferror(infile)) {
+ fprintf(stderr, "ERROR: file i/o error.\n");
+ NSS_CMSDecoder_Cancel(dcx);
+ return SECFailure;
+ }
+ /* eof */
+ break;
+ }
+ (void)NSS_CMSDecoder_Update(dcx, (const char *)ibuf, nb);
+ }
+ cmsg = NSS_CMSDecoder_Finish(dcx);
+ if (cmsg == NULL)
+ return -1;
+
+ if (decodeOptions.headerLevel >= 0) {
+ fprintf(out, "SMIME: ", decodeOptions.headerLevel, i);
+ }
+
+ nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
+ for (i = 0; i < nlevels; i++) {
+ cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
+ typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "\tlevel=%d.%d; ", decodeOptions.headerLevel, nlevels - i);
+
+ switch (typetag) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "type=signedData; ");
+ sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ if (sigd == NULL) {
+ SECU_PrintError(progName, "problem finding signedData component");
+ return -1;
+ }
+
+ /* if we have a content file, but no digests for this signedData */
+ if (decodeOptions.contentFile != NULL && !NSS_CMSSignedData_HasDigests(sigd)) {
+ if ((poolp = PORT_NewArena(1024)) == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ return -1;
+ }
+ digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
+ if (DigestFile (poolp, &digests, decodeOptions.contentFile, digestalgs) != SECSuccess) {
+ SECU_PrintError(progName, "problem computing message digest");
+ return -1;
+ }
+ if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
+
+ SECU_PrintError(progName, "problem setting message digests");
+ return -1;
+ }
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
+
+ /* still no digests? */
+ if (!NSS_CMSSignedData_HasDigests(sigd)) {
+ SECU_PrintError(progName, "no message digests");
+ return -1;
+ }
+
+ /* find out about signers */
+ nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "nsigners=%d; ", nsigners);
+ if (nsigners == 0) {
+ /* must be a cert transport message */
+ } else {
+ /* import the certificates */
+ if (NSS_CMSSignedData_ImportCerts(sigd, options.certHandle, options.certUsage, PR_FALSE) != SECSuccess) {
+ SECU_PrintError(progName, "cert import failed");
+ return -1;
+ }
+
+ for (j = 0; j < nsigners; j++) {
+ si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
+
+ signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
+ if (signercn == NULL)
+ signercn = "";
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
+ (void)NSS_CMSSignedData_VerifySignerInfo(sigd, j, options.certHandle, options.certUsage);
+
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "signer%d.status=%s; ", j, NSS_CMSUtil_VerificationStatusToString(NSS_CMSSignerInfo_GetVerificationStatus(si)));
+ /* XXX what do we do if we don't print headers? */
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "type=envelopedData; ");
+ envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ break;
+ case SEC_OID_PKCS7_DATA:
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "type=data; ");
+ break;
+ default:
+ break;
+ }
+ if (decodeOptions.headerLevel >= 0)
+ fprintf(out, "\n");
+ }
+
+ if (!decodeOptions.suppressContent) {
+ /* XXX only if we do not have detached content... */
+ if ((item = NSS_CMSMessage_GetContent(cmsg)) != NULL) {
+ fwrite(item->data, item->len, 1, out);
+ }
+ }
+
+ NSS_CMSMessage_Destroy(cmsg);
+ return 0;
+}
+
+static void
+writeout(void *arg, const char *buf, unsigned long len)
+{
+ FILE *f = (FILE *)arg;
+
+ if (f != NULL && buf != NULL)
+ (void)fwrite(buf, len, 1, f);
+}
+
+static int
+sign(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct signOptionsStr signOptions)
+{
+ NSSCMSEncoderContext *ecx;
+ NSSCMSMessage *cmsg;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSSignedData *sigd;
+ NSSCMSSignerInfo *signerinfo;
+ int nb;
+ char ibuf[4096];
+ PK11PasswordFunc pwcb;
+ void *pwcb_arg;
+ CERTCertificate *cert;
+
+ if (signOptions.nickname == NULL) {
+ fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
+ return SECFailure;
+ }
+
+ if ((cert = CERT_FindCertByNickname(options.certHandle, signOptions.nickname)) == NULL) {
+ SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
+ signOptions.nickname);
+ return SECFailure;
+ }
+
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ return SECFailure;
+ }
+ /*
+ * build chain of objects: message->signedData->data
+ */
+ if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+
+ cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
+ /* we're always passing data in and detaching optionally */
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, signOptions.detached) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+
+ /*
+ * create & attach signer information
+ */
+ if ((signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, SEC_OID_SHA1)) == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+
+ /* we want the cert chain included for this one */
+ if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, options.certUsage) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot find cert chain.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+
+ if (signOptions.signingTime) {
+ if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+ }
+ if (signOptions.smimeProfile) {
+ /* TBD */
+ }
+ if (signOptions.encryptionKeyPreferenceNick) {
+ /* TBD */
+ /* get the cert, add it to the message */
+ }
+
+ if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECFailure;
+ }
+
+ /*
+ * do not add signer independent certificates
+ */
+
+ pwcb = (options.password != NULL) ? ownpw : NULL;
+ pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
+
+ ecx = NSS_CMSEncoder_Start(cmsg,
+ writeout, out, /* DER output callback */
+ NULL, NULL, /* destination storage */
+ pwcb, pwcb_arg, /* password callback */
+ NULL, NULL, /* decrypt key callback */
+ NULL, NULL); /* detached digests (not used, we feed) */
+
+ for (;;) {
+ if (feof(infile)) break;
+ nb = fread(ibuf, 1, sizeof(ibuf), infile);
+ if (nb == 0) {
+ if (ferror(infile)) {
+ fprintf(stderr, "ERROR: file i/o error.\n");
+ NSS_CMSEncoder_Cancel(ecx);
+ return SECFailure;
+ }
+ /* eof */
+ break;
+ }
+ (void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
+ }
+ if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
+ fprintf(stderr, "ERROR: DER encoding problem.\n");
+ return SECFailure;
+ }
+
+ NSS_CMSMessage_Destroy(cmsg);
+ return SECSuccess;
+}
+
+static int
+envelope(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct envelopeOptionsStr envelopeOptions)
+{
+ SECStatus retval = SECFailure;
+ NSSCMSEncoderContext *ecx;
+ NSSCMSMessage *cmsg = NULL;
+ NSSCMSContentInfo *cinfo;
+ NSSCMSEnvelopedData *envd;
+ NSSCMSRecipientInfo *recipientinfo;
+ int cnt;
+ int nb;
+ char ibuf[4096];
+ PK11PasswordFunc pwcb;
+ void *pwcb_arg;
+ CERTCertificate **recipientcerts;
+ PLArenaPool *tmppoolp = NULL;
+ SECOidTag bulkalgtag;
+ int keysize, i;
+
+ if ((cnt = NSS_CMSArray_Count(envelopeOptions.recipients)) == 0) {
+ fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
+ goto loser;
+ }
+
+ if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+
+ /* XXX find the recipient's certs by email address or nickname */
+ if ((recipientcerts = (CERTCertificate **)NSS_CMSArray_Alloc(tmppoolp, cnt)) == NULL) {
+ fprintf(stderr, "ERROR: out of memory.\n");
+ goto loser;
+ }
+
+ for (i=0; envelopeOptions.recipients[i] != NULL; i++) {
+ if ((recipientcerts[i] = CERT_FindCertByNicknameOrEmailAddr(options.certHandle, envelopeOptions.recipients[i])) == NULL) {
+ SECU_PrintError(progName, "cannot find certificate for \"%s\"", envelopeOptions.recipients[i]);
+ goto loser;
+ }
+ }
+ recipientcerts[i] = NULL;
+
+ /* find a nice bulk algorithm */
+ if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, &keysize) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
+ goto loser;
+ }
+
+ /*
+ * create the message object
+ */
+ cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
+ if (cmsg == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS message.\n");
+ goto loser;
+ }
+ /*
+ * build chain of objects: message->envelopedData->data
+ */
+ if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
+ if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
+ goto loser;
+ }
+ cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
+ /* we're always passing data in, so the content is NULL */
+ if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
+ goto loser;
+ }
+
+ /*
+ * create & attach recipient information
+ */
+ for (i = 0; recipientcerts[i] != NULL; i++) {
+ if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, recipientcerts[i])) == NULL) {
+ fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
+ goto loser;
+ }
+ if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) != SECSuccess) {
+ fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
+ goto loser;
+ }
+ }
+
+ /* we might need a password for diffie hellman key agreement, so set it up... */
+ pwcb = (options.password != NULL) ? ownpw : NULL;
+ pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
+
+ ecx = NSS_CMSEncoder_Start(cmsg,
+ writeout, out, /* DER output callback */
+ NULL, NULL, /* destination storage */
+ pwcb, pwcb_arg, /* password callback */
+ NULL, NULL, /* decrypt key callback (not used) */
+ NULL, NULL); /* detached digests (not used) */
+
+ for (;;) {
+ if (feof(infile)) break;
+ nb = fread(ibuf, 1, sizeof(ibuf), infile);
+ if (nb == 0) {
+ if (ferror(infile)) {
+ fprintf(stderr, "ERROR: file i/o error.\n");
+ NSS_CMSEncoder_Cancel(ecx);
+ goto loser;
+ }
+ /* eof */
+ break;
+ }
+ (void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
+ }
+ if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
+ fprintf(stderr, "ERROR: DER encoding problem.\n");
+ goto loser;
+ }
+
+ retval = SECSuccess;
+
+loser:
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ if (tmppoolp)
+ PORT_FreeArena(tmppoolp, PR_FALSE);
+ return retval;
+}
+
+typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT } Mode;
+
+int
+main(int argc, char **argv)
+{
+ char *progName;
+ FILE *outFile, *inFile;
+ PLOptState *optstate;
+ PLOptStatus status;
+ Mode mode = UNKNOWN;
+ struct decodeOptionsStr decodeOptions = { 0 };
+ struct signOptionsStr signOptions = { 0 };
+ struct envelopeOptionsStr envelopeOptions = { 0 };
+ struct optionsStr options = { 0 };
+ int exitstatus;
+ static char *ptrarray[128] = { 0 };
+ int nrecipients = 0;
+
+ progName = strrchr(argv[0], '/');
+ progName = progName ? progName+1 : argv[0];
+
+ inFile = stdin;
+ outFile = stdout;
+ mode = UNKNOWN;
+ decodeOptions.contentFile = NULL;
+ decodeOptions.suppressContent = PR_FALSE;
+ decodeOptions.headerLevel = -1;
+ options.certUsage = certUsageEmailSigner;
+ options.password = NULL;
+ signOptions.nickname = NULL;
+ signOptions.detached = PR_FALSE;
+ signOptions.signingTime = PR_FALSE;
+ signOptions.smimeProfile = PR_FALSE;
+ signOptions.encryptionKeyPreferenceNick = NULL;
+ envelopeOptions.recipients = NULL;
+
+ /*
+ * Parse command line arguments
+ */
+ optstate = PL_CreateOptState(argc, argv, "DSEnN:TGPY:h:p:i:c:d:o:s:u:r:");
+ while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
+ switch (optstate->option) {
+ case '?':
+ Usage(progName);
+ break;
+
+ case 'D':
+ mode = DECODE;
+ break;
+ case 'S':
+ mode = SIGN;
+ break;
+ case 'E':
+ mode = ENCRYPT;
+ break;
+
+ case 'n':
+ if (mode != DECODE) {
+ fprintf(stderr, "%s: option -n only supported with option -D.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ decodeOptions.suppressContent = PR_TRUE;
+ break;
+
+ case 'N':
+ if (mode != SIGN) {
+ fprintf(stderr, "%s: option -N only supported with option -S.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.nickname = strdup(optstate->value);
+ break;
+
+ case 'Y':
+ if (mode != SIGN) {
+ fprintf(stderr, "%s: option -Y only supported with option -S.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
+ break;
+
+ case 'T':
+ if (mode != SIGN) {
+ fprintf(stderr, "%s: option -T only supported with option -S.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.detached = PR_TRUE;
+ break;
+
+ case 'G':
+ if (mode != SIGN) {
+ fprintf(stderr, "%s: option -G only supported with option -S.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.signingTime = PR_TRUE;
+ break;
+
+ case 'P':
+ if (mode != SIGN) {
+ fprintf(stderr, "%s: option -P only supported with option -S.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ signOptions.smimeProfile = PR_TRUE;
+ break;
+
+ case 'h':
+ if (mode != DECODE) {
+ fprintf(stderr, "%s: option -h only supported with option -D.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ decodeOptions.headerLevel = atoi(optstate->value);
+ if (decodeOptions.headerLevel < 0) {
+ fprintf(stderr, "option -h cannot have a negative value.\n");
+ exit(1);
+ }
+ break;
+
+ case 'p':
+ if (!optstate->value) {
+ fprintf(stderr, "%s: option -p must have a value.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+
+ options.password = strdup(optstate->value);
+ break;
+
+ case 'i':
+ if ((inFile = fopen(optstate->value, "r")) == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'c':
+ if (mode != DECODE) {
+ fprintf(stderr, "%s: option -c only supported with option -D.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+ if ((decodeOptions.contentFile = fopen(optstate->value, "r")) == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'o':
+ if ((outFile = fopen(optstate->value, "w")) == NULL) {
+ fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
+ progName, optstate->value);
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (!optstate->value) {
+ fprintf(stderr, "%s: option -r must have a value.\n", progName);
+ Usage(progName);
+ exit(1);
+ }
+#if 0
+ fprintf(stderr, "recipient = %s\n", optstate->value);
+#endif
+ envelopeOptions.recipients = ptrarray;
+ envelopeOptions.recipients[nrecipients++] = strdup(optstate->value);
+ envelopeOptions.recipients[nrecipients] = NULL;
+ break;
+
+ case 'd':
+ SECU_ConfigDirectory(optstate->value);
+ break;
+
+ case 'u': {
+ int usageType;
+
+ usageType = atoi (strdup(optstate->value));
+ if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
+ return -1;
+ options.certUsage = (SECCertUsage)usageType;
+ break;
+ }
+
+ }
+ }
+
+ /* Call the libsec initialization routines */
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ SECU_PKCS11Init(PR_FALSE);
+ SEC_Init();
+
+ /* open cert database */
+ options.certHandle = OpenCertDB(progName);
+ if (options.certHandle == NULL) {
+ return -1;
+ }
+ CERT_SetDefaultCertDB(options.certHandle);
+
+ exitstatus = 0;
+ switch (mode) {
+ case DECODE:
+ if (decode(outFile, inFile, progName, options, decodeOptions)) {
+ SECU_PrintError(progName, "problem decoding");
+ exitstatus = 1;
+ }
+ break;
+ case SIGN:
+ if (sign(outFile, inFile, progName, options, signOptions)) {
+ SECU_PrintError(progName, "problem signing");
+ exitstatus = 1;
+ }
+ break;
+ case ENCRYPT:
+ if (envelope(outFile, inFile, progName, options, envelopeOptions)) {
+ SECU_PrintError(progName, "problem encrypting");
+ exitstatus = 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "One of options -D, -S or -E must be set.\n");
+ Usage(progName);
+ exitstatus = 1;
+ }
+ if (outFile != stdout)
+ fclose(outFile);
+
+ exit(exitstatus);
+}
diff --git a/security/nss/cmd/smimetools/manifest.mn b/security/nss/cmd/smimetools/manifest.mn
new file mode 100644
index 000000000..9ed96c658
--- /dev/null
+++ b/security/nss/cmd/smimetools/manifest.mn
@@ -0,0 +1,45 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+CORE_DEPTH = ../../..
+
+MODULE = security
+
+CSRCS = cmsutil.c
+
+MYLIB = $(DIST)/lib/libsmime.a
+
+REQUIRES = seccmd dbm
+
+PROGRAM = cmsutil
+SCRIPTS = smime
diff --git a/security/nss/cmd/smimetools/rules.mk b/security/nss/cmd/smimetools/rules.mk
new file mode 100644
index 000000000..51e0baaed
--- /dev/null
+++ b/security/nss/cmd/smimetools/rules.mk
@@ -0,0 +1,37 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+# $Id$
+#
+
+install::
+ $(INSTALL) -m 755 $(SCRIPTS) $(SOURCE_BIN_DIR)
diff --git a/security/nss/cmd/smimetools/smime b/security/nss/cmd/smimetools/smime
new file mode 100755
index 000000000..d70f3d358
--- /dev/null
+++ b/security/nss/cmd/smimetools/smime
@@ -0,0 +1,325 @@
+#!/usr/local/bin/perl
+
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+
+#
+# smime.pl - frontend for S/MIME message generation
+#
+# $Id$
+#
+
+use Getopt::Std;
+
+@boundarychars = ( "0" .. "9", "A" .. "F" );
+
+# path to cmsutil
+$cmsutilpath = "cmsutil";
+
+#
+# Thanks to Gisle Aas <gisle@aas.no> for the encode_base64 function
+# originally taken from MIME-Base64-2.11 at www.cpan.org
+#
+sub encode_base64($)
+{
+ my $res = "";
+ pos($_[0]) = 0; # ensure start at the beginning
+ while ($_[0] =~ /(.{1,45})/gs) {
+ $res .= substr(pack('u', $1), 1); # get rid of length byte after packing
+ chop($res);
+ }
+ $res =~ tr|` -_|AA-Za-z0-9+/|;
+ # fix padding at the end
+ my $padding = (3 - length($_[0]) % 3) % 3;
+ $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
+ # break encoded string into lines of no more than 76 characters each
+ $res =~ s/(.{1,76})/$1\n/g;
+ $res;
+}
+
+#
+# encryptentity($entity, $options) - encrypt an S/MIME entity
+#
+# entity - string containing entire S/MIME entity to encrypt
+# options - options for cmsutil
+#
+# this will generate and return a new multipart/signed entity consisting
+# of the canonicalized original content, plus a signature block.
+#
+sub encryptentity($$)
+{
+ my ($entity, $cmsutiloptions) = @_;
+ my $out = "";
+ my $boundary;
+
+ $tmpencfile = "/tmp/encryptentity.$$";
+
+ #
+ # generate a random boundary string
+ #
+ $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
+
+ #
+ # tell cmsutil to generate a signed CMS message using the canonicalized data
+ # The signedData has detached content (-T) and includes a signing time attribute (-G)
+ #
+ # if we do not provide a password on the command line, here's where we would be asked for it
+ #
+ open(CMS, "|$cmsutilpath -E $cmsutiloptions -o $tmpencfile") or die "ERROR: cannot pipe to cmsutil";
+ print CMS $entity;
+ unless (close(CMS)) {
+ print STDERR "ERROR: encryption failed.\n";
+ unlink($tmpsigfile);
+ exit 1;
+ }
+
+ open (ENC, $tmpencfile) or die "ERROR: cannot find newly generated encrypted content";
+
+ #
+ # construct a new multipart/signed MIME entity consisting of the original content and
+ # the signature
+ #
+ # (we assume that cmsutil generates a SHA1 digest)
+ $out .= "Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m\n";
+ $out .= "Content-Transfer-Encoding: base64\n";
+ $out .= "Content-Disposition: attachment; filename=smime.p7m\n";
+ $out .= "\n"; # end of entity header
+
+ local($/) = undef; # slurp whole file
+ $out .= encode_base64(<ENC>), "\n"; # append base64-encoded signature
+
+ close(ENC);
+ unlink($tmpencfile);
+
+ $out;
+}
+
+#
+# signentity($entity, $options) - sign an S/MIME entity
+#
+# entity - string containing entire S/MIME entity to sign
+# options - options for cmsutil
+#
+# this will generate and return a new multipart/signed entity consisting
+# of the canonicalized original content, plus a signature block.
+#
+sub signentity($$)
+{
+ my ($entity, $cmsutiloptions) = @_;
+ my $out = "";
+ my $boundary;
+
+ $tmpsigfile = "/tmp/signentity.$$";
+
+ #
+ # generate a random boundary string
+ #
+ $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
+
+ #
+ # tell cmsutil to generate a signed CMS message using the canonicalized data
+ # The signedData has detached content (-T) and includes a signing time attribute (-G)
+ #
+ # if we do not provide a password on the command line, here's where we would be asked for it
+ #
+ open(CMS, "|$cmsutilpath -S -T -G $cmsutiloptions -o $tmpsigfile") or die "ERROR: cannot pipe to cmsutil";
+ print CMS $entity;
+ unless (close(CMS)) {
+ print STDERR "ERROR: signature generation failed.\n";
+ unlink($tmpsigfile);
+ exit 1;
+ }
+
+ open (SIG, $tmpsigfile) or die "ERROR: cannot find newly generated signature";
+
+ #
+ # construct a new multipart/signed MIME entity consisting of the original content and
+ # the signature
+ #
+ # (we assume that cmsutil generates a SHA1 digest)
+ $out .= "Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1; boundary=\"${boundary}\"\n";
+ $out .= "\n"; # end of entity header
+ $out .= "This is a cryptographically signed message in MIME format.\n\n"; # explanatory comment
+ $out .= "--${boundary}\n";
+ $out .= "$entity\n"; # the trailing \n seems to be important
+ $out .= "--${boundary}\n";
+ $out .= "Content-Type: application/pkcs7-signature; name=smime.p7s\n";
+ $out .= "Content-Transfer-Encoding: base64\n";
+ $out .= "Content-Disposition: attachment; filename=smime.p7s\n";
+ $out .= "Content-Description: S/MIME Cryptographic Signature\n";
+ $out .= "\n"; # end of signature subentity header
+
+ local($/) = undef; # slurp whole file
+ $out .= encode_base64(<SIG>), "\n"; # append base64-encoded signature
+ $out .= "--${boundary}--\n";
+
+ close(SIG);
+ unlink($tmpsigfile);
+
+ $out;
+}
+
+sub usage {
+ print STDERR "usage: smime [options]\n";
+ print STDERR " options:\n";
+ print STDERR " -S nick generate signed message, use certificate named \"nick\"\n";
+ print STDERR " -p passwd use \"passwd\" as security module password\n";
+ print STDERR " -E rec1[,rec2...] generate encrypted message for recipients\n";
+ print STDERR " -C pathname set pathname of \"cmsutil\"\n";
+ print STDERR "\nWith -S or -E, smime will take a regular RFC822 message or MIME entity\n";
+ print STDERR "and generate a signed or encrypted S/MIME message with the same headers\n";
+ print STDERR "and content from it. The output can be used as input to a MTA.\n";
+}
+
+#
+# start of main procedures
+#
+
+#
+# process command line options
+#
+unless (getopts('S:E:p:C:D')) {
+ usage();
+ exit 1;
+}
+
+unless (defined($opt_S) or defined($opt_E)) {
+ print STDERR "ERROR: -S and/or -E must be specified.\n";
+ usage();
+ exit 1;
+}
+
+$signopts = "";
+$encryptopts = "";
+
+if (defined($opt_S)) {
+ $signopts .= "-N \"$opt_S\" ";
+}
+
+if (defined($opt_p)) {
+ $signopts .= "-p \"$opt_p\" ";
+}
+
+if (defined($opt_E)) {
+ @recipients = split(",", $opt_E);
+ $encryptopts .= "-r ";
+ $encryptopts .= join (" -r ", @recipients);
+}
+
+if (defined($opt_C)) {
+ $cmsutilpath = $opt_C;
+}
+
+#
+# split headers into mime entity headers and RFC822 headers
+# The RFC822 headers are preserved and stay on the outer layer of the message
+#
+$rfc822headers = "";
+$mimeentity = "";
+while (<STDIN>) {
+ last if (/^$/);
+ if (/^content-\S+: /i) {
+ $mimeentity .= $_;
+ } elsif (/^mime-version: /i) {
+ ; # skip it
+ } else {
+ $rfc822headers .= $_;
+ }
+}
+
+#
+# if there are no MIME entity headers, generate some default ones
+#
+if ($mimeentity eq "") {
+ $mimeentity .= "Content-Type: text/plain; charset=us-ascii\n";
+ $mimeentity .= "Content-Transfer-Encoding: 7bit\n";
+}
+
+#
+# generate end of header-LF/LF pair
+#
+$mimeentity .= "\n";
+
+#
+# slurp in the entity body
+#
+$saveRS = $/;
+$/ = undef;
+$mimeentity .= <STDIN>;
+$/ = $saveRS;
+
+if (defined $opt_D) {
+ #
+ # decode
+ #
+
+
+
+} else {
+ #
+ # encode
+ #
+
+ #
+ # canonicalize inner entity (rudimentary yet)
+ # convert single LFs to CRLF
+ # if no Content-Transfer-Encoding header present:
+ # if 8 bit chars present, use Content-Transfer-Encoding: quoted-printable
+ # otherwise, use Content-Transfer-Encoding: 7bit
+ #
+ $mimeentity =~ s/\n/\r\n/mg;
+
+ #
+ # now do the wrapping
+ # we sign first, then encrypt because that's what Communicator needs
+ #
+ if (defined($opt_S)) {
+ $mimeentity = signentity($mimeentity, $signopts);
+ }
+
+ if (defined($opt_E)) {
+ $mimeentity = encryptentity($mimeentity, $encryptopts);
+ }
+
+ #
+ # XXX sign again to do triple wrapping (RFC2634)
+ #
+
+ #
+ # now write out the RFC822 headers
+ # followed by the final $mimeentity
+ #
+ print $rfc822headers;
+ print "MIME-Version: 1.0 (NSS SMIME - http://www.mozilla.org/projects/security)\n"; # set up the flag
+ print $mimeentity;
+}
+
+exit 0;