diff options
Diffstat (limited to 'camel')
-rw-r--r-- | camel/ChangeLog | 14 | ||||
-rw-r--r-- | camel/Makefile.am | 6 | ||||
-rw-r--r-- | camel/camel-cms-context.c | 324 | ||||
-rw-r--r-- | camel/camel-cms-context.h | 128 | ||||
-rw-r--r-- | camel/camel-smime-context.c | 1080 | ||||
-rw-r--r-- | camel/camel-smime-context.h | 20 | ||||
-rw-r--r-- | camel/camel-smime-utils.c | 126 | ||||
-rw-r--r-- | camel/camel-smime-utils.h (renamed from camel/camel-smime.h) | 28 | ||||
-rw-r--r-- | camel/camel-smime.c | 504 |
9 files changed, 1291 insertions, 939 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index a56052f885..fb13e24c4d 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,17 @@ +2001-05-31 Jeffrey Stedfast <fejj@ximian.com> + + * camel-cms-context.[c,h]: New virtual class for manipulating + cryptographic message syntax messages (like S/MIME). + + * camel-smime-context.[c,h]: Modified to inherit from the + CamelCMSContext class rather than the CamelCipherContext class. + + * camel-smime.[c,h]: Removed - just use camel-smime-context + directly. + + * camel-smime-utils.[c,h]: New source files. Moved the 2 useful + functions from camel-smime.[c,h] into here. + 2001-05-30 Dan Winship <danw@ximian.com> * camel-charset-map.c: Redo the BUILD_MAP code to not depend on diff --git a/camel/Makefile.am b/camel/Makefile.am index 8284d35abc..c3a964b991 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -22,6 +22,7 @@ libcamel_la_SOURCES = \ broken-date-parser.c \ camel-address.c \ camel-cipher-context.c \ + camel-cms-context.c \ camel-data-wrapper.c \ camel-digest-folder.c \ camel-disco-diary.c \ @@ -60,7 +61,7 @@ libcamel_la_SOURCES = \ camel-pgp-context.c \ camel-pgp-mime.c \ camel-smime-context.c \ - camel-smime.c \ + camel-smime-utils.c \ camel-provider.c \ camel-remote-store.c \ camel-sasl.c \ @@ -103,6 +104,7 @@ libcamelinclude_HEADERS = \ camel-address.h \ camel-charset-map.h \ camel-cipher-context.h \ + camel-cms-context.h \ camel-data-wrapper.h \ camel-digest-folder.h \ camel-disco-diary.h \ @@ -142,7 +144,7 @@ libcamelinclude_HEADERS = \ camel-pgp-context.h \ camel-pgp-mime.h \ camel-smime-context.h \ - camel-smime.h \ + camel-smime-utils.h \ camel-provider.h \ camel-remote-store.h \ camel-sasl.h \ diff --git a/camel/camel-cms-context.c b/camel/camel-cms-context.c new file mode 100644 index 0000000000..dea3951877 --- /dev/null +++ b/camel/camel-cms-context.c @@ -0,0 +1,324 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 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 + +#include "camel-cms-context.h" + +#include <glib.h> + +#ifdef ENABLE_THREADS +#include <pthread.h> +#define CMS_LOCK(ctx) g_mutex_lock (((CamelCMSContext *) ctx)->priv->lock) +#define CMS_UNLOCK(ctx) g_mutex_unlock (((CamelCMSContext *) ctx)->priv->lock); +#else +#define CMS_LOCK(ctx) +#define CMS_UNLOCK(ctx) +#endif + +#define d(x) + +#define CCC_CLASS(o) CAMEL_CMS_CONTEXT_CLASS(CAMEL_OBJECT_GET_CLASS(o)) + +struct _CamelCMSContextPrivate { +#ifdef ENABLE_THREADS + GMutex *lock; +#endif +}; + +static CamelMimeMessage *cms_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex); + +static CamelMimeMessage *cms_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +static CamelMimeMessage *cms_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +static CamelMimeMessage *cms_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +static CamelMimeMessage *cms_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex); + +static CamelObjectClass *parent_class; + +static void +camel_cms_context_init (CamelCMSContext *context) +{ + context->priv = g_new0 (struct _CamelCMSContextPrivate, 1); +#ifdef ENABLE_THREADS + context->priv->lock = g_mutex_new (); +#endif +} + +static void +camel_cms_context_finalise (CamelObject *o) +{ + CamelCMSContext *context = (CamelCMSContext *)o; + + camel_object_unref (CAMEL_OBJECT (context->session)); + +#ifdef ENABLE_THREADS + g_mutex_free (context->priv->lock); +#endif + + g_free (context->priv); +} + +static void +camel_cms_context_class_init (CamelCMSContextClass *camel_cms_context_class) +{ + parent_class = camel_type_get_global_classfuncs (camel_object_get_type ()); + + camel_cms_context_class->sign = cms_sign; + camel_cms_context_class->certsonly = cms_certsonly; + camel_cms_context_class->encrypt = cms_encrypt; + camel_cms_context_class->envelope = cms_envelope; + camel_cms_context_class->decode = cms_decode; +} + +CamelType +camel_cms_context_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + type = camel_type_register (camel_object_get_type (), + "CamelCMSContext", + sizeof (CamelCMSContext), + sizeof (CamelCMSContextClass), + (CamelObjectClassInitFunc) camel_cms_context_class_init, + NULL, + (CamelObjectInitFunc) camel_cms_context_init, + (CamelObjectFinalizeFunc) camel_cms_context_finalise); + } + + return type; +} + + +/** + * camel_cms_context_new: + * @session: CamelSession + * @encryption_key: preferred encryption key nickname + * + * This creates a new CamelCMSContext object which is used to sign, + * encrypt, envelope and decode CMS messages. + * + * Return value: the new CamelCMSContext + **/ +CamelCMSContext * +camel_cms_context_new (CamelSession *session) +{ + CamelCMSContext *context; + + g_return_val_if_fail (session != NULL, NULL); + g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); + + context = CAMEL_CMS_CONTEXT (camel_object_new (CAMEL_CMS_CONTEXT_TYPE)); + + camel_object_ref (CAMEL_OBJECT (session)); + context->session = session; + + return context; +} + + +/** + * camel_cms_context_construct: + * @context: CMS Context + * @session: CamelSession + * + * Construct the CMS Context. + **/ +void +camel_cms_context_construct (CamelCMSContext *context, CamelSession *session) +{ + g_return_if_fail (CAMEL_IS_CMS_CONTEXT (context)); + g_return_if_fail (CAMEL_IS_SESSION (session)); + + camel_object_ref (CAMEL_OBJECT (session)); + context->session = session; +} + + +static CamelMimeMessage * +cms_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex) +{ + g_warning ("Using default CamelCMSContext::sign() method."); + + return NULL; +} + + +CamelMimeMessage * +camel_cms_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex) +{ + g_return_val_if_fail (CAMEL_IS_CMS_CONTEXT (ctx), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_val_if_fail (userid != NULL, NULL); + + return CCC_CLASS (ctx)->sign (ctx, message, userid, signing_time, detached, ex); +} + + +static CamelMimeMessage * +cms_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_warning ("Using default CamelCMSContext::certsonly() method."); + + return NULL; +} + + +CamelMimeMessage * +camel_cms_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_return_val_if_fail (CAMEL_IS_CMS_CONTEXT (ctx), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_val_if_fail (userid != NULL, NULL); + g_return_val_if_fail (recipients != NULL, NULL); + + return CCC_CLASS (ctx)->certsonly (ctx, message, userid, recipients, ex); +} + + +static CamelMimeMessage * +cms_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_warning ("Using default CamelCMSContext::envelope() method."); + + return NULL; +} + + +CamelMimeMessage * +camel_cms_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_return_val_if_fail (CAMEL_IS_CMS_CONTEXT (ctx), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_val_if_fail (userid != NULL, NULL); + g_return_val_if_fail (recipients != NULL, NULL); + + return CCC_CLASS (ctx)->envelope (ctx, message, userid, recipients, ex); +} + + +static CamelMimeMessage * +cms_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_warning ("Using default CamelCMSContext::encrypt() method."); + + return NULL; +} + + +CamelMimeMessage * +camel_cms_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + g_return_val_if_fail (CAMEL_IS_CMS_CONTEXT (ctx), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_val_if_fail (userid != NULL, NULL); + g_return_val_if_fail (recipients != NULL, NULL); + + return CCC_CLASS (ctx)->encrypt (ctx, message, userid, recipients, ex); +} + + +static CamelMimeMessage * +cms_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex) +{ + g_warning ("Using default CamelCMSContext::decode() method."); + + return NULL; +} + + +CamelMimeMessage * +camel_cms_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex) +{ + g_return_val_if_fail (CAMEL_IS_CMS_CONTEXT (ctx), NULL); + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + + return CCC_CLASS (ctx)->decode (ctx, message, info, ex); +} + + +void +camel_cms_signer_free (CamelCMSSigner *signer) +{ + CamelCMSSigner *next; + + if (!signer) + return; + + while (signer) { + next = signer->next; + g_free (signer->signercn); + g_free (signer->status); + g_free (signer); + signer = next; + } +} + + +void +camel_cms_validity_info_free (CamelCMSValidityInfo *info) +{ + CamelCMSValidityInfo *next; + + if (!info) + return; + + while (info) { + next = info->next; + if (info->type == CAMEL_CMS_TYPE_SIGNED) + camel_cms_signer_free (info->signers); + g_free (info); + info = next; + } +} diff --git a/camel/camel-cms-context.h b/camel/camel-cms-context.h new file mode 100644 index 0000000000..b68547e8b0 --- /dev/null +++ b/camel/camel-cms-context.h @@ -0,0 +1,128 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 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. + * + */ + +#ifndef CAMEL_CMS_CONTEXT_H +#define CAMEL_CMS_CONTEXT_H + +#include <camel/camel-session.h> +#include <camel/camel-stream.h> +#include <camel/camel-exception.h> +#include <camel/camel-mime-message.h> + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#define CAMEL_CMS_CONTEXT_TYPE (camel_cms_context_get_type ()) +#define CAMEL_CMS_CONTEXT(obj) (CAMEL_CHECK_CAST((obj), CAMEL_CMS_CONTEXT_TYPE, CamelCMSContext)) +#define CAMEL_CMS_CONTEXT_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_CMS_CONTEXT_TYPE, CamelCMSContextClass)) +#define CAMEL_IS_CMS_CONTEXT(o) (CAMEL_CHECK_TYPE((o), CAMEL_CMS_CONTEXT_TYPE)) + +typedef enum { + CAMEL_CMS_TYPE_DATA, + CAMEL_CMS_TYPE_SIGNED, + CAMEL_CMS_TYPE_ENVELOPED, + CAMEL_CMS_TYPE_ENCRYPTED +} CamelCMSType; + +typedef struct _CamelCMSSigner { + struct _CamelCMSSigner *next; + char *signercn; + char *status; +} CamelCMSSigner; + +typedef struct _CamelCMSValidityInfo { + struct _CamelCMSValidityInfo *next; + CamelCMSType type; + CamelCMSSigner *signers; +} CamelCMSValidityInfo; + + +typedef struct _CamelCMSContext { + CamelObject parent_object; + + struct _CamelCMSContextPrivate *priv; + + CamelSession *session; +} CamelCMSContext; + +typedef struct _CamelCMSContextClass { + CamelObjectClass parent_class; + + CamelMimeMessage *(*sign) (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex); + + CamelMimeMessage *(*certsonly) (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + + CamelMimeMessage *(*encrypt) (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + + CamelMimeMessage *(*envelope) (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + + CamelMimeMessage *(*decode) (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex); + +} CamelCMSContextClass; + +CamelType camel_cms_context_get_type (void); + +CamelCMSContext *camel_cms_context_new (CamelSession *session); + +void camel_cms_context_construct (CamelCMSContext *context, CamelSession *session); + +/* cms routines */ +CamelMimeMessage *camel_cms_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex); + +CamelMimeMessage *camel_cms_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +CamelMimeMessage *camel_cms_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +CamelMimeMessage *camel_cms_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +CamelMimeMessage *camel_cms_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex); + + +void camel_cms_signer_free (CamelCMSSigner *signer); + +void camel_cms_validity_info_free (CamelCMSValidityInfo *info); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_CMS_CONTEXT_H */ diff --git a/camel/camel-smime-context.c b/camel/camel-smime-context.c index 7115c13ad1..7d2814fb82 100644 --- a/camel/camel-smime-context.c +++ b/camel/camel-smime-context.c @@ -27,18 +27,14 @@ #ifdef HAVE_NSS #include "camel-smime-context.h" +#include "camel-mime-filter-from.h" +#include "camel-mime-filter-crlf.h" +#include "camel-stream-filter.h" #include "camel-stream-fs.h" #include "camel-stream-mem.h" #include "nss.h" -#include <cert.h> -#include <certt.h> -#include <certdb.h> -#include <hasht.h> -#include <keylow.h> -#include <secpkcs7.h> -#include <secmime.h> -#include <smime.h> +#include <cms.h> #include <gtk/gtk.h> /* for _() macro */ @@ -49,20 +45,24 @@ struct _CamelSMimeContextPrivate { }; -static int smime_sign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash, - CamelStream *istream, CamelStream *ostream, CamelException *ex); -static int smime_clearsign (CamelCipherContext *context, const char *userid, - CamelCipherHash hash, CamelStream *istream, - CamelStream *ostream, CamelException *ex); -static CamelCipherValidity *smime_verify (CamelCipherContext *context, CamelCipherHash hash, - CamelStream *istream, CamelStream *sigstream, +static CamelMimeMessage *smime_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex); + +static CamelMimeMessage *smime_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +static CamelMimeMessage *smime_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex); + +static CamelMimeMessage *smime_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, CamelException *ex); -static int smime_encrypt (CamelCipherContext *context, gboolean sign, const char *userid, - GPtrArray *recipients, CamelStream *istream, CamelStream *ostream, - CamelException *ex); -static int smime_decrypt (CamelCipherContext *context, CamelStream *istream, - CamelStream *ostream, CamelException *ex); +static CamelMimeMessage *smime_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex); static CamelCipherContextClass *parent_class; @@ -77,22 +77,23 @@ camel_smime_context_finalise (CamelObject *o) { CamelSMimeContext *context = (CamelSMimeContext *)o; + g_free (context->encryption_key); g_free (context->priv); } static void camel_smime_context_class_init (CamelSMimeContextClass *camel_smime_context_class) { - CamelCipherContextClass *camel_cipher_context_class = - CAMEL_CIPHER_CONTEXT_CLASS (camel_smime_context_class); + CamelCMSContextClass *camel_cms_context_class = + CAMEL_CMS_CONTEXT_CLASS (camel_smime_context_class); - parent_class = CAMEL_CIPHER_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cipher_context_get_type ())); + parent_class = CAMEL_CMS_CONTEXT_CLASS (camel_type_get_global_classfuncs (camel_cms_context_get_type ())); - camel_cipher_context_class->sign = smime_sign; - camel_cipher_context_class->clearsign = smime_clearsign; - camel_cipher_context_class->verify = smime_verify; - camel_cipher_context_class->encrypt = smime_encrypt; - camel_cipher_context_class->decrypt = smime_decrypt; + camel_cms_context_class->sign = cms_sign; + camel_cms_context_class->certsonly = cms_certsonly; + camel_cms_context_class->encrypt = cms_encrypt; + camel_cms_context_class->envelope = cms_envelope; + camel_cms_context_class->decode = cms_decode; } CamelType @@ -101,7 +102,7 @@ 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 (), + type = camel_type_register (camel_cms_context_get_type (), "CamelSMimeContext", sizeof (CamelSMimeContext), sizeof (CamelSMimeContextClass), @@ -118,6 +119,7 @@ camel_smime_context_get_type (void) /** * camel_smime_context_new: * @session: CamelSession + * @encryption_key: preferred encryption key (used when attaching cert chains to messages) * * This creates a new CamelSMimeContext object which is used to sign, * verify, encrypt and decrypt streams. @@ -125,519 +127,811 @@ camel_smime_context_get_type (void) * Return value: the new CamelSMimeContext **/ CamelSMimeContext * -camel_smime_context_new (CamelSession *session) +camel_smime_context_new (CamelSession *session, const char *encryption_key) { CamelSMimeContext *context; - CERTCertDBHandle *handle; + CERTCertDBHandle *certdb; g_return_val_if_fail (session != NULL, NULL); g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL); + certdb = CERT_GetDefaultCertDB (); + if (!certdb) + return NULL; + context = CAMEL_SMIME_CONTEXT (camel_object_new (CAMEL_SMIME_CONTEXT_TYPE)); - camel_cipher_context_construct (CAMEL_CIPHER_CONTEXT (context), session); + camel_cms_context_construct (CAMEL_CMS_CONTEXT (context), session); - handle = CERT_GetDefaultCertDB (); - if (!handle) { - camel_object_unref (CAMEL_OBJECT (context)); - return NULL; - } - - context->priv->certdb = handle; + context->encryption_key = g_strdup (encryption_key); + context->priv->certdb = certdb; return context; } -/*----------------------------------------------------------------------* - * Public crypto functions - *----------------------------------------------------------------------*/ struct _GetPasswdData { CamelSession *session; - CamelException *ex; const char *userid; + CamelException *ex; }; -static SECItem * -get_zero_len_passwd (SECKEYKeyDBHandle *handle) +static char * +smime_get_password (PK11SlotInfo *info, PRBool retry, void *arg) { - SECItem *pwitem; - SECStatus rv; + CamelSession *session = ((struct _GetPasswdData *)arg)->session; + const char *userid = ((struct _GetPasswdData *)arg)->userid; + CamelException *ex = ((struct _GetPasswdData *)arg)->ex; + char *prompt, *passwd, *ret; - /* hash the empty string as a password */ - pwitem = SECKEY_DeriveKeyDBPassword (handle, ""); - if (pwitem == NULL) - return NULL; + prompt = g_strdup_printf (_("Please enter your password for %s"), userid); + passwd = camel_session_get_password (session, prompt, TRUE, + NULL, userid, ex); + g_free (prompt); - /* check to see if this is the right password */ - rv = SECKEY_CheckKeyDBPassword (handle, pwitem); - if (rv == SECFailure) - return NULL; + ret = PL_strdup (passwd); + g_free (passwd); - return pwitem; + return ret; +} + +static PK11SymKey * +decode_key_cb (void *arg, SECAlgorithmID *algid) +{ + return (PK11SymKey *)arg; } -static SECItem * -get_password (void *arg, SECKEYKeyDBHandle *handle) + +static NSSCMSMessage * +signed_data (CamelSMimeContext *ctx, const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex) { - CamelSession *session = ((struct _GetPasswdData *) arg)->session; - CamelException *ex = ((struct _GetPasswdData *) arg)->ex; - const char *userid = ((struct _GetPasswdData *) arg)->userid; - char *prompt, *passwd = NULL; - SECItem *pwitem; - SECStatus rv; - - /* Check to see if zero length password or not */ - pwitem = get_zero_len_passwd (handle); - if (pwitem) - return pwitem; + NSSCMSMessage *cmsg = NULL; + NSSCMSContentInfo *cinfo; + NSSCMSSignedData *sigd; + NSSCMSSignerInfo *signerinfo; + CERTCertificate *cert, *ekpcert; + + if (!userid) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Please indicate the nickname of a certificate to sign with.")); + return NULL; + } - prompt = g_strdup_printf (_("Please enter your password for %s"), userid); - passwd = camel_session_get_password (session, prompt, TRUE, - NULL, userid, NULL); - g_free (prompt); + if ((cert = CERT_FindCertByNickname (ctx->priv->certdb, userid)) == NULL) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("The signature certificate for \"%s\" does not exist."), + userid); + return NULL; + } + + /* create the cms message object */ + cmsg = NSS_CMSMessage_Create (NULL); + + /* build chain of objects: message->signedData->data */ + sigd = NSS_CMSSignedData_Create (cmsg); - /* hash the password */ - pwitem = SECKEY_DeriveKeyDBPassword (handle, passwd ? passwd : ""); + cinfo = NSS_CMSMessage_GetContentInfo (cmsg); + NSS_CMSContentInfo_SetContent_SignedData (cmsg, cinfo, sigd); - /* clear out the password strings */ - if (passwd) { - memset (passwd, 0, strlen (passwd)); - g_free (passwd); + cinfo = NSS_CMSSignedData_GetContentInfo (sigd); + + /* speciffy whether we want detached signatures or not */ + NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, detached); + + /* create & attach signer information */ + signerinfo = NSS_CMSSignerInfo_Create (cmsg, cert, SEC_OID_SHA1); + + /* include the cert chain */ + NSS_CMSSignerInfo_IncludeCerts (signerinfo, NSSCMSCM_CertChain, + certUsageEmailSigner); + + if (signing_time) { + NSS_CMSSignerInfo_AddSigningTime (signerinfo, PR_Now ()); } - if (pwitem == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Error hashing password.")); - - return NULL; + if (TRUE) { + /* Add S/MIME Capabilities */ + NSS_CMSSignerInfo_AddSMIMECaps (signerinfo); } - /* confirm the password */ - rv = SECKEY_CheckKeyDBPassword (handle, pwitem); - if (rv) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Invalid password.")); + if (ctx->encryption_key) { + /* get the cert, add it to the message */ + ekpcert = CERT_FindCertByNickname (ctx->priv->certdb, ctx->encryption_key); + if (!ekpcert) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("The encryption certificate for \"%s\" does not exist."), + ctx->encryption_key); + goto exception; + } - SECITEM_ZfreeItem (pwitem, PR_TRUE); + NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs (signerinfo, ekpcert, ctx->priv->certdb); - return NULL; + NSS_CMSSignedData_AddCertificate (sigd, ekpcert); + } else { + /* check signing cert for fitness as encryption cert */ + /* if yes, add signing cert as EncryptionKeyPreference */ + NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs (signerinfo, cert, ctx->priv->certdb); } - return pwitem; -} - -static HASH_HashType -camel_cipher_hash_to_nss (CamelCipherHash hash) -{ - switch (hash) { - case CAMEL_CIPHER_HASH_DEFAULT: - return HASH_AlgSHA1; - case CAMEL_CIPHER_HASH_MD2: - return HASH_AlgMD2; - case CAMEL_CIPHER_HASH_MD5: - return HASH_AlgMD5; - case CAMEL_CIPHER_HASH_SHA1: - return HASH_AlgSHA1; - } + NSS_CMSSignedData_AddSignerInfo (sigd, signerinfo); - return HASH_AlgNULL; -} - -static SECOidTag -nss_hash_to_sec_oid (HASH_HashType hash) -{ - switch (hash) { - case HASH_AlgMD2: - return SEC_OID_MD2; - case HASH_AlgMD5: - return SEC_OID_MD5; - case HASH_AlgSHA1: - return SEC_OID_SHA1; - default: - g_assert_not_reached (); - return 0; - } + return cmsg; + + exception: + + NSS_CMSMessage_Destroy (cmsg); + + return NULL; } -static int -smime_digest (SECItem *data, char *digestdata, unsigned int *len, unsigned int maxlen, HASH_HashType hash) +static void +smime_sign_restore (CamelMimePart *mime_part, GSList *encodings) { - const SECHashObject *hashObj; - void *hashcx; + CamelDataWrapper *wrapper; - hashObj = &SECHashObjects[hash]; + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + if (!wrapper) + return; - hashcx = (* hashObj->create)(); - if (hashcx == NULL) - return -1; - - (* hashObj->begin)(hashcx); - (* hashObj->update)(hashcx, data->data, data->len); - (* hashObj->end)(hashcx, (unsigned char *)digestdata, len, maxlen); - (* hashObj->destroy)(hashcx, PR_TRUE); - - return 0; + if (CAMEL_IS_MULTIPART (wrapper)) { + int parts, i; + + parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper)); + for (i = 0; i < parts; i++) { + CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i); + + smime_sign_restore (part, encodings); + encodings = encodings->next; + } + } else { + CamelMimePartEncodingType encoding; + + encoding = GPOINTER_TO_INT (encodings->data); + + camel_mime_part_set_encoding (mime_part, encoding); + } } static void -smime_output_cb (void *arg, const char *buf, unsigned long len) +smime_sign_prepare (CamelMimePart *mime_part, GSList **encodings) { - CamelStream *stream; - - stream = CAMEL_STREAM (arg); - camel_stream_write (stream, buf, len); + CamelDataWrapper *wrapper; + int parts, i; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + if (!wrapper) + return; + + if (CAMEL_IS_MULTIPART (wrapper)) { + parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper)); + for (i = 0; i < parts; i++) { + CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i); + + smime_sign_prepare (part, encodings); + } + } else { + CamelMimePartEncodingType encoding; + + encoding = camel_mime_part_get_encoding (mime_part); + + /* FIXME: find the best encoding for this part and use that instead?? */ + /* the encoding should really be QP or Base64 */ + if (encoding != CAMEL_MIME_PART_ENCODING_BASE64) + camel_mime_part_set_encoding (mime_part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE); + + *encodings = g_slist_append (*encodings, GINT_TO_POINTER (encoding)); + } } -static int -smime_sign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash, - CamelStream *istream, CamelStream *ostream, CamelException *ex) + +static CamelMimeMessage * +smime_sign (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, gboolean signing_time, + gboolean detached, CamelException *ex) { - CamelSMimeContext *context = CAMEL_SMIME_CONTEXT (ctx); - SEC_PKCS7EncoderContext *ecx = NULL; - struct _GetPasswdData *data = NULL; - SEC_PKCS7ContentInfo *cinfo = NULL; - SECItem data2sign, digest; - HASH_HashType hash_type; - CERTCertificate *cert; - guchar digestdata[32]; + CamelMimeMessage *mesg = NULL; + NSSCMSMessage *cmsg = NULL; + struct _GetPasswdData *data; + PLArenaPool *arena; + NSSCMSEncoderContext *ecx; + SECItem output = { 0, 0, 0 }; CamelStream *stream; + GSList *encodings = NULL; GByteArray *buf; - guint len; - g_return_val_if_fail (userid != NULL, -1); - g_return_val_if_fail (istream != NULL, -1); - g_return_val_if_fail (ostream != NULL, -1); + cmsg = signed_data (CAMEL_SMIME_CONTEXT (ctx), userid, signing_time, detached, ex); + if (!cmsg) + return NULL; + + arena = PORT_NewArena (1024); + data = g_new (struct _GetPasswdData, 1); + data->session = ctx->session; + data->userid = userid; + data->ex = ex; + ecx = NSS_CMSEncoder_Start (cmsg, NULL, NULL, &output, arena, + smime_get_password, data, NULL, NULL, + NULL, NULL); stream = camel_stream_mem_new (); - camel_stream_write_to_stream (istream, stream); + + smime_sign_prepare (CAMEL_MIME_PART (message), &encodings); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream); + smime_sign_restore (CAMEL_MIME_PART (message), encodings); + g_slist_free (encodings); + buf = CAMEL_STREAM_MEM (stream)->buffer; - data2sign.data = buf->data; - data2sign.len = buf->len; - hash_type = camel_cipher_hash_to_nss (hash); - smime_digest (&data2sign, digestdata, &len, 32, hash_type); - digest.data = (unsigned char *)digestdata; - digest.len = len; + NSS_CMSEncoder_Update (ecx, buf->data, buf->len); + NSS_CMSEncoder_Finish (ecx); camel_object_unref (CAMEL_OBJECT (stream)); + g_free (data); - cert = CERT_FindCertByNickname (context->priv->certdb, (char *) userid); - if (!cert) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not sign: certificate not found for \"%s\"."), - userid); - return -1; - } + /* write the result to a camel stream */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, output.data, output.len); + PORT_FreeArena (arena, PR_FALSE); - data = g_new (struct _GetPasswdData, 1); - data->session = ctx->session; - data->userid = userid; - data->ex = ex; + NSS_CMSMessage_Destroy (cmsg); - cinfo = SECMIME_CreateSigned (cert, cert, context->priv->certdb, - nss_hash_to_sec_oid (hash_type), - &digest, get_password, data); + /* parse the stream into a new CamelMimeMessage */ + mesg = camel_mime_message_new (); + camel_stream_reset (stream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mesg), stream); + camel_object_unref (CAMEL_OBJECT (stream)); - if (cinfo == NULL) { + return mesg; +} + + +static NSSCMSMessage * +certsonly_data (CamelSMimeContext *ctx, const char *userid, GByteArray *recipients, CamelException *ex) +{ + NSSCMSMessage *cmsg = NULL; + NSSCMSContentInfo *cinfo; + NSSCMSSignedData *sigd; + CERTCertificate **rcerts; + int i; + + /* find the signer's and the recipients' certs */ + rcerts = g_new (CERTCertificate *, recipients->len + 2); + rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, userid); + if (!rcerts[0]) { camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not sign: failed to create content info.")); + _("Failed to find certificate for \"%s\"."), + recipients->pdata[i]); goto exception; } - ecx = SEC_PKCS7EncoderStart (cinfo, smime_output_cb, ostream, NULL); - if (ecx == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not sign: failed to create signing context.")); - goto exception; + for (i = 0; i < recipients->len; i++) { + rcerts[i + 1] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, + recipients->pdata[i]); + + if (!rcerts[i + 1]) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to find certificate for \"%s\"."), + recipients->pdata[i]); + goto exception; + } } + rcerts[i + 1] = NULL; - if (SEC_PKCS7EncoderFinish (ecx, NULL, NULL) != SECSuccess) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not sign: failed to create signature.")); - goto exception; + /* create the cms message object */ + cmsg = NSS_CMSMessage_Create (NULL); + + sigd = NSS_CMSSignedData_CreateCertsOnly (cmsg, rcerts[0], PR_TRUE); + + /* add the recipient cert chain */ + for (i = 0; i < recipients->len; i++) { + NSS_CMSSignedData_AddCertChain (sigd, certs[i]); } - g_free (data); + cinfo = NSS_CMSMessage_GetContentInfo (cmsg); + NSS_CMSContentInfo_SetContent_SignedData (cmsg, cinfo, sigd); - SEC_PKCS7DestroyContentInfo (cinfo); + cinfo = NSS_CMSSignedData_GetContentInfo (sigd); + NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, PR_FALSE); + + g_free (rcerts); + + return cmsg; - return 0; - exception: - if (cinfo) - SEC_PKCS7DestroyContentInfo (cinfo); + NSS_CMSMessage_Destroy (cmsg); - if (data) - g_free (data); + g_free (rcerts); - return -1; -} - - -static int -smime_clearsign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash, - CamelStream *istream, CamelStream *ostream, CamelException *ex) -{ - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("'clearsign' is not supported by S/MIME.")); - return -1; + return NULL; } -#if 0 -/* this is just meant as a reference so I can see what the valid enums are */ -typedef enum { - certUsageSSLClient, - certUsageSSLServer, - certUsageSSLServerWithStepUp, - certUsageSSLCA, - certUsageEmailSigner, - certUsageEmailRecipient, - certUsageObjectSigner, - certUsageUserCertImport, - certUsageVerifyCA, - certUsageProtectedObjectSigner, - certUsageStatusResponder, - certUsageAnyCA -} SECCertUsage; -#endif - -/* FIXME: god knows if this code works, NSS "docs" are so not helpful at all */ -static CamelCipherValidity * -smime_verify (CamelCipherContext *ctx, CamelCipherHash hash, CamelStream *istream, - CamelStream *sigstream, CamelException *ex) +static CamelMimeMessage * +smime_certsonly (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) { - CamelSMimeContext *context = CAMEL_SMIME_CONTEXT (ctx); - CamelCipherValidity *valid = NULL; - SEC_PKCS7ContentInfo *cinfo; - SECCertUsage usage; - GByteArray *plaintext; + CamelMimeMessage *mesg = NULL; + NSSCMSMessage *cmsg = NULL; + PLArenaPool *arena; + NSSCMSEncoderContext *ecx; + SECItem output = { 0, 0, 0 }; CamelStream *stream; - gboolean isvalid; + GByteArray *buf; - /* create our ContentInfo object */ - stream = camel_stream_mem_new (); - camel_stream_write_to_stream (istream, stream); - plaintext = CAMEL_STREAM_MEM (stream)->buffer; - cinfo = SEC_PKCS7CreateData (); - SEC_PKCS7SetContent (cinfo, plaintext->data, plaintext->len); - camel_object_unref (CAMEL_OBJECT (stream)); + cmsg = certsonly_data (CAMEL_SMIME_CONTEXT (ctx), userid, recipients, ex); + if (!cmsg) + return NULL; - usage = certUsageEmailSigner; /* just a guess. or maybe certUsageVerifyCA?? */ + arena = PORT_NewArena (1024); + data = g_new (struct _GetPasswdData, 1); + data->session = ctx->session; + data->userid = userid; + data->ex = ex; + ecx = NSS_CMSEncoder_Start (cmsg, NULL, NULL, &output, arena, + smime_get_password, data, NULL, NULL, + NULL, NULL); - valid = camel_cipher_validity_new (); + stream = camel_stream_mem_new (); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream); + buf = CAMEL_STREAM_MEM (stream)->buffer; - if (sigstream) { - HASH_HashType digest_type; - GByteArray *signature; - SECItem digest; - - /* create our digest object */ - stream = camel_stream_mem_new (); - camel_stream_write_to_stream (sigstream, stream); - signature = CAMEL_STREAM_MEM (stream)->buffer; - digest.data = signature->data; - digest.len = signature->len; - - switch (hash) { - default: - case CAMEL_CIPHER_HASH_DEFAULT: - digest_type = HASH_AlgNULL; - break; - case CAMEL_CIPHER_HASH_MD2: - digest_type = HASH_AlgMD2; - break; - case CAMEL_CIPHER_HASH_MD5: - digest_type = HASH_AlgMD5; - break; - case CAMEL_CIPHER_HASH_SHA1: - digest_type = HASH_AlgSHA1; - break; - } - - isvalid = SEC_PKCS7VerifyDetachedSignature (cinfo, usage, &digest, - digest_type, PR_FALSE); - camel_object_unref (CAMEL_OBJECT (stream)); - } else { - isvalid = SEC_PKCS7VerifySignature (cinfo, usage, PR_FALSE); - } + NSS_CMSEncoder_Update (ecx, buf->data, buf->len); + NSS_CMSEncoder_Finish (ecx); + + camel_object_unref (CAMEL_OBJECT (stream)); + g_free (data); - camel_cipher_validity_set_valid (valid, isvalid); + /* write the result to a camel stream */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, output.data, output.len); + PORT_FreeArena (arena, PR_FALSE); - SEC_PKCS7DestroyContentInfo (cinfo); + NSS_CMSMessage_Destroy (cmsg); - /* FIXME: set a meaningful description...in UTF8 */ - camel_cipher_validity_set_description (valid, ""); + /* parse the stream into a new CamelMimeMessage */ + mesg = camel_mime_message_new (); + camel_stream_reset (stream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mesg), stream); + camel_object_unref (CAMEL_OBJECT (stream)); - return valid; + return mesg; } -static int -smime_encrypt (CamelCipherContext *ctx, gboolean sign, const char *userid, GPtrArray *recipients, - CamelStream *istream, CamelStream *ostream, CamelException *ex) + +static NSSCMSMessage * +enveloped_data (CamelSMimeContext *ctx, const char *userid, GPtrArray *recipients, CamelException *ex) { - CamelSMimeContext *context = CAMEL_SMIME_CONTEXT (ctx); - const char *invalid_userkey = NULL; - SEC_PKCS7ContentInfo *cinfo = NULL; - GPtrArray *certificates = NULL; - SEC_PKCS7EncoderContext *ecx; - struct _GetPasswdData *data; - CamelStream *stream = NULL; - CERTCertificate *scert; - GByteArray *buf; - int i = 0; - - g_return_val_if_fail (userid != NULL, -1); - g_return_val_if_fail (recipients != NULL, -1); - g_return_val_if_fail (recipients->len != 0, -1); - g_return_val_if_fail (istream != NULL, -1); - g_return_val_if_fail (ostream != NULL, -1); - - scert = CERT_FindCertByNickname (context->priv->certdb, (char *) userid); - if (!scert) { - invalid_userkey = recipients->pdata[i]; + NSSCMSMessage *cmsg = NULL; + NSSCMSContentInfo *cinfo; + NSSCMSEnvelopedData *envd; + NSSCMSRecipientInfo *rinfo; + CERTCertificate **rcerts; + SECOidTag bulkalgtag; + int keysize, i; + + /* find the recipient certs by email address or nickname */ + rcerts = g_new (CERTCertificate *, recipients->len + 2); + rcerts[0] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, userid); + if (!rcerts[0]) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to find certificate for \"%s\"."), + userid); goto exception; } - certificates = g_ptr_array_new (); for (i = 0; i < recipients->len; i++) { - CERTCertificate *cert; - - cert = CERT_FindCertByNickname (context->priv->certdb, recipients->pdata[i]); - if (!cert) { - invalid_userkey = recipients->pdata[i]; + rcerts[i + 1] = CERT_FindCertByNicknameOrEmailAddr (ctx->priv->certdb, + recipients->pdata[i]); + if (!rcerts[i + 1]) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to find certificate for \"%s\"."), + recipients->pdata[i]); goto exception; } - - g_ptr_array_add (certificates, cert); } - g_ptr_array_add (certificates, NULL); + rcerts[i + 1] = NULL; + + /* find a nice bulk algorithm */ + if (!NSS_SMIMEUtil_FindBulkAlgForRecipients (rcerts, &bulkalgtag, &keysize)) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to find a common bulk algorithm.")); + goto exception; + } + + /* create a cms message object */ + cmsg = NSS_CMSMessage_Create (NULL); + + envd = NSS_CMSEnvelopedData_Create (cmsg, bulkalgtag, keysize); + cinfo = NSS_CMSMessage_GetContentInfo (cmsg); + NSS_CMSContentInfo_SetContent_EnvelopedData (cmsg, cinfo, envd); + + cinfo = NSS_CMSEnvelopedData_GetContentInfo (envd); + NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, PR_FALSE); + + /* create & attach recipient information */ + for (i = 0; rcerts[i] != NULL; i++) { + rinfo = NSS_CMSRecipientInfo_Create (cmsg, rcerts[i]); + NSS_CMSEnvelopedData_AddRecipient (envd, rinfo); + } + + g_free (rcerts); + return cmsg; + + exception: + + NSS_CMSMessage_Destroy (cmsg); + + g_free (rcerts); + + return NULL; +} + +static CamelMimeMessage * +smime_envelope (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) +{ + CamelMimeMessage *mesg = NULL; + NSSCMSMessage *cmsg = NULL; + PLArenaPool *arena; + NSSCMSEncoderContext *ecx; + SECItem output = { 0, 0, 0 }; + CamelStream *stream; + GByteArray *buf; + + cmsg = enveloped_data (CAMEL_SMIME_CONTEXT (ctx), userid, recipients, ex); + if (!cmsg) + return NULL; + + arena = PORT_NewArena (1024); data = g_new (struct _GetPasswdData, 1); data->session = ctx->session; data->userid = userid; data->ex = ex; + ecx = NSS_CMSEncoder_Start (cmsg, NULL, NULL, &output, arena, + smime_get_password, data, NULL, NULL, + NULL, NULL); - cinfo = SECMIME_CreateEncrypted (scert, (CERTCertificate **) certificates->pdata, - context->priv->certdb, get_password, data); - - g_free (data); + stream = camel_stream_mem_new (); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream); + buf = CAMEL_STREAM_MEM (stream)->buffer; - if (!cinfo) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not encrypt: failed to create enveloped data.")); - goto exception; - } + NSS_CMSEncoder_Update (ecx, buf->data, buf->len); + NSS_CMSEncoder_Finish (ecx); - ecx = SEC_PKCS7EncoderStart (cinfo, smime_output_cb, ostream, NULL); - if (ecx == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not encrypt: failed to create encryption context.")); - goto exception; - } + camel_object_unref (CAMEL_OBJECT (stream)); + g_free (data); + /* write the result to a camel stream */ stream = camel_stream_mem_new (); - camel_stream_write_to_stream (istream, stream); - buf = CAMEL_STREAM_MEM (stream)->buffer; - if (SEC_PKCS7EncoderUpdate (ecx, buf->data, buf->len) != SECSuccess) - goto exception; + camel_stream_write (stream, output.data, output.len); + PORT_FreeArena (arena, PR_FALSE); + + NSS_CMSMessage_Destroy (cmsg); + /* parse the stream into a new CamelMimeMessage */ + mesg = camel_mime_message_new (); + camel_stream_reset (stream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mesg), stream); camel_object_unref (CAMEL_OBJECT (stream)); - stream = NULL; - if (SEC_PKCS7EncoderFinish (ecx, NULL, NULL) != SECSuccess) - goto exception; + return mesg; +} + + +struct _BulkKey { + PK11KeySym *bulkkey; + SECOidTag bulkkeytag; + int keysize; +}; + +static NSSCMSMessage * +encrypted_data (CamelSMimeContext *ctx, GByteArray *input, struct _BulkKey *key, + CamelStream *ostream, CamelException *ex) +{ + NSSCMSMessage *cmsg = NULL; + NSSCMSContentInfo *cinfo; + NSSCMSEncryptedData *encd; + NSSCMSEncoderContext *ecx = NULL; + PLArenaPool *arena = NULL; + SECItem output = { 0, 0, 0 }; - g_ptr_array_free (certificates, TRUE); + /* arena for output */ + arena = PORT_NewArena (1024); - SEC_PKCS7DestroyContentInfo (cinfo); + /* create cms message object */ + cmsg = NSS_CMSMessage_Create (NULL); - return 0; + encd = NSS_CMSEncryptedData_Create (cmsg, key->bulkalgtag, key->keysize); - exception: + cinfo = NSS_CMSMessage_GetContentInfo (cmsg); + NSS_CMSContentInfo_SetContent_EncryptedData (cmsg, cinfo, encd); - if (certificates) - g_ptr_array_free (certificates, TRUE); + cinfo = NSS_CMSEncryptedData_GetContentInfo (encd); + NSS_CMSContentInfo_SetContent_Data (cmsg, cinfo, NULL, PR_FALSE); - if (stream) - camel_object_unref (CAMEL_OBJECT (stream)); + ecx = NSS_CMSEncoder_Start (cmsg, NULL, NULL, &output, arena, NULL, NULL, + decode_key_cb, key->bulkkey, NULL, NULL); - if (cinfo) - SEC_PKCS7DestroyContentInfo (cinfo); + NSS_CMSEncoder_Update (ecx, input->data, input->len); - if (invalid_userkey) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not encrypt data: invalid user key: \"%s\"."), - invalid_userkey); - } + NSS_CMSEncoder_Finish (ecx); - if (!camel_exception_is_set (ex)) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Could not encrypt: encoding failed.")); - } + camel_stream_write (ostream, output.data, output.len); - return -1; + if (arena) + PORT_FreeArena (arena, PR_FALSE); + + return cmsg; } -static PRBool -decryption_allowed (SECAlgorithmID *algid, PK11SymKey *key) +static struct _BulkKey * +get_bulkkey (CamelSMimeContext *ctx, const char *userid, GPtrArray *recipients, CamelException *ex) { - return PR_TRUE; + struct _BulkKey *bulkkey = NULL; + NSSCMSMessage *env_cmsg; + NSSCMSContentInfo *cinfo; + SECItem dummyOut = { 0, 0, 0 }; + SECItem dummyIn = { 0, 0, 0 }; + char str[] = "You are not a beautiful and unique snowflake."; + PLArenaPool *arena; + int i, nlevels; + + /* construct an enveloped data message to obtain bulk keys */ + arena = PORT_NewArena (1024); + dummyIn.data = (unsigned char *)str; + dummyIn.len = strlen (str); + + env_cmsg = enveloped_data (ctx, userid, recipients, ex); + NSS_CMSDEREncode (env_cmsg, &dummyIn, &dummyOut, arena); + /*camel_stream_write (envstream, dummyOut.data, dummyOut.len);*/ + PORT_FreeArena (arena, PR_FALSE); + + /* get the content info for the enveloped data */ + nlevels = NSS_CMSMessage_ContentLevelCount (env_cmsg); + for (i = 0; i < nlevels; i++) { + SECOidTag typetag; + + cinfo = NSS_CMSMessage_ContentLevel (env_cmsg, i); + typetag = NSS_CMSContentInfo_GetContentTypeTag (cinfo); + if (typetag == SEC_OID_PKCS7_DATA) { + bulkkey = g_new (struct _BulkKey, 1); + + /* get the symmertic key */ + bulkkey->bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag (cinfo); + bulkkey->keysize = NSS_CMSContentInfo_GetBulkKeySize (cinfo); + bulkkey->bulkkey = NSS_CMSContentInfo_GetBulkKey (cinfo); + + return bulkkey; + } + } + + return NULL; } -static int -smime_decrypt (CamelCipherContext *ctx, CamelStream *istream, - CamelStream *ostream, CamelException *ex) +static CamelMimeMessage * +smime_encrypt (CamelCMSContext *ctx, CamelMimeMessage *message, + const char *userid, GPtrArray *recipients, + CamelException *ex) { - struct _GetPasswdData *data; - SEC_PKCS7DecoderContext *dcx; - SEC_PKCS7ContentInfo *cinfo; - CamelStream *stream = NULL; - SECItem secdata; + struct _BulkKey *bulkkey = NULL; + CamelMimeMessage *mesg = NULL; + NSSCMSMessage *cmsg = NULL; + CamelStream *stream; GByteArray *buf; - g_return_val_if_fail (istream != NULL, -1); - g_return_val_if_fail (ostream != NULL, -1); + bulkkey = get_bulkkey (CAMEL_SMIME_CONTEXT (ctx), userid, recipients, ex); + if (!bulkkey) + return NULL; + buf = g_byte_array_new (); stream = camel_stream_mem_new (); - camel_stream_write_to_stream (istream, stream); - buf = CAMEL_STREAM_MEM (stream)->buffer; - secdata.data = buf->data; - secdata.len = buf->len; + camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (stream), buf); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + stream = camel_stream_mem_new (); + cmsg = encrypted_data (CAMEL_SMIME_CONTEXT (ctx), buf, bulkkey, stream, ex); + g_byte_array_free (buf, TRUE); + g_free (bulkkey); + if (!cmsg) { + camel_object_unref (CAMEL_OBJECT (stream)); + return NULL; + } + + NSS_CMSMessage_Destroy (cmsg); + + /* parse the stream into a new CamelMimeMessage */ + mesg = camel_mime_message_new (); + camel_stream_reset (stream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mesg), stream); + camel_object_unref (CAMEL_OBJECT (stream)); + + return mesg; +} + + +static NSSCMSMessage * +decode (CamelSMimeContext *ctx, GByteArray *input, CamelStream *ostream, + CamelCMSValidityInfo **info, CamelExcepton *ex) +{ + NSSCMSDecoderContext *dcx; + struct _GetPasswdData *data; + CamelCMSValidityInfo *vinfo = NULL; + NSSCMSMessage *cmsg = NULL; + NSSCMSContentInfo *cinfo; + NSSCMSSignedData *sigd = NULL; + NSSCMSEnvelopedData *envd; + NSSCMSEncryptedData *encd; + SECAlgorithmID **digestalgs; + int nlevels, i, nsigners, j; + char *signercn; + NSSCMSSignerInfo *si; + SECOidTag typetag; + SECItem **digests; + PLArenaPool *arena; + SECItem *item, sitem = { 0, 0, 0 }; data = g_new (struct _GetPasswdData, 1); data->session = ctx->session; data->userid = NULL; data->ex = ex; - dcx = SEC_PKCS7DecoderStart (smime_output_cb, ostream, get_password, data, - NULL, NULL, decryption_allowed); - if (dcx == NULL) - goto exception; + dcx = NSS_CMSDecoder_Start (NULL, + NULL, NULL, + smime_get_password, data, + decode_key_cb, + decodeOptions->bulkkey); - SEC_PKCS7DecoderUpdate (dcx, secdata.data, secdata.len); - cinfo = SEC_PKCS7DecoderFinish (dcx); + NSS_CMSDecoder_Update (dcx, input->data, input->len); - camel_object_unref (CAMEL_OBJECT (stream)); + cmsg = NSS_CMSDecoder_Finish (dcx); g_free (data); + if (cmsg == NULL) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to decode message.")); + return NULL; + } - if (cinfo == NULL) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, - _("Failed to decrypt: Unknown")); - return -1; + nlevels = NSS_CMSMessage_ContentLevelCount (cmsg); + for (i = 0; i < nlevels; i++) { + CamelCMSSigner *signers = NULL; + + cinfo = NSS_CMSMessage_ContentLevel (cmsg, i); + typetag = NSS_CMSContentInfo_GetContentTypeTag (cinfo); + + if (info && !vinfo) { + vinfo = g_new0 (CamelCMSValidityInfo, 1); + *info = vinfo; + } else if (vinfo) { + vinfo->next = g_new0 (CamelCMSValidityInfo, 1); + vinfo = vinfo->next; + } + + switch (typetag) { + case SEC_OID_PKCS7_SIGNED_DATA: + if (vinfo) + vinfo->type = CAMEL_CMS_TYPE_SIGNED; + + sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent (cinfo); + + /* import the certificates */ + NSS_CMSSignedData_ImportCerts (sigd, ctx->priv->certdb, + certUsageEmailSigner, PR_FALSE); + + /* find out about signers */ + nsigners = NSS_CMSSignedData_SignerInfoCount (sigd); + + if (nsigners == 0) { + /* must be a cert transport message */ + SECStatus retval; + + /* XXX workaround for bug #54014 */ + NSS_CMSSignedData_ImportCerts (sigd, ctx->priv->certdb, + certUsageEmailSigner, PR_TRUE); + + retval = NSS_CMSSignedData_VerifyCertsOnly (sigd, ctx->priv->certdb, + certUsageEmailSigner); + if (retval != SECSuccess) { + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to verify certificates.")); + goto exception; + } + + return cmsg; + } + + for (j = 0; vinfo && j < nsigners; j++) { + if (!signers) { + signers = g_new0 (CamelCMSSigner, 1); + vinfo->signers = signers; + } else { + signers->next = g_new0 (CamelCMSSigner, 1); + signers = signers->next; + } + + si = NSS_CMSSignedData_GetSignerInfo (sigd, j); + signercn = NSS_CMSSignerInfo_GetSignerCommonName (si); + if (signercn == NULL) + signercn = ""; + + NSS_CMSSignedData_VerifySignerInfo (sigd, j, ctx->priv->certdb, + certUsageEmailSigner); + + if (signers) { + signers->signeercn = g_strdup (signercn); + signers->status = g_strdup ( + NSS_CMSUtil_VerificationStatusToString ( + NSS_CMSSignerInfo_GetVerificationStatus (si))); + } + } + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + if (vinfo) + vinfo->type = CAMEL_CMS_TYPE_ENVELOPED; + + envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent (cinfo); + break; + case SEC_OID_PKCS7_ENCRYPTED_DATA: + if (vinfo) + vinfo->type = CAMEL_CMS_TYPE_ENCRYPTED; + + encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent (cinfo); + break; + case SEC_OID_PKCS7_DATA: + break; + default: + break; + } } - SEC_PKCS7DestroyContentInfo (cinfo); + item = NSS_CMSMessage_GetContent (cmsg); + camel_stream_write (ostream, item->data, item->len); - return 0; + return cmsg; exception: - if (stream) - camel_object_unref (CAMEL_OBJECT (stream)); + if (info) + camel_cms_validity_info_free (*info); + + if (cmsg) + NSS_CMSMessage_Destroy (cmsg); + + return NULL; +} + + +static CamelMimeMessage * +smime_decode (CamelCMSContext *ctx, CamelMimeMessage *message, + CamelCMSValidityInfo **info, CamelException *ex) +{ + CamelMimeMessage *mesg = NULL; + NSSCMSMessage *cmsg = NULL; + CamelStream *stream, *ostream; + GByteArray *buf; + + stream = camel_stream_mem_new (); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), stream); + buf = CAMEL_STREAM_MEM (stream)->buffer; + + ostream = camel_stream_mem_new (); + cmsg = decode (CAMEL_SMIME_CONTEXT (ctx), buf, ostream, info, ex); + camel_object_unref (CAMEL_OBJECT (stream)); + if (!cmsg) { + camel_object_unref (CAMEL_OBJECT (ostream)); + return NULL; + } + + /* construct a new mime message from the stream */ + mesg = camel_mime_message_new (); + camel_stream_reset (ostream); + camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (mesg), ostream); + camel_object_unref (CAMEL_OBJECT (ostream)); - return -1; + return mesg; } #endif /* HAVE_NSS */ diff --git a/camel/camel-smime-context.h b/camel/camel-smime-context.h index 0ced7b9c1b..3a2214fc3a 100644 --- a/camel/camel-smime-context.h +++ b/camel/camel-smime-context.h @@ -24,9 +24,8 @@ #define CAMEL_SMIME_CONTEXT_H #include <camel/camel-session.h> -#include <camel/camel-stream.h> #include <camel/camel-exception.h> -#include <camel/camel-cipher-context.h> +#include <camel/camel-cms-context.h> #ifdef __cplusplus extern "C" { @@ -39,32 +38,21 @@ extern "C" { #define CAMEL_IS_SMIME_CONTEXT(o) (CAMEL_CHECK_TYPE((o), CAMEL_SMIME_CONTEXT_TYPE)) typedef struct _CamelSMimeContext { - CamelCipherContext parent_object; + CamelCMSContext parent_object; struct _CamelSMimeContextPrivate *priv; } CamelSMimeContext; typedef struct _CamelSMimeContextClass { - CamelCipherContextClass parent_class; + CamelCMSContextClass parent_class; } CamelSMimeContextClass; CamelType camel_smime_context_get_type (void); -CamelSMimeContext *camel_smime_context_new (CamelSession *session); - -/* SMIME routines */ -#define camel_smime_sign(c, u, h, i, o, e) camel_cipher_sign (CAMEL_CIPHER_CONTEXT (c), u, h, i, o, e) - -#define camel_smime_clearsign(c, u, h, i, o, e) camel_cipher_clearsign (CAMEL_CIPHER_CONTEXT (c), u, h, i, o, e) - -#define camel_smime_verify(c, h, i, s, e) camel_cipher_verify (CAMEL_CIPHER_CONTEXT (c), h, i, s, e) - -#define camel_smime_encrypt(c, s, u, r, i, o, e) camel_cipher_encrypt (CAMEL_CIPHER_CONTEXT (c), s, u, r, i, o, e) - -#define camel_smime_decrypt(c, i, o, e) camel_cipher_decrypt (CAMEL_CIPHER_CONTEXT (c), i, o, e) +CamelSMimeContext *camel_smime_context_new (CamelSession *session, const char *encryption_key); #ifdef __cplusplus } diff --git a/camel/camel-smime-utils.c b/camel/camel-smime-utils.c new file mode 100644 index 0000000000..cf588898f3 --- /dev/null +++ b/camel/camel-smime-utils.c @@ -0,0 +1,126 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2001 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 + +#include "camel-smime-utils.h" +#include "camel-multipart.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define d(x) x + +/** rfc2633 stuff (aka S/MIME v3) ********************************/ + +gboolean +camel_smime_is_smime_v3_signed (CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMultipart *mp; + CamelMimePart *part; + CamelContentType *type; + const gchar *param, *micalg; + int nparts; + + /* check that we have a multipart/signed */ + type = camel_mime_part_get_content_type (mime_part); + if (!header_content_type_is (type, "multipart", "signed")) + return FALSE; + + /* check that we have a protocol param with the value: "application/pkcs7-signature" */ + param = header_content_type_param (type, "protocol"); + if (!param || g_strcasecmp (param, "application/pkcs7-signature")) + return FALSE; + + /* check that we have a micalg parameter */ + micalg = header_content_type_param (type, "micalg"); + if (!micalg) + return FALSE; + + /* check that we have exactly 2 subparts */ + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + mp = CAMEL_MULTIPART (wrapper); + nparts = camel_multipart_get_number (mp); + if (nparts != 2) + return FALSE; + + /* The first part may be of any type except for + * application/pkcs7-signature - check it. */ + part = camel_multipart_get_part (mp, 0); + type = camel_mime_part_get_content_type (part); + if (header_content_type_is (type, "application", "pkcs7-signature")) + return FALSE; + + /* The second part should be application/pkcs7-signature. */ + part = camel_multipart_get_part (mp, 1); + type = camel_mime_part_get_content_type (part); + if (!header_content_type_is (type, "application", "pkcs7-signature")) + return FALSE; + + return TRUE; +} + +gboolean +camel_smime_is_smime_v3_encrypted (CamelMimePart *mime_part) +{ + char *types[] = { "p7m", "p7c", "p7s", NULL }; + const gchar *param, *filename; + CamelContentType *type; + int i; + + /* check that we have a application/pkcs7-mime part */ + type = camel_mime_part_get_content_type (mime_part); + if (header_content_type_is (type, "application", "pkcs7-mime")) { + /* check to make sure it's an encrypted pkcs7-mime part? */ + return TRUE; + } + + if (header_content_type_is (type, "application", "octent-stream")) { + /* check to see if we have a paremeter called "smime-type" */ + param = header_content_type_param (type, "smime-type"); + if (param) + return TRUE; + + /* check to see if there is a name param and if it has a smime extension */ + param = header_content_type_param (type, "smime-type"); + if (param && *param && strlen (param) > 4) { + for (i = 0; types[i]; i++) + if (!g_strcasecmp (param + strlen (param)-4, types[i])) + return TRUE; + } + + /* check to see if there is a name param and if it has a smime extension */ + filename = camel_mime_part_get_filename (mime_part); + if (filename && *filename && strlen (filename) > 4) { + for (i = 0; types[i]; i++) + if (!g_strcasecmp (filename + strlen (filename)-4, types[i])) + return TRUE; + } + } + + return FALSE; +} diff --git a/camel/camel-smime.h b/camel/camel-smime-utils.h index 67cb027588..4d955baa77 100644 --- a/camel/camel-smime.h +++ b/camel/camel-smime-utils.h @@ -21,13 +21,11 @@ */ -#ifndef CAMEL_SMIME_H -#define CAMEL_SMIME_H +#ifndef CAMEL_SMIME_UTILS_H +#define CAMEL_SMIME_UTILS_H #include <glib.h> #include <camel/camel-mime-part.h> -#include <camel/camel-smime-context.h> -#include <camel/camel-exception.h> #ifdef __cplusplus extern "C" { @@ -35,29 +33,11 @@ extern "C" { #endif /* __cplusplus */ gboolean camel_smime_is_smime_v3_signed (CamelMimePart *part); -gboolean camel_smime_is_smime_v3_encrypted (CamelMimePart *part); - -void camel_smime_part_sign (CamelSMimeContext *context, - CamelMimePart **mime_part, - const char *userid, - CamelCipherHash hash, - CamelException *ex); - -CamelCipherValidity *camel_smime_part_verify (CamelSMimeContext *context, - CamelMimePart *mime_part, - CamelException *ex); -void camel_smime_part_encrypt (CamelSMimeContext *context, - CamelMimePart **mime_part, - GPtrArray *recipients, - CamelException *ex); - -CamelMimePart *camel_smime_part_decrypt (CamelSMimeContext *context, - CamelMimePart *mime_part, - CamelException *ex); +gboolean camel_smime_is_smime_v3_encrypted (CamelMimePart *part); #ifdef __cplusplus } #endif /* __cplusplus */ -#endif /* ! CAMEL_SMIME_H */ +#endif /* ! CAMEL_SMIME_UTILS_H */ diff --git a/camel/camel-smime.c b/camel/camel-smime.c deleted file mode 100644 index 587fcb6057..0000000000 --- a/camel/camel-smime.c +++ /dev/null @@ -1,504 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors: Jeffrey Stedfast <fejj@ximian.com> - * - * Copyright 2001 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 - -#include "camel-smime.h" -#include "camel-mime-filter-from.h" -#include "camel-mime-filter-crlf.h" -#include "camel-stream-filter.h" -#include "camel-stream-mem.h" -#include "camel-multipart.h" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define d(x) x - -/** rfc2633 stuff (aka S/MIME v3) ********************************/ - -gboolean -camel_smime_is_smime_v3_signed (CamelMimePart *mime_part) -{ - CamelDataWrapper *wrapper; - CamelMultipart *mp; - CamelMimePart *part; - CamelContentType *type; - const gchar *param, *micalg; - int nparts; - - /* check that we have a multipart/signed */ - type = camel_mime_part_get_content_type (mime_part); - if (!header_content_type_is (type, "multipart", "signed")) - return FALSE; - - /* check that we have a protocol param with the value: "application/pkcs7-signature" */ - param = header_content_type_param (type, "protocol"); - if (!param || g_strcasecmp (param, "application/pkcs7-signature")) - return FALSE; - - /* check that we have a micalg parameter */ - micalg = header_content_type_param (type, "micalg"); - if (!micalg) - return FALSE; - - /* check that we have exactly 2 subparts */ - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - mp = CAMEL_MULTIPART (wrapper); - nparts = camel_multipart_get_number (mp); - if (nparts != 2) - return FALSE; - - /* The first part may be of any type except for - * application/pkcs7-signature - check it. */ - part = camel_multipart_get_part (mp, 0); - type = camel_mime_part_get_content_type (part); - if (header_content_type_is (type, "application", "pkcs7-signature")) - return FALSE; - - /* The second part should be application/pkcs7-signature. */ - part = camel_multipart_get_part (mp, 1); - type = camel_mime_part_get_content_type (part); - if (!header_content_type_is (type, "application", "pkcs7-signature")) - return FALSE; - - return TRUE; -} - -gboolean -camel_smime_is_smime_v3_encrypted (CamelMimePart *mime_part) -{ - char *types[] = { "p7m", "p7c", "p7s", NULL }; - const gchar *param, *filename; - CamelContentType *type; - int i; - - /* check that we have a application/pkcs7-mime part */ - type = camel_mime_part_get_content_type (mime_part); - if (header_content_type_is (type, "application", "pkcs7-mime")) { - /* check to make sure it's an encrypted pkcs7-mime part? */ - return TRUE; - } - - if (header_content_type_is (type, "application", "octent-stream")) { - /* check to see if we have a paremeter called "smime-type" */ - param = header_content_type_param (type, "smime-type"); - if (param) - return TRUE; - - /* check to see if there is a name param and if it has a smime extension */ - param = header_content_type_param (type, "smime-type"); - if (param && *param && strlen (param) > 4) { - for (i = 0; types[i]; i++) - if (!g_strcasecmp (param + strlen (param)-4, types[i])) - return TRUE; - } - - /* check to see if there is a name param and if it has a smime extension */ - filename = camel_mime_part_get_filename (mime_part); - if (filename && *filename && strlen (filename) > 4) { - for (i = 0; types[i]; i++) - if (!g_strcasecmp (filename + strlen (filename)-4, types[i])) - return TRUE; - } - } - - return FALSE; -} - - -static void -smime_part_sign_restore_part (CamelMimePart *mime_part, GSList *encodings) -{ - CamelDataWrapper *wrapper; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - if (!wrapper) - return; - - if (CAMEL_IS_MULTIPART (wrapper)) { - int parts, i; - - parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper)); - for (i = 0; i < parts; i++) { - CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i); - - smime_part_sign_restore_part (part, encodings); - encodings = encodings->next; - } - } else { - CamelMimePartEncodingType encoding; - - encoding = GPOINTER_TO_INT (encodings->data); - - camel_mime_part_set_encoding (mime_part, encoding); - } -} - -static void -smime_part_sign_prepare_part (CamelMimePart *mime_part, GSList **encodings) -{ - CamelDataWrapper *wrapper; - int parts, i; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - if (!wrapper) - return; - - if (CAMEL_IS_MULTIPART (wrapper)) { - parts = camel_multipart_get_number (CAMEL_MULTIPART (wrapper)); - for (i = 0; i < parts; i++) { - CamelMimePart *part = camel_multipart_get_part (CAMEL_MULTIPART (wrapper), i); - - smime_part_sign_prepare_part (part, encodings); - } - } else { - CamelMimePartEncodingType encoding; - - encoding = camel_mime_part_get_encoding (mime_part); - - /* FIXME: find the best encoding for this part and use that instead?? */ - /* the encoding should really be QP or Base64 */ - if (encoding != CAMEL_MIME_PART_ENCODING_BASE64) - camel_mime_part_set_encoding (mime_part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE); - - *encodings = g_slist_append (*encodings, GINT_TO_POINTER (encoding)); - } -} - - -/** - * camel_smime_part_sign: - * @context: S/MIME Context - * @mime_part: a MIME part that will be replaced by an S/MIME signed part - * @userid: userid to sign with - * @hash: one of CAMEL_CIPHER_HASH_TYPE_MD5 or CAMEL_CIPHER_HASH_TYPE_SHA1 - * @ex: exception which will be set if there are any errors. - * - * Constructs a S/MIME multipart in compliance with rfc2015/rfc2633 and - * replaces @part with the generated multipart/signed. On failure, - * @ex will be set and #part will remain untouched. - **/ -void -camel_smime_part_sign (CamelSMimeContext *context, CamelMimePart **mime_part, const char *userid, - CamelCipherHash hash, CamelException *ex) -{ - CamelMimePart *part, *signed_part; - CamelMultipart *multipart; - CamelContentType *mime_type; - CamelStreamFilter *filtered_stream; - CamelMimeFilter *crlf_filter, *from_filter; - CamelStream *stream, *sigstream; - gchar *hash_type = NULL; - GSList *encodings = NULL; - - g_return_if_fail (*mime_part != NULL); - g_return_if_fail (CAMEL_IS_MIME_PART (*mime_part)); - g_return_if_fail (userid != NULL); - - part = *mime_part; - - /* Prepare all the parts for signing... */ - smime_part_sign_prepare_part (part, &encodings); - - /* get the cleartext */ - stream = camel_stream_mem_new (); - crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, - CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - from_filter = CAMEL_MIME_FILTER (camel_mime_filter_from_new ()); - filtered_stream = camel_stream_filter_new_with_stream (stream); - camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); - camel_object_unref (CAMEL_OBJECT (crlf_filter)); - camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (from_filter)); - camel_object_unref (CAMEL_OBJECT (from_filter)); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); - camel_object_unref (CAMEL_OBJECT (filtered_stream)); - - /* reset the stream */ - camel_stream_reset (stream); - - /* construct the signature stream */ - sigstream = camel_stream_mem_new (); - - switch (hash) { - case CAMEL_CIPHER_HASH_MD5: - hash_type = "md5"; - break; - case CAMEL_CIPHER_HASH_SHA1: - hash_type = "sha1"; - break; - default: - /* set a reasonable default */ - hash = CAMEL_CIPHER_HASH_SHA1; - hash_type = "sha1"; - break; - } - - /* get the signature */ - if (camel_smime_sign (context, userid, hash, stream, sigstream, ex) == -1) { - camel_object_unref (CAMEL_OBJECT (stream)); - camel_object_unref (CAMEL_OBJECT (sigstream)); - - /* restore the original encoding */ - smime_part_sign_restore_part (part, encodings); - g_slist_free (encodings); - return; - } - - camel_object_unref (CAMEL_OBJECT (stream)); - camel_stream_reset (sigstream); - - /* we don't need these anymore... */ - g_slist_free (encodings); - - /* construct the pkcs7-signature mime part */ - signed_part = camel_mime_part_new (); - camel_mime_part_set_content (signed_part, CAMEL_STREAM_MEM (sigstream)->buffer->data, - CAMEL_STREAM_MEM (sigstream)->buffer->len, - "application/pkcs7-signature"); - camel_object_unref (CAMEL_OBJECT (sigstream)); - camel_mime_part_set_encoding (signed_part, CAMEL_MIME_PART_ENCODING_BASE64); - camel_mime_part_set_filename (signed_part, "smime.p7s"); - - /* construct the container multipart/signed */ - multipart = camel_multipart_new (); - - mime_type = header_content_type_new ("multipart", "signed"); - header_content_type_set_param (mime_type, "micalg", hash_type); - header_content_type_set_param (mime_type, "protocol", "application/pkcs7-signature"); - camel_data_wrapper_set_mime_type_field (CAMEL_DATA_WRAPPER (multipart), mime_type); - header_content_type_unref (mime_type); - - camel_multipart_set_boundary (multipart, NULL); - - /* add the parts to the multipart */ - camel_multipart_add_part (multipart, part); - camel_object_unref (CAMEL_OBJECT (part)); - camel_multipart_add_part (multipart, signed_part); - camel_object_unref (CAMEL_OBJECT (signed_part)); - - /* replace the input part with the output part */ - *mime_part = camel_mime_part_new (); - camel_medium_set_content_object (CAMEL_MEDIUM (*mime_part), - CAMEL_DATA_WRAPPER (multipart)); - camel_object_unref (CAMEL_OBJECT (multipart)); -} - -struct { - char *name; - CamelCipherHash hash; -} known_hash_types[] = { - { "md5", CAMEL_CIPHER_HASH_MD5 }, - { "rsa-md5", CAMEL_CIPHER_HASH_MD5 }, - { "sha1", CAMEL_CIPHER_HASH_SHA1 }, - { "rsa-sha1", CAMEL_CIPHER_HASH_SHA1 }, - { NULL, CAMEL_CIPHER_HASH_DEFAULT } -}; - -static CamelCipherHash -get_hash_type (const char *string) -{ - int i; - - for (i = 0; known_hash_types[i].name; i++) - if (!g_strcasecmp (known_hash_types[i].name, string)) - return known_hash_types[i].hash; - - return CAMEL_CIPHER_HASH_DEFAULT; -} - -/** - * camel_smime_part_verify: - * @context: S/MIME Context - * @mime_part: a multipart/signed MIME Part - * @ex: exception - * - * Returns a CamelCipherValidity on success or NULL on fail. - **/ -CamelCipherValidity * -camel_smime_part_verify (CamelSMimeContext *context, CamelMimePart *mime_part, CamelException *ex) -{ - CamelDataWrapper *wrapper; - CamelMultipart *multipart; - CamelMimePart *part, *sigpart; - CamelStreamFilter *filtered_stream; - CamelMimeFilter *crlf_filter, *from_filter; - CamelStream *stream, *sigstream; - CamelContentType *type; - CamelCipherValidity *valid; - CamelCipherHash hash; - const char *hash_str; - - g_return_val_if_fail (mime_part != NULL, NULL); - g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL); - - if (!camel_smime_is_smime_v3_signed (mime_part)) - return NULL; - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); - multipart = CAMEL_MULTIPART (wrapper); - - /* get the plain part */ - part = camel_multipart_get_part (multipart, 0); - stream = camel_stream_mem_new (); - crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, - CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - from_filter = CAMEL_MIME_FILTER (camel_mime_filter_from_new ()); - filtered_stream = camel_stream_filter_new_with_stream (stream); - camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); - camel_object_unref (CAMEL_OBJECT (crlf_filter)); - camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (from_filter)); - camel_object_unref (CAMEL_OBJECT (from_filter)); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); - camel_object_unref (CAMEL_OBJECT (filtered_stream)); - camel_stream_reset (stream); - - /* get the signed part */ - sigpart = camel_multipart_get_part (multipart, 1); - sigstream = camel_stream_mem_new (); - camel_data_wrapper_write_to_stream (camel_medium_get_content_object (CAMEL_MEDIUM (sigpart)), - sigstream); - camel_stream_reset (sigstream); - - /* verify */ - type = camel_mime_part_get_content_type (sigpart); - hash_str = header_content_type_param (type, "micalg"); - hash = get_hash_type (hash_str); - valid = camel_smime_verify (context, hash, stream, sigstream, ex); - - camel_object_unref (CAMEL_OBJECT (sigstream)); - camel_object_unref (CAMEL_OBJECT (stream)); - - return valid; -} - - -/** - * camel_smime_part_encrypt: - * @context: S/MIME Context - * @mime_part: a MIME part that will be replaced by a pgp encrypted part - * @recipients: list of recipient PGP Key IDs - * @ex: exception which will be set if there are any errors. - * - * Constructs a PGP/MIME multipart in compliance with rfc2015 and - * replaces #mime_part with the generated multipart/signed. On failure, - * #ex will be set and #part will remain untouched. - **/ -void -camel_smime_part_encrypt (CamelSMimeContext *context, CamelMimePart **mime_part, - GPtrArray *recipients, CamelException *ex) -{ - CamelMimePart *part, *encrypted_part; - CamelStreamFilter *filtered_stream; - CamelMimeFilter *crlf_filter; - CamelStream *stream, *ciphertext; - - g_return_if_fail (*mime_part != NULL); - g_return_if_fail (CAMEL_IS_MIME_PART (*mime_part)); - g_return_if_fail (recipients != NULL); - - part = *mime_part; - - /* get the contents */ - stream = camel_stream_mem_new (); - crlf_filter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, - CAMEL_MIME_FILTER_CRLF_MODE_CRLF_ONLY); - filtered_stream = camel_stream_filter_new_with_stream (stream); - camel_stream_filter_add (filtered_stream, CAMEL_MIME_FILTER (crlf_filter)); - camel_object_unref (CAMEL_OBJECT (crlf_filter)); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (part), CAMEL_STREAM (filtered_stream)); - camel_object_unref (CAMEL_OBJECT (filtered_stream)); - camel_stream_reset (stream); - - /* smime encrypt */ - ciphertext = camel_stream_mem_new (); - if (camel_smime_encrypt (context, FALSE, NULL, recipients, stream, ciphertext, ex) == -1) { - camel_object_unref (CAMEL_OBJECT (stream)); - camel_object_unref (CAMEL_OBJECT (ciphertext)); - return; - } - - camel_object_unref (CAMEL_OBJECT (stream)); - camel_stream_reset (ciphertext); - - /* construct the encrypted mime part */ - encrypted_part = camel_mime_part_new (); - camel_mime_part_set_content (encrypted_part, CAMEL_STREAM_MEM (ciphertext)->buffer->data, - CAMEL_STREAM_MEM (ciphertext)->buffer->len, - "application/pkcs7-mime; smime-type=enveloped-data"); - camel_mime_part_set_encoding (encrypted_part, CAMEL_MIME_PART_ENCODING_BASE64); - camel_object_unref (CAMEL_OBJECT (ciphertext)); - - /* replace the input part with the output part */ - camel_object_unref (CAMEL_OBJECT (*mime_part)); - *mime_part = encrypted_part; -} - - -/** - * camel_smime_part_decrypt: - * @context: S/MIME Context - * @mime_part: a S/MIME encrypted MIME Part - * @ex: exception - * - * Returns the decrypted MIME Part on success or NULL on fail. - **/ -CamelMimePart * -camel_smime_part_decrypt (CamelSMimeContext *context, CamelMimePart *mime_part, CamelException *ex) -{ - CamelMimePart *part; - CamelStream *stream, *ciphertext; - - g_return_val_if_fail (mime_part != NULL, NULL); - g_return_val_if_fail (CAMEL_IS_MIME_PART (mime_part), NULL); - - /* make sure the mime part is a S/MIME encrypted */ - if (!camel_smime_is_smime_v3_encrypted (mime_part)) - return NULL; - - /* get the ciphertext */ - ciphertext = camel_stream_mem_new (); - camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (mime_part), ciphertext); - camel_stream_reset (ciphertext); - - /* get the cleartext */ - stream = camel_stream_mem_new (); - if (camel_smime_decrypt (context, ciphertext, stream, ex) == -1) { - camel_object_unref (CAMEL_OBJECT (ciphertext)); - camel_object_unref (CAMEL_OBJECT (stream)); - return NULL; - } - - camel_object_unref (CAMEL_OBJECT (ciphertext)); - camel_stream_reset (stream); - - /* construct the new decrypted mime part from the stream */ - part = camel_mime_part_new (); - camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (part), stream); - camel_object_unref (CAMEL_OBJECT (stream)); - - return part; -} |