1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2001-2003 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
30 #include <glib/gi18n-lib.h>
32 #include "camel-cipher-context.h"
33 #include "camel-stream.h"
34 #include "camel-operation.h"
36 #include "camel-mime-utils.h"
37 #include "camel-medium.h"
38 #include "camel-multipart.h"
39 #include "camel-mime-message.h"
40 #include "camel-mime-filter-canon.h"
41 #include "camel-stream-filter.h"
43 #define CIPHER_LOCK(ctx) g_mutex_lock (((CamelCipherContext *) ctx)->priv->lock)
44 #define CIPHER_UNLOCK(ctx) g_mutex_unlock (((CamelCipherContext *) ctx)->priv->lock);
48 #define CCC_CLASS(o) CAMEL_CIPHER_CONTEXT_CLASS(CAMEL_OBJECT_GET_CLASS(o))
50 struct _CamelCipherContextPrivate {
54 static CamelObjectClass *parent_class = NULL;
57 * camel_cipher_context_new:
58 * @session: CamelSession
60 * This creates a new CamelCipherContext object which is used to sign,
61 * verify, encrypt and decrypt streams.
63 * Return value: the new CamelCipherContext
66 camel_cipher_context_new (CamelSession *session)
68 CamelCipherContext *context;
70 g_return_val_if_fail (session != NULL, NULL);
72 context = CAMEL_CIPHER_CONTEXT (camel_object_new (CAMEL_CIPHER_CONTEXT_TYPE));
74 camel_object_ref (session);
75 context->session = session;
81 * camel_cipher_context_construct:
82 * @context: CamelCipherContext
83 * @session: CamelSession
85 * Constucts the CamelCipherContext
88 camel_cipher_context_construct (CamelCipherContext *context, CamelSession *session)
90 g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
91 g_return_if_fail (CAMEL_IS_SESSION (session));
93 camel_object_ref (session);
94 context->session = session;
98 cipher_sign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash,
99 struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
101 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
102 _("Signing is not supported by this cipher"));
108 * @context: Cipher Context
109 * @userid: private key to use to sign the stream
110 * @hash: preferred Message-Integrity-Check hash algorithm
111 * @ipart: Input part.
112 * @opart: output part.
115 * Converts the (unsigned) part @ipart into a new self-contained mime part @opart.
116 * This may be a multipart/signed part, or a simple part for enveloped types.
118 * Return value: 0 for success or -1 for failure.
121 camel_cipher_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
122 struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
126 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
128 camel_operation_start(NULL, _("Signing message"));
130 CIPHER_LOCK(context);
132 retval = CCC_CLASS (context)->sign (context, userid, hash, ipart, opart, ex);
134 CIPHER_UNLOCK(context);
136 camel_operation_end(NULL);
141 static CamelCipherValidity *
142 cipher_verify (CamelCipherContext *context, struct _CamelMimePart *sigpart, CamelException *ex)
144 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
145 _("Verifying is not supported by this cipher"));
150 * camel_cipher_verify:
151 * @context: Cipher Context
152 * @ipart: part to verify
155 * Verifies the signature. If @istream is a clearsigned stream,
156 * you should pass %NULL as the sigstream parameter. Otherwise
157 * @sigstream is assumed to be the signature stream and is used to
158 * verify the integirity of the @istream.
160 * Return value: a CamelCipherValidity structure containing information
161 * about the integrity of the input stream or %NULL on failure to
164 CamelCipherValidity *
165 camel_cipher_verify (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex)
167 CamelCipherValidity *valid;
169 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
171 camel_operation_start(NULL, _("Verifying message"));
173 CIPHER_LOCK(context);
175 valid = CCC_CLASS (context)->verify (context, ipart, ex);
177 CIPHER_UNLOCK(context);
179 camel_operation_end(NULL);
185 cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients,
186 struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
188 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
189 _("Encryption is not supported by this cipher"));
194 * camel_cipher_encrypt:
195 * @context: Cipher Context
196 * @userid: key id (or email address) to use when signing, or NULL to not sign.
197 * @recipients: an array of recipient key ids and/or email addresses
198 * @ipart: cleartext input stream
199 * @opart: ciphertext output stream
202 * Encrypts (and optionally signs) the cleartext input stream and
203 * writes the resulting ciphertext to the output stream.
205 * Return value: 0 for success or -1 for failure.
208 camel_cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients,
209 struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
213 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
215 camel_operation_start(NULL, _("Encrypting message"));
217 CIPHER_LOCK(context);
219 retval = CCC_CLASS (context)->encrypt (context, userid, recipients, ipart, opart, ex);
221 CIPHER_UNLOCK(context);
223 camel_operation_end(NULL);
228 static CamelCipherValidity *
229 cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
231 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
232 _("Decryption is not supported by this cipher"));
237 * camel_cipher_decrypt:
243 * Decrypts @ipart into @opart.
245 * Return value: A validity/encryption status.
247 CamelCipherValidity *
248 camel_cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
250 CamelCipherValidity *valid;
252 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
254 camel_operation_start(NULL, _("Decrypting message"));
256 CIPHER_LOCK(context);
258 valid = CCC_CLASS (context)->decrypt (context, ipart, opart, ex);
260 CIPHER_UNLOCK(context);
262 camel_operation_end(NULL);
268 cipher_import_keys (CamelCipherContext *context, struct _CamelStream *istream, CamelException *ex)
270 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
271 _("You may not import keys with this cipher"));
277 * camel_cipher_import_keys:
278 * @context: Cipher Context
279 * @istream: input stream (containing keys)
282 * Imports a stream of keys/certificates contained within @istream
283 * into the key/certificate database controlled by @ctx.
285 * Returns 0 on success or -1 on fail.
288 camel_cipher_import_keys (CamelCipherContext *context, struct _CamelStream *istream, CamelException *ex)
290 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
291 g_return_val_if_fail (CAMEL_IS_STREAM (istream), -1);
293 return CCC_CLASS (context)->import_keys (context, istream, ex);
297 cipher_export_keys (CamelCipherContext *context, GPtrArray *keys,
298 struct _CamelStream *ostream, CamelException *ex)
300 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
301 _("You may not export keys with this cipher"));
307 * camel_cipher_export_keys:
308 * @context: Cipher Context
309 * @keys: an array of key ids
310 * @ostream: output stream
313 * Exports the keys/certificates in @keys to the stream @ostream from
314 * the key/certificate database controlled by @ctx.
316 * Returns 0 on success or -1 on fail.
319 camel_cipher_export_keys (CamelCipherContext *context, GPtrArray *keys,
320 struct _CamelStream *ostream, CamelException *ex)
322 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
323 g_return_val_if_fail (CAMEL_IS_STREAM (ostream), -1);
324 g_return_val_if_fail (keys != NULL, -1);
326 return CCC_CLASS (context)->export_keys (context, keys, ostream, ex);
329 static CamelCipherHash
330 cipher_id_to_hash(CamelCipherContext *context, const char *id)
332 return CAMEL_CIPHER_HASH_DEFAULT;
335 /* a couple of util functions */
337 camel_cipher_id_to_hash(CamelCipherContext *context, const char *id)
339 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), CAMEL_CIPHER_HASH_DEFAULT);
341 return CCC_CLASS (context)->id_to_hash (context, id);
345 cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash)
351 camel_cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash)
353 g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
355 return CCC_CLASS (context)->hash_to_id (context, hash);
358 /* Cipher Validity stuff */
360 ccv_certinfo_free(CamelCipherCertInfo *info)
367 CamelCipherValidity *
368 camel_cipher_validity_new (void)
370 CamelCipherValidity *validity;
372 validity = g_malloc(sizeof(*validity));
373 camel_cipher_validity_init(validity);
379 camel_cipher_validity_init (CamelCipherValidity *validity)
381 g_assert (validity != NULL);
383 memset(validity, 0, sizeof(*validity));
384 e_dlist_init(&validity->children);
385 e_dlist_init(&validity->sign.signers);
386 e_dlist_init(&validity->encrypt.encrypters);
390 camel_cipher_validity_get_valid (CamelCipherValidity *validity)
392 return validity != NULL
393 && validity->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
397 camel_cipher_validity_set_valid (CamelCipherValidity *validity, gboolean valid)
399 g_assert (validity != NULL);
401 validity->sign.status = valid?CAMEL_CIPHER_VALIDITY_SIGN_GOOD:CAMEL_CIPHER_VALIDITY_SIGN_BAD;
405 camel_cipher_validity_get_description (CamelCipherValidity *validity)
407 if (validity == NULL)
410 return validity->sign.description;
414 camel_cipher_validity_set_description (CamelCipherValidity *validity, const gchar *description)
416 g_assert (validity != NULL);
418 g_free(validity->sign.description);
419 validity->sign.description = g_strdup(description);
423 camel_cipher_validity_clear (CamelCipherValidity *validity)
425 g_assert (validity != NULL);
427 /* TODO: this doesn't free children/clear key lists */
428 g_free(validity->sign.description);
429 g_free(validity->encrypt.description);
430 camel_cipher_validity_init(validity);
433 CamelCipherValidity *
434 camel_cipher_validity_clone(CamelCipherValidity *vin)
436 CamelCipherValidity *vo;
437 CamelCipherCertInfo *info;
439 vo = camel_cipher_validity_new();
440 vo->sign.status = vin->sign.status;
441 vo->sign.description = g_strdup(vin->sign.description);
442 vo->encrypt.status = vin->encrypt.status;
443 vo->encrypt.description = g_strdup(vin->encrypt.description);
445 info = (CamelCipherCertInfo *)vin->sign.signers.head;
447 camel_cipher_validity_add_certinfo(vo, CAMEL_CIPHER_VALIDITY_SIGN, info->name, info->email);
451 info = (CamelCipherCertInfo *)vin->encrypt.encrypters.head;
453 camel_cipher_validity_add_certinfo(vo, CAMEL_CIPHER_VALIDITY_ENCRYPT, info->name, info->email);
461 * camel_cipher_validity_add_certinfo:
467 * Add a cert info to the signer or encrypter info.
470 camel_cipher_validity_add_certinfo(CamelCipherValidity *vin, enum _camel_cipher_validity_mode_t mode, const char *name, const char *email)
472 CamelCipherCertInfo *info;
475 info = g_malloc0(sizeof(*info));
476 info->name = g_strdup(name);
477 info->email = g_strdup(email);
479 list = (mode==CAMEL_CIPHER_VALIDITY_SIGN)?&vin->sign.signers:&vin->encrypt.encrypters;
480 e_dlist_addtail(list, (EDListNode *)info);
482 printf("adding certinfo %s <%s>\n", name?name:"unset", email?email:"unset");
486 * camel_cipher_validity_envelope:
490 * Calculate a conglomerate validity based on wrapping one secure part inside
494 camel_cipher_validity_envelope(CamelCipherValidity *parent, CamelCipherValidity *valid)
496 CamelCipherCertInfo *info;
498 if (parent->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE
499 && parent->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
500 && valid->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE
501 && valid->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
502 /* case 1: only signed inside only encrypted -> merge both */
503 parent->encrypt.status = valid->encrypt.status;
504 parent->encrypt.description = g_strdup(valid->encrypt.description);
505 info = (CamelCipherCertInfo *)valid->encrypt.encrypters.head;
507 camel_cipher_validity_add_certinfo(parent, CAMEL_CIPHER_VALIDITY_ENCRYPT, info->name, info->email);
510 } else if (parent->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_NONE
511 && parent->encrypt.status != CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE
512 && valid->sign.status != CAMEL_CIPHER_VALIDITY_SIGN_NONE
513 && valid->encrypt.status == CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE) {
514 /* case 2: only encrypted inside only signed */
515 parent->sign.status = valid->sign.status;
516 parent->sign.description = g_strdup(valid->sign.description);
517 info = (CamelCipherCertInfo *)valid->sign.signers.head;
519 camel_cipher_validity_add_certinfo(parent, CAMEL_CIPHER_VALIDITY_SIGN, info->name, info->email);
523 /* Otherwise, I dunno - what do you do? */
527 camel_cipher_validity_free (CamelCipherValidity *validity)
529 CamelCipherValidity *child;
530 CamelCipherCertInfo *info;
532 if (validity == NULL)
535 while ((child = (CamelCipherValidity *)e_dlist_remhead(&validity->children)))
536 camel_cipher_validity_free(child);
538 while ((info = (CamelCipherCertInfo *)e_dlist_remhead(&validity->sign.signers)))
539 ccv_certinfo_free(info);
541 while ((info = (CamelCipherCertInfo *)e_dlist_remhead(&validity->encrypt.encrypters)))
542 ccv_certinfo_free(info);
544 camel_cipher_validity_clear(validity);
548 /* ********************************************************************** */
551 camel_cipher_context_init (CamelCipherContext *context)
553 context->priv = g_new0 (struct _CamelCipherContextPrivate, 1);
554 context->priv->lock = g_mutex_new ();
558 camel_cipher_context_finalise (CamelObject *o)
560 CamelCipherContext *context = (CamelCipherContext *)o;
562 camel_object_unref (CAMEL_OBJECT (context->session));
564 g_mutex_free (context->priv->lock);
566 g_free (context->priv);
570 camel_cipher_context_class_init (CamelCipherContextClass *camel_cipher_context_class)
572 parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
574 camel_cipher_context_class->hash_to_id = cipher_hash_to_id;
575 camel_cipher_context_class->id_to_hash = cipher_id_to_hash;
576 camel_cipher_context_class->sign = cipher_sign;
577 camel_cipher_context_class->verify = cipher_verify;
578 camel_cipher_context_class->encrypt = cipher_encrypt;
579 camel_cipher_context_class->decrypt = cipher_decrypt;
580 camel_cipher_context_class->import_keys = cipher_import_keys;
581 camel_cipher_context_class->export_keys = cipher_export_keys;
585 camel_cipher_context_get_type (void)
587 static CamelType type = CAMEL_INVALID_TYPE;
589 if (type == CAMEL_INVALID_TYPE) {
590 type = camel_type_register (camel_object_get_type (),
591 "CamelCipherContext",
592 sizeof (CamelCipherContext),
593 sizeof (CamelCipherContextClass),
594 (CamelObjectClassInitFunc) camel_cipher_context_class_init,
596 (CamelObjectInitFunc) camel_cipher_context_init,
597 (CamelObjectFinalizeFunc) camel_cipher_context_finalise);
603 /* See rfc3156, section 2 and others */
604 /* We do this simply: Anything not base64 must be qp
605 This is so that we can safely translate any occurance of "From "
606 into the quoted-printable escaped version safely. */
608 cc_prepare_sign(CamelMimePart *part)
610 CamelDataWrapper *dw;
611 CamelTransferEncoding encoding;
614 dw = camel_medium_get_content_object((CamelMedium *)part);
618 if (CAMEL_IS_MULTIPART (dw)) {
619 parts = camel_multipart_get_number((CamelMultipart *)dw);
620 for (i = 0; i < parts; i++)
621 cc_prepare_sign(camel_multipart_get_part((CamelMultipart *)dw, i));
622 } else if (CAMEL_IS_MIME_MESSAGE (dw)) {
623 cc_prepare_sign((CamelMimePart *)dw);
625 encoding = camel_mime_part_get_encoding(part);
627 if (encoding != CAMEL_TRANSFER_ENCODING_BASE64
628 && encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
629 camel_mime_part_set_encoding(part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
635 * camel_cipher_canonical_to_stream:
636 * @part: Part to write.
637 * @flags: flags for the canonicalisation filter (CamelMimeFilterCanon)
638 * @ostream: stream to write canonicalised output to.
640 * Writes a part to a stream in a canonicalised format, suitable for signing/encrypting.
642 * The transfer encoding paramaters for the part may be changed by this function.
644 * Return value: -1 on error;
647 camel_cipher_canonical_to_stream(CamelMimePart *part, guint32 flags, CamelStream *ostream)
649 CamelStreamFilter *filter;
650 CamelMimeFilter *canon;
653 if (flags & (CAMEL_MIME_FILTER_CANON_FROM|CAMEL_MIME_FILTER_CANON_STRIP))
654 cc_prepare_sign(part);
656 filter = camel_stream_filter_new_with_stream(ostream);
657 canon = camel_mime_filter_canon_new(flags);
658 camel_stream_filter_add(filter, canon);
659 camel_object_unref(canon);
661 if (camel_data_wrapper_write_to_stream((CamelDataWrapper *)part, (CamelStream *)filter) != -1
662 && camel_stream_flush((CamelStream *)filter) != -1)
665 camel_object_unref(filter);
666 camel_stream_reset(ostream);