diff options
Diffstat (limited to 'camel/camel-smime-context.c')
-rw-r--r-- | camel/camel-smime-context.c | 1080 |
1 files changed, 0 insertions, 1080 deletions
diff --git a/camel/camel-smime-context.c b/camel/camel-smime-context.c deleted file mode 100644 index c32d6e4d72..0000000000 --- a/camel/camel-smime-context.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors: Jeffrey Stedfast <fejj@ximian.com> - * Michael Zucchi <notzed@ximian.com> - * - * 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. - * - * Copyright 2003 Ximian, Inc. (www.ximian.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#ifdef HAVE_NSS - -#include "nss.h" -#include <cms.h> -#include <cert.h> -#include <certdb.h> -#include <pkcs11.h> -#include <smime.h> -#include <pkcs11t.h> -#include <pk11func.h> - -#include <errno.h> - -#include <camel/camel-exception.h> -#include <camel/camel-stream-mem.h> -#include <camel/camel-data-wrapper.h> - -#include <camel/camel-mime-part.h> -#include <camel/camel-multipart-signed.h> -#include <camel/camel-stream-fs.h> -#include <camel/camel-stream-filter.h> -#include <camel/camel-mime-filter-basic.h> -#include <camel/camel-mime-filter-canon.h> - -#include "camel-smime-context.h" -#include "camel-operation.h" - -#define d(x) - -struct _CamelSMIMEContextPrivate { - CERTCertDBHandle *certdb; - - char *encrypt_key; - camel_smime_sign_t sign_mode; - - int password_tries; - unsigned int send_encrypt_key_prefs:1; -}; - -static CamelCipherContextClass *parent_class = NULL; - -/* used for decode content callback, for streaming decode */ -static void -sm_write_stream(void *arg, const char *buf, unsigned long len) -{ - camel_stream_write((CamelStream *)arg, buf, len); -} - -static PK11SymKey * -sm_decrypt_key(void *arg, SECAlgorithmID *algid) -{ - printf("Decrypt key called\n"); - return (PK11SymKey *)arg; -} - -static char * -sm_get_passwd(PK11SlotInfo *info, PRBool retry, void *arg) -{ - CamelSMIMEContext *context = arg; - char *pass, *nsspass = NULL; - char *prompt; - CamelException *ex; - - ex = camel_exception_new(); - - /* we got a password, but its asking again, the password we had was wrong */ - if (context->priv->password_tries > 0) { - camel_session_forget_password(((CamelCipherContext *)context)->session, NULL, PK11_GetTokenName(info), NULL); - context->priv->password_tries = 0; - } - - prompt = g_strdup_printf(_("Enter security pass-phrase for `%s'"), PK11_GetTokenName(info)); - pass = camel_session_get_password(((CamelCipherContext *)context)->session, prompt, - CAMEL_SESSION_PASSWORD_SECRET|CAMEL_SESSION_PASSWORD_STATIC, NULL, PK11_GetTokenName(info), ex); - camel_exception_free(ex); - g_free(prompt); - if (pass) { - nsspass = PORT_Strdup(pass); - memset(pass, 0, strlen(pass)); - g_free(pass); - context->priv->password_tries++; - } - - return nsspass; -} - -/** - * camel_smime_context_new: - * @session: session - * - * Creates a new sm cipher context object. - * - * Returns a new sm cipher context object. - **/ -CamelCipherContext * -camel_smime_context_new(CamelSession *session) -{ - CamelCipherContext *cipher; - CamelSMIMEContext *ctx; - - g_return_val_if_fail(CAMEL_IS_SESSION(session), NULL); - - ctx =(CamelSMIMEContext *) camel_object_new(camel_smime_context_get_type()); - - cipher =(CamelCipherContext *) ctx; - cipher->session = session; - camel_object_ref(session); - - return cipher; -} - -void -camel_smime_context_set_encrypt_key(CamelSMIMEContext *context, gboolean use, const char *key) -{ - context->priv->send_encrypt_key_prefs = use; - g_free(context->priv->encrypt_key); - context->priv->encrypt_key = g_strdup(key); -} - -/* set signing mode, clearsigned multipart/signed or enveloped */ -void -camel_smime_context_set_sign_mode(CamelSMIMEContext *context, camel_smime_sign_t type) -{ - context->priv->sign_mode = type; -} - -/* TODO: This is suboptimal, but the only other solution is to pass around NSSCMSMessages */ -guint32 -camel_smime_context_describe_part(CamelSMIMEContext *context, CamelMimePart *part) -{ - guint32 flags = 0; - CamelContentType *ct; - const char *tmp; - - ct = camel_mime_part_get_content_type(part); - - if (camel_content_type_is(ct, "multipart", "signed")) { - tmp = camel_content_type_param(ct, "protocol"); - if (tmp && - (g_ascii_strcasecmp(tmp, ((CamelCipherContext *)context)->sign_protocol) == 0 - || g_ascii_strcasecmp(tmp, "application/pkcs7-signature") == 0)) - flags = CAMEL_SMIME_SIGNED; - } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) { - CamelStreamMem *istream; - NSSCMSMessage *cmsg; - NSSCMSDecoderContext *dec; - - /* FIXME: stream this to the decoder incrementally */ - istream = (CamelStreamMem *)camel_stream_mem_new(); - camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)part), (CamelStream *)istream); - camel_stream_reset((CamelStream *)istream); - - dec = NSS_CMSDecoder_Start(NULL, - NULL, NULL, - sm_get_passwd, context, /* password callback */ - NULL, NULL); /* decrypt key callback */ - - NSS_CMSDecoder_Update(dec, istream->buffer->data, istream->buffer->len); - camel_object_unref(istream); - - cmsg = NSS_CMSDecoder_Finish(dec); - if (cmsg) { - if (NSS_CMSMessage_IsSigned(cmsg)) { - printf("message is signed\n"); - flags |= CAMEL_SMIME_SIGNED; - } - - if (NSS_CMSMessage_IsEncrypted(cmsg)) { - printf("message is encrypted\n"); - flags |= CAMEL_SMIME_ENCRYPTED; - } -#if 0 - if (NSS_CMSMessage_ContainsCertsOrCrls(cmsg)) { - printf("message contains certs or crls\n"); - flags |= CAMEL_SMIME_CERTS; - } -#endif - NSS_CMSMessage_Destroy(cmsg); - } else { - printf("Message could not be parsed\n"); - } - } - - return flags; -} - -static const char * -sm_hash_to_id(CamelCipherContext *context, CamelCipherHash hash) -{ - switch(hash) { - case CAMEL_CIPHER_HASH_MD5: - return "md5"; - case CAMEL_CIPHER_HASH_SHA1: - case CAMEL_CIPHER_HASH_DEFAULT: - return "sha1"; - default: - return NULL; - } -} - -static CamelCipherHash -sm_id_to_hash(CamelCipherContext *context, const char *id) -{ - if (id) { - if (!strcmp(id, "md5")) - return CAMEL_CIPHER_HASH_MD5; - else if (!strcmp(id, "sha1")) - return CAMEL_CIPHER_HASH_SHA1; - } - - return CAMEL_CIPHER_HASH_DEFAULT; -} - -static NSSCMSMessage * -sm_signing_cmsmessage(CamelSMIMEContext *context, const char *nick, SECOidTag hash, int detached, CamelException *ex) -{ - struct _CamelSMIMEContextPrivate *p = context->priv; - NSSCMSMessage *cmsg = NULL; - NSSCMSContentInfo *cinfo; - NSSCMSSignedData *sigd; - NSSCMSSignerInfo *signerinfo; - CERTCertificate *cert= NULL, *ekpcert = NULL; - - if ((cert = CERT_FindUserCertByUsage(p->certdb, - (char *)nick, - certUsageEmailSigner, - PR_FALSE, - NULL)) == NULL) { - camel_exception_setv(ex, 1, "Can't find certificate for '%s'", nick); - return NULL; - } - - cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ - if (cmsg == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS message"); - goto fail; - } - - if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS signedData"); - goto fail; - } - - cinfo = NSS_CMSMessage_GetContentInfo(cmsg); - if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't attach CMS signedData"); - goto fail; - } - - /* if !detatched, the contentinfo will alloc a data item for us */ - cinfo = NSS_CMSSignedData_GetContentInfo(sigd); - if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, detached) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't attach CMS data"); - goto fail; - } - - signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, hash); - if (signerinfo == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS SignerInfo"); - goto fail; - } - - /* we want the cert chain included for this one */ - if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't find cert chain"); - goto fail; - } - - /* SMIME RFC says signing time should always be added */ - if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't add CMS SigningTime"); - goto fail; - } - -#if 0 - /* this can but needn't be added. not sure what general usage is */ - if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { - fprintf(stderr, "ERROR: cannot add SMIMECaps attribute.\n"); - goto loser; - } -#endif - - /* Check if we need to send along our return encrypt cert, rfc2633 2.5.3 */ - if (p->send_encrypt_key_prefs) { - CERTCertificate *enccert = NULL; - - if (p->encrypt_key) { - /* encrypt key has its own nick */ - if ((ekpcert = CERT_FindUserCertByUsage( - p->certdb, - p->encrypt_key, - certUsageEmailRecipient, PR_FALSE, NULL)) == NULL) { - camel_exception_setv(ex, 1, "encryption cert for '%s' does not exist", p->encrypt_key); - goto fail; - } - enccert = ekpcert; - } else if (CERT_CheckCertUsage(cert, certUsageEmailRecipient) == SECSuccess) { - /* encrypt key is signing key */ - enccert = cert; - } else { - /* encrypt key uses same nick */ - if ((ekpcert = CERT_FindUserCertByUsage( - p->certdb, (char *)nick, - certUsageEmailRecipient, PR_FALSE, NULL)) == NULL) { - camel_exception_setv(ex, 1, "encryption cert for '%s' does not exist", nick); - goto fail; - } - enccert = ekpcert; - } - - if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, enccert, p->certdb) != SECSuccess) { - camel_exception_setv(ex, 1, "can't add SMIMEEncKeyPrefs attribute"); - goto fail; - } - - if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, enccert, p->certdb) != SECSuccess) { - camel_exception_setv(ex, 1, "can't add MS SMIMEEncKeyPrefs attribute"); - goto fail; - } - - if (ekpcert != NULL && NSS_CMSSignedData_AddCertificate(sigd, ekpcert) != SECSuccess) { - camel_exception_setv(ex, 1, "can't add add encryption certificate"); - goto fail; - } - } - - if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { - camel_exception_setv(ex, 1, "can't add CMS SignerInfo"); - goto fail; - } - - if (ekpcert) - CERT_DestroyCertificate(ekpcert); - - if (cert) - CERT_DestroyCertificate(cert); - - return cmsg; -fail: - if (ekpcert) - CERT_DestroyCertificate(ekpcert); - - if (cert) - CERT_DestroyCertificate(cert); - - NSS_CMSMessage_Destroy(cmsg); - - return NULL; -} - -static int -sm_sign(CamelCipherContext *context, const char *userid, CamelCipherHash hash, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) -{ - int res = -1; - NSSCMSMessage *cmsg; - CamelStream *ostream, *istream; - SECOidTag sechash; - NSSCMSEncoderContext *enc; - CamelDataWrapper *dw; - CamelContentType *ct; - - switch (hash) { - case CAMEL_CIPHER_HASH_SHA1: - case CAMEL_CIPHER_HASH_DEFAULT: - default: - sechash = SEC_OID_SHA1; - break; - case CAMEL_CIPHER_HASH_MD5: - sechash = SEC_OID_MD5; - break; - } - - cmsg = sm_signing_cmsmessage((CamelSMIMEContext *)context, userid, sechash, - ((CamelSMIMEContext *)context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN, ex); - if (cmsg == NULL) - return -1; - - ostream = camel_stream_mem_new(); - - /* FIXME: stream this, we stream output at least */ - istream = camel_stream_mem_new(); - if (camel_cipher_canonical_to_stream(ipart, - CAMEL_MIME_FILTER_CANON_STRIP - |CAMEL_MIME_FILTER_CANON_CRLF - |CAMEL_MIME_FILTER_CANON_FROM, istream) == -1) { - camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not generate signing data: %s"), g_strerror(errno)); - goto fail; - } - - enc = NSS_CMSEncoder_Start(cmsg, - sm_write_stream, ostream, /* DER output callback */ - NULL, NULL, /* destination storage */ - sm_get_passwd, context, /* password callback */ - NULL, NULL, /* decrypt key callback */ - NULL, NULL ); /* detached digests */ - if (!enc) { - camel_exception_setv(ex, 1, "Cannot create encoder context"); - goto fail; - } - - if (NSS_CMSEncoder_Update(enc, ((CamelStreamMem *)istream)->buffer->data, ((CamelStreamMem *)istream)->buffer->len) != SECSuccess) { - NSS_CMSEncoder_Cancel(enc); - camel_exception_setv(ex, 1, "Failed to add data to CMS encoder"); - goto fail; - } - - if (NSS_CMSEncoder_Finish(enc) != SECSuccess) { - camel_exception_setv(ex, 1, "Failed to encode data"); - goto fail; - } - - res = 0; - - dw = camel_data_wrapper_new(); - camel_stream_reset(ostream); - camel_data_wrapper_construct_from_stream(dw, ostream); - dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY; - - if (((CamelSMIMEContext *)context)->priv->sign_mode == CAMEL_SMIME_SIGN_CLEARSIGN) { - CamelMultipartSigned *mps; - CamelMimePart *sigpart; - - sigpart = camel_mime_part_new(); - ct = camel_content_type_new("application", "x-pkcs7-signature"); - camel_content_type_set_param(ct, "name", "smime.p7s"); - camel_data_wrapper_set_mime_type_field(dw, ct); - camel_content_type_unref(ct); - - camel_medium_set_content_object((CamelMedium *)sigpart, dw); - - camel_mime_part_set_filename(sigpart, "smime.p7s"); - camel_mime_part_set_disposition(sigpart, "attachment"); - camel_mime_part_set_encoding(sigpart, CAMEL_TRANSFER_ENCODING_BASE64); - - mps = camel_multipart_signed_new(); - ct = camel_content_type_new("multipart", "signed"); - camel_content_type_set_param(ct, "micalg", camel_cipher_hash_to_id(context, hash)); - camel_content_type_set_param(ct, "protocol", context->sign_protocol); - camel_data_wrapper_set_mime_type_field((CamelDataWrapper *)mps, ct); - camel_content_type_unref(ct); - camel_multipart_set_boundary((CamelMultipart *)mps, NULL); - - mps->signature = sigpart; - mps->contentraw = istream; - camel_stream_reset(istream); - camel_object_ref(istream); - - camel_medium_set_content_object((CamelMedium *)opart, (CamelDataWrapper *)mps); - } else { - ct = camel_content_type_new("application", "x-pkcs7-mime"); - camel_content_type_set_param(ct, "name", "smime.p7m"); - camel_content_type_set_param(ct, "smime-type", "signed-data"); - camel_data_wrapper_set_mime_type_field(dw, ct); - camel_content_type_unref(ct); - - camel_medium_set_content_object((CamelMedium *)opart, dw); - - camel_mime_part_set_filename(opart, "smime.p7m"); - camel_mime_part_set_description(opart, "S/MIME Signed Message"); - camel_mime_part_set_disposition(opart, "attachment"); - camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64); - } - - camel_object_unref(dw); -fail: - camel_object_unref(ostream); - camel_object_unref(istream); - - return res; -} - -static const char * -sm_status_description(NSSCMSVerificationStatus status) -{ - /* could use this but then we can't control i18n? */ - /*NSS_CMSUtil_VerificationStatusToString(status));*/ - - switch(status) { - case NSSCMSVS_Unverified: - default: - return _("Unverified"); - case NSSCMSVS_GoodSignature: - return _("Good signature"); - case NSSCMSVS_BadSignature: - return _("Bad signature"); - case NSSCMSVS_DigestMismatch: - return _("Content tampered with or altered in transit"); - case NSSCMSVS_SigningCertNotFound: - return _("Signing certificate not found"); - case NSSCMSVS_SigningCertNotTrusted: - return _("Signing certificate not trusted"); - case NSSCMSVS_SignatureAlgorithmUnknown: - return _("Signature algorithm unknown"); - case NSSCMSVS_SignatureAlgorithmUnsupported: - return _("Siganture algorithm unsupported"); - case NSSCMSVS_MalformedSignature: - return _("Malformed signature"); - case NSSCMSVS_ProcessingError: - return _("Processing error"); - } -} - -static CamelCipherValidity * -sm_verify_cmsg(CamelCipherContext *context, NSSCMSMessage *cmsg, CamelStream *extstream, CamelException *ex) -{ - struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv; - NSSCMSSignedData *sigd = NULL; - NSSCMSEnvelopedData *envd; - NSSCMSEncryptedData *encd; - SECAlgorithmID **digestalgs; - NSSCMSDigestContext *digcx; - int count, i, nsigners, j; - SECItem **digests; - PLArenaPool *poolp = NULL; - CamelStreamMem *mem; - NSSCMSVerificationStatus status; - CamelCipherValidity *valid; - GString *description; - - description = g_string_new(""); - valid = camel_cipher_validity_new(); - camel_cipher_validity_set_valid(valid, TRUE); - status = NSSCMSVS_Unverified; - - /* NB: this probably needs to go into a decoding routine that can be used for processing - enveloped data too */ - count = NSS_CMSMessage_ContentLevelCount(cmsg); - for (i = 0; i < count; i++) { - NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cmsg, i); - SECOidTag typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo); - - switch (typetag) { - case SEC_OID_PKCS7_SIGNED_DATA: - sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo); - if (sigd == NULL) { - camel_exception_setv(ex, 1, "No signedData in signature"); - goto fail; - } - - /* need to build digests of the content */ - if (!NSS_CMSSignedData_HasDigests(sigd)) { - if (extstream == NULL) { - camel_exception_setv(ex, 1, "Digests missing from enveloped data"); - goto fail; - } - - if ((poolp = PORT_NewArena(1024)) == NULL) { - camel_exception_setv(ex, 1, "out of memory"); - goto fail; - } - - digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd); - - digcx = NSS_CMSDigestContext_StartMultiple(digestalgs); - if (digcx == NULL) { - camel_exception_setv(ex, 1, "Cannot calculate digests"); - goto fail; - } - - mem = (CamelStreamMem *)camel_stream_mem_new(); - camel_stream_write_to_stream(extstream, (CamelStream *)mem); - NSS_CMSDigestContext_Update(digcx, mem->buffer->data, mem->buffer->len); - camel_object_unref(mem); - - if (NSS_CMSDigestContext_FinishMultiple(digcx, poolp, &digests) != SECSuccess) { - camel_exception_setv(ex, 1, "Cannot calculate digests"); - goto fail; - } - - if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) { - camel_exception_setv(ex, 1, "Cannot set message digests"); - goto fail; - } - - PORT_FreeArena(poolp, PR_FALSE); - poolp = NULL; - } - - /* import the certificates */ - if (NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailSigner, PR_FALSE) != SECSuccess) { - camel_exception_setv(ex, 1, "cert import failed"); - goto fail; - } - - /* check for certs-only message */ - nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); - if (nsigners == 0) { - /* ?? Should we check other usages? */ - NSS_CMSSignedData_ImportCerts(sigd, p->certdb, certUsageEmailSigner, PR_TRUE); - if (NSS_CMSSignedData_VerifyCertsOnly(sigd, p->certdb, certUsageEmailSigner) != SECSuccess) { - g_string_printf(description, "Certficate only message, cannot verify certificates"); - } else { - status = NSSCMSVS_GoodSignature; - g_string_printf(description, "Certficate only message, certificates imported and verified"); - } - } else { - if (!NSS_CMSSignedData_HasDigests(sigd)) { - camel_exception_setv(ex, 1, "Can't find signature digests"); - goto fail; - } - - for (j = 0; j < nsigners; j++) { - NSSCMSSignerInfo *si; - char *cn, *em; - - si = NSS_CMSSignedData_GetSignerInfo(sigd, j); - NSS_CMSSignedData_VerifySignerInfo(sigd, j, p->certdb, certUsageEmailSigner); - - status = NSS_CMSSignerInfo_GetVerificationStatus(si); - - cn = NSS_CMSSignerInfo_GetSignerCommonName(si); - em = NSS_CMSSignerInfo_GetSignerEmailAddress(si); - - g_string_append_printf(description, _("Signer: %s <%s>: %s\n"), - cn?cn:"<unknown>", em?em:"<unknown>", - sm_status_description(status)); - - camel_cipher_validity_add_certinfo(valid, CAMEL_CIPHER_VALIDITY_SIGN, cn, em); - - if (cn) - PORT_Free(cn); - if (em) - PORT_Free(em); - - if (status != NSSCMSVS_GoodSignature) - camel_cipher_validity_set_valid(valid, FALSE); - } - } - break; - case SEC_OID_PKCS7_ENVELOPED_DATA: - envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo); - break; - case SEC_OID_PKCS7_ENCRYPTED_DATA: - encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo); - break; - case SEC_OID_PKCS7_DATA: - break; - default: - break; - } - } - - camel_cipher_validity_set_valid(valid, status == NSSCMSVS_GoodSignature); - camel_cipher_validity_set_description(valid, description->str); - g_string_free(description, TRUE); - - return valid; - -fail: - camel_cipher_validity_free(valid); - g_string_free(description, TRUE); - - return NULL; -} - -static CamelCipherValidity * -sm_verify(CamelCipherContext *context, CamelMimePart *ipart, CamelException *ex) -{ - NSSCMSDecoderContext *dec; - NSSCMSMessage *cmsg; - CamelStreamMem *mem; - CamelStream *constream; - CamelCipherValidity *valid = NULL; - CamelContentType *ct; - const char *tmp; - CamelMimePart *sigpart; - CamelDataWrapper *dw; - - dw = camel_medium_get_content_object((CamelMedium *)ipart); - ct = dw->mime_type; - - /* FIXME: we should stream this to the decoder */ - mem = (CamelStreamMem *)camel_stream_mem_new(); - - if (camel_content_type_is(ct, "multipart", "signed")) { - CamelMultipart *mps = (CamelMultipart *)dw; - - tmp = camel_content_type_param(ct, "protocol"); - if (!CAMEL_IS_MULTIPART_SIGNED(mps) - || tmp == NULL - || (g_ascii_strcasecmp(tmp, context->sign_protocol) != 0 - && g_ascii_strcasecmp(tmp, "application/pkcs7-signature") != 0)) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot verify message signature: Incorrect message format")); - goto fail; - } - - constream = camel_multipart_signed_get_content_stream((CamelMultipartSigned *)mps, ex); - if (constream == NULL) - goto fail; - - sigpart = camel_multipart_get_part(mps, CAMEL_MULTIPART_SIGNED_SIGNATURE); - if (sigpart == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot verify message signature: Incorrect message format")); - goto fail; - } - } else if (camel_content_type_is(ct, "application", "x-pkcs7-mime")) { - sigpart = ipart; - } else { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Cannot verify message signature: Incorrect message format")); - goto fail; - } - - dec = NSS_CMSDecoder_Start(NULL, - NULL, NULL, /* content callback */ - sm_get_passwd, context, /* password callback */ - NULL, NULL); /* decrypt key callback */ - - camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)sigpart), (CamelStream *)mem); - (void)NSS_CMSDecoder_Update(dec, mem->buffer->data, mem->buffer->len); - cmsg = NSS_CMSDecoder_Finish(dec); - if (cmsg == NULL) { - camel_exception_setv(ex, 1, "Decoder failed"); - goto fail; - } - - valid = sm_verify_cmsg(context, cmsg, constream, ex); - - NSS_CMSMessage_Destroy(cmsg); -fail: - camel_object_unref(mem); - if (constream) - camel_object_unref(constream); - - return valid; -} - -static int -sm_encrypt(CamelCipherContext *context, const char *userid, GPtrArray *recipients, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) -{ - struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv; - /*NSSCMSRecipientInfo **recipient_infos;*/ - CERTCertificate **recipient_certs = NULL; - NSSCMSContentInfo *cinfo; - PK11SymKey *bulkkey = NULL; - SECOidTag bulkalgtag; - int bulkkeysize, i; - CK_MECHANISM_TYPE type; - PK11SlotInfo *slot; - PLArenaPool *poolp; - NSSCMSMessage *cmsg = NULL; - NSSCMSEnvelopedData *envd; - NSSCMSEncoderContext *enc = NULL; - CamelStreamMem *mem; - CamelStream *ostream = NULL; - CamelDataWrapper *dw; - CamelContentType *ct; - - poolp = PORT_NewArena(1024); - if (poolp == NULL) { - camel_exception_setv(ex, 1, "Out of memory"); - return -1; - } - - /* Lookup all recipients certs, for later working */ - recipient_certs = (CERTCertificate **)PORT_ArenaZAlloc(poolp, sizeof(*recipient_certs[0])*(recipients->len + 1)); - if (recipient_certs == NULL) { - camel_exception_setv(ex, 1, "Out of memory"); - goto fail; - } - - for (i=0;i<recipients->len;i++) { - recipient_certs[i] = CERT_FindCertByNicknameOrEmailAddr(p->certdb, recipients->pdata[i]); - if (recipient_certs[i] == NULL) { - camel_exception_setv(ex, 1, "Can't find certificate for `%s'", recipients->pdata[i]); - goto fail; - } - } - - /* Find a common algorithm, probably 3DES anyway ... */ - if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipient_certs, &bulkalgtag, &bulkkeysize) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't find common bulk encryption algorithm"); - goto fail; - } - - /* Generate a new bulk key based on the common algorithm - expensive */ - type = PK11_AlgtagToMechanism(bulkalgtag); - slot = PK11_GetBestSlot(type, context); - if (slot == NULL) { - /* PORT_GetError(); ?? */ - camel_exception_setv(ex, 1, "Can't allocate slot for encryption bulk key"); - goto fail; - } - - bulkkey = PK11_KeyGen(slot, type, NULL, bulkkeysize/8, context); - PK11_FreeSlot(slot); - - /* Now we can start building the message */ - /* msg->envelopedData->data */ - cmsg = NSS_CMSMessage_Create(NULL); - if (cmsg == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS Message"); - goto fail; - } - - envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, bulkkeysize); - if (envd == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS EnvelopedData"); - goto fail; - } - - cinfo = NSS_CMSMessage_GetContentInfo(cmsg); - if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't attach CMS EnvelopedData"); - goto fail; - } - - cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); - if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't attach CMS data object"); - goto fail; - } - - /* add recipient certs */ - for (i=0;recipient_certs[i];i++) { - NSSCMSRecipientInfo *ri = NSS_CMSRecipientInfo_Create(cmsg, recipient_certs[i]); - - if (ri == NULL) { - camel_exception_setv(ex, 1, "Can't create CMS RecipientInfo"); - goto fail; - } - - if (NSS_CMSEnvelopedData_AddRecipient(envd, ri) != SECSuccess) { - camel_exception_setv(ex, 1, "Can't add CMS RecipientInfo"); - goto fail; - } - } - - /* dump it out */ - ostream = camel_stream_mem_new(); - enc = NSS_CMSEncoder_Start(cmsg, - sm_write_stream, ostream, - NULL, NULL, - sm_get_passwd, context, - sm_decrypt_key, bulkkey, - NULL, NULL); - if (enc == NULL) { - camel_exception_setv(ex, 1, "Can't create encoder context"); - goto fail; - } - - /* FIXME: Stream the input */ - /* FIXME: Canonicalise the input? */ - mem = (CamelStreamMem *)camel_stream_mem_new(); - camel_data_wrapper_write_to_stream((CamelDataWrapper *)ipart, (CamelStream *)mem); - if (NSS_CMSEncoder_Update(enc, mem->buffer->data, mem->buffer->len) != SECSuccess) { - NSS_CMSEncoder_Cancel(enc); - camel_object_unref(mem); - camel_exception_setv(ex, 1, "Failed to add data to encoder"); - goto fail; - } - camel_object_unref(mem); - - if (NSS_CMSEncoder_Finish(enc) != SECSuccess) { - camel_exception_setv(ex, 1, "Failed to encode data"); - goto fail; - } - - PK11_FreeSymKey(bulkkey); - NSS_CMSMessage_Destroy(cmsg); - for (i=0;recipient_certs[i];i++) - CERT_DestroyCertificate(recipient_certs[i]); - PORT_FreeArena(poolp, PR_FALSE); - - dw = camel_data_wrapper_new(); - camel_data_wrapper_construct_from_stream(dw, ostream); - camel_object_unref(ostream); - dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY; - - ct = camel_content_type_new("application", "x-pkcs7-mime"); - camel_content_type_set_param(ct, "name", "smime.p7m"); - camel_content_type_set_param(ct, "smime-type", "enveloped-data"); - camel_data_wrapper_set_mime_type_field(dw, ct); - camel_content_type_unref(ct); - - camel_medium_set_content_object((CamelMedium *)opart, dw); - camel_object_unref(dw); - - camel_mime_part_set_disposition(opart, "attachment"); - camel_mime_part_set_filename(opart, "smime.p7m"); - camel_mime_part_set_description(opart, "S/MIME Encrypted Message"); - camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64); - - return 0; - -fail: - if (ostream) - camel_object_unref(ostream); - if (cmsg) - NSS_CMSMessage_Destroy(cmsg); - if (bulkkey) - PK11_FreeSymKey(bulkkey); - - if (recipient_certs) { - for (i=0;recipient_certs[i];i++) - CERT_DestroyCertificate(recipient_certs[i]); - } - - PORT_FreeArena(poolp, PR_FALSE); - - return -1; -} - -static CamelCipherValidity * -sm_decrypt(CamelCipherContext *context, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) -{ - NSSCMSDecoderContext *dec; - NSSCMSMessage *cmsg; - CamelStreamMem *istream; - CamelStream *ostream; - CamelCipherValidity *valid = NULL; - - /* FIXME: This assumes the content is only encrypted. Perhaps its ok for - this api to do this ... */ - - ostream = camel_stream_mem_new(); - - /* FIXME: stream this to the decoder incrementally */ - istream = (CamelStreamMem *)camel_stream_mem_new(); - camel_data_wrapper_decode_to_stream(camel_medium_get_content_object((CamelMedium *)ipart), (CamelStream *)istream); - camel_stream_reset((CamelStream *)istream); - - dec = NSS_CMSDecoder_Start(NULL, - sm_write_stream, ostream, /* content callback */ - sm_get_passwd, context, /* password callback */ - NULL, NULL); /* decrypt key callback */ - - if (NSS_CMSDecoder_Update(dec, istream->buffer->data, istream->buffer->len) != SECSuccess) { - printf("decoder update failed\n"); - } - camel_object_unref(istream); - - cmsg = NSS_CMSDecoder_Finish(dec); - if (cmsg == NULL) { - camel_exception_setv(ex, 1, "Decoder failed, error %d", PORT_GetError()); - goto fail; - } - -#if 0 - /* not sure if we really care about this? */ - if (!NSS_CMSMessage_IsEncrypted(cmsg)) { - camel_exception_setv(ex, 1, "S/MIME Decrypt: No encrypted content found"); - NSS_CMSMessage_Destroy(cmsg); - goto fail; - } -#endif - - camel_stream_reset(ostream); - camel_data_wrapper_construct_from_stream((CamelDataWrapper *)opart, ostream); - - if (NSS_CMSMessage_IsSigned(cmsg)) { - valid = sm_verify_cmsg(context, cmsg, NULL, ex); - } else { - valid = camel_cipher_validity_new(); - valid->encrypt.description = g_strdup(_("Encrypted content")); - valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED; - } - - NSS_CMSMessage_Destroy(cmsg); -fail: - camel_object_unref(ostream); - - return valid; -} - -static int -sm_import_keys(CamelCipherContext *context, CamelStream *istream, CamelException *ex) -{ - camel_exception_setv(ex, 1, "import keys: unimplemented"); - - return -1; -} - -static int -sm_export_keys(CamelCipherContext *context, GPtrArray *keys, CamelStream *ostream, CamelException *ex) -{ - camel_exception_setv(ex, 1, "export keys: unimplemented"); - - return -1; -} - -/* ********************************************************************** */ - -static void -camel_smime_context_class_init(CamelSMIMEContextClass *klass) -{ - CamelCipherContextClass *cipher_class = CAMEL_CIPHER_CONTEXT_CLASS(klass); - - parent_class = CAMEL_CIPHER_CONTEXT_CLASS(camel_type_get_global_classfuncs(camel_cipher_context_get_type())); - - cipher_class->hash_to_id = sm_hash_to_id; - cipher_class->id_to_hash = sm_id_to_hash; - cipher_class->sign = sm_sign; - cipher_class->verify = sm_verify; - cipher_class->encrypt = sm_encrypt; - cipher_class->decrypt = sm_decrypt; - cipher_class->import_keys = sm_import_keys; - cipher_class->export_keys = sm_export_keys; -} - -static void -camel_smime_context_init(CamelSMIMEContext *context) -{ - CamelCipherContext *cipher =(CamelCipherContext *) context; - - cipher->sign_protocol = "application/x-pkcs7-signature"; - cipher->encrypt_protocol = "application/x-pkcs7-mime"; - cipher->key_protocol = "application/x-pkcs7-signature"; - - context->priv = g_malloc0(sizeof(*context->priv)); - context->priv->certdb = CERT_GetDefaultCertDB(); - context->priv->sign_mode = CAMEL_SMIME_SIGN_CLEARSIGN; - context->priv->password_tries = 0; -} - -static void -camel_smime_context_finalise(CamelObject *object) -{ - CamelSMIMEContext *context = (CamelSMIMEContext *)object; - - /* FIXME: do we have to free the certdb? */ - - g_free(context->priv); -} - -CamelType -camel_smime_context_get_type(void) -{ - static CamelType type = CAMEL_INVALID_TYPE; - - if (type == CAMEL_INVALID_TYPE) { - type = camel_type_register(camel_cipher_context_get_type(), - "CamelSMIMEContext", - sizeof(CamelSMIMEContext), - sizeof(CamelSMIMEContextClass), - (CamelObjectClassInitFunc) camel_smime_context_class_init, - NULL, - (CamelObjectInitFunc) camel_smime_context_init, - (CamelObjectFinalizeFunc) camel_smime_context_finalise); - } - - return type; -} - -#endif /* HAVE_NSS */ |