Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-cipher-context.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2001-2003 Ximian, Inc. (www.ximian.com)
6  *
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.
10  *
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.
15  *
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.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28
29 #include <glib.h>
30 #include <glib/gi18n-lib.h>
31
32 #include "camel-cipher-context.h"
33 #include "camel-stream.h"
34 #include "camel-operation.h"
35
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"
42
43 #define CIPHER_LOCK(ctx)   g_mutex_lock (((CamelCipherContext *) ctx)->priv->lock)
44 #define CIPHER_UNLOCK(ctx) g_mutex_unlock (((CamelCipherContext *) ctx)->priv->lock);
45
46 #define d(x)
47
48 #define CCC_CLASS(o) CAMEL_CIPHER_CONTEXT_CLASS(CAMEL_OBJECT_GET_CLASS(o))
49
50 struct _CamelCipherContextPrivate {
51         GMutex *lock;
52 };
53
54 static CamelObjectClass *parent_class = NULL;
55
56 /**
57  * camel_cipher_context_new:
58  * @session: CamelSession
59  *
60  * This creates a new CamelCipherContext object which is used to sign,
61  * verify, encrypt and decrypt streams.
62  *
63  * Return value: the new CamelCipherContext
64  **/
65 CamelCipherContext *
66 camel_cipher_context_new (CamelSession *session)
67 {
68         CamelCipherContext *context;
69         
70         g_return_val_if_fail (session != NULL, NULL);
71         
72         context = CAMEL_CIPHER_CONTEXT (camel_object_new (CAMEL_CIPHER_CONTEXT_TYPE));
73         
74         camel_object_ref (session);
75         context->session = session;
76         
77         return context;
78 }
79
80 /**
81  * camel_cipher_context_construct:
82  * @context: CamelCipherContext
83  * @session: CamelSession
84  *
85  * Constucts the CamelCipherContext
86  **/
87 void
88 camel_cipher_context_construct (CamelCipherContext *context, CamelSession *session)
89 {
90         g_return_if_fail (CAMEL_IS_CIPHER_CONTEXT (context));
91         g_return_if_fail (CAMEL_IS_SESSION (session));
92         
93         camel_object_ref (session);
94         context->session = session;
95 }
96
97 static int
98 cipher_sign (CamelCipherContext *ctx, const char *userid, CamelCipherHash hash,
99              struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
100 {
101         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
102                              _("Signing is not supported by this cipher"));
103         return -1;
104 }
105
106 /**
107  * camel_cipher_sign:
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.
113  * @ex: exception
114  *
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.
117  *
118  * Return value: 0 for success or -1 for failure.
119  **/
120 int
121 camel_cipher_sign (CamelCipherContext *context, const char *userid, CamelCipherHash hash,
122                    struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
123 {
124         int retval;
125         
126         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
127
128         camel_operation_start(NULL, _("Signing message"));
129
130         CIPHER_LOCK(context);
131         
132         retval = CCC_CLASS (context)->sign (context, userid, hash, ipart, opart, ex);
133         
134         CIPHER_UNLOCK(context);
135
136         camel_operation_end(NULL);
137
138         return retval;
139 }
140
141 static CamelCipherValidity *
142 cipher_verify (CamelCipherContext *context, struct _CamelMimePart *sigpart, CamelException *ex)
143 {
144         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
145                              _("Verifying is not supported by this cipher"));
146         return NULL;
147 }
148
149 /**
150  * camel_cipher_verify:
151  * @context: Cipher Context
152  * @ipart: part to verify
153  * @ex: exception
154  *
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.
159  *
160  * Return value: a CamelCipherValidity structure containing information
161  * about the integrity of the input stream or %NULL on failure to
162  * execute at all.
163  **/
164 CamelCipherValidity *
165 camel_cipher_verify (CamelCipherContext *context, struct _CamelMimePart *ipart, CamelException *ex)
166 {
167         CamelCipherValidity *valid;
168         
169         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
170         
171         camel_operation_start(NULL, _("Verifying message"));
172
173         CIPHER_LOCK(context);
174         
175         valid = CCC_CLASS (context)->verify (context, ipart, ex);
176         
177         CIPHER_UNLOCK(context);
178
179         camel_operation_end(NULL);
180         
181         return valid;
182 }
183
184 static int
185 cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients,
186                 struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
187 {
188         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
189                              _("Encryption is not supported by this cipher"));
190         return -1;
191 }
192
193 /**
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
200  * @ex: exception
201  *
202  * Encrypts (and optionally signs) the cleartext input stream and
203  * writes the resulting ciphertext to the output stream.
204  *
205  * Return value: 0 for success or -1 for failure.
206  **/
207 int
208 camel_cipher_encrypt (CamelCipherContext *context, const char *userid, GPtrArray *recipients,
209                       struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
210 {
211         int retval;
212         
213         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
214
215         camel_operation_start(NULL, _("Encrypting message"));
216         
217         CIPHER_LOCK(context);
218         
219         retval = CCC_CLASS (context)->encrypt (context, userid, recipients, ipart, opart, ex);
220         
221         CIPHER_UNLOCK(context);
222
223         camel_operation_end(NULL);
224         
225         return retval;
226 }
227
228 static CamelCipherValidity *
229 cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
230 {
231         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
232                              _("Decryption is not supported by this cipher"));
233         return NULL;
234 }
235
236 /**
237  * camel_cipher_decrypt:
238  * @context: 
239  * @ipart: 
240  * @opart: 
241  * @ex: 
242  * 
243  * Decrypts @ipart into @opart.
244  * 
245  * Return value: A validity/encryption status.
246  **/
247 CamelCipherValidity *
248 camel_cipher_decrypt(CamelCipherContext *context, struct _CamelMimePart *ipart, struct _CamelMimePart *opart, CamelException *ex)
249 {
250         CamelCipherValidity *valid;
251
252         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
253
254         camel_operation_start(NULL, _("Decrypting message"));
255         
256         CIPHER_LOCK(context);
257         
258         valid = CCC_CLASS (context)->decrypt (context, ipart, opart, ex);
259         
260         CIPHER_UNLOCK(context);
261
262         camel_operation_end(NULL);
263         
264         return valid;
265 }
266
267 static int
268 cipher_import_keys (CamelCipherContext *context, struct _CamelStream *istream, CamelException *ex)
269 {
270         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
271                              _("You may not import keys with this cipher"));
272         
273         return -1;
274 }
275
276 /**
277  * camel_cipher_import_keys:
278  * @context: Cipher Context
279  * @istream: input stream (containing keys)
280  * @ex: exception
281  *
282  * Imports a stream of keys/certificates contained within @istream
283  * into the key/certificate database controlled by @ctx.
284  *
285  * Returns 0 on success or -1 on fail.
286  **/
287 int
288 camel_cipher_import_keys (CamelCipherContext *context, struct _CamelStream *istream, CamelException *ex)
289 {
290         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), -1);
291         g_return_val_if_fail (CAMEL_IS_STREAM (istream), -1);
292         
293         return CCC_CLASS (context)->import_keys (context, istream, ex);
294 }
295
296 static int
297 cipher_export_keys (CamelCipherContext *context, GPtrArray *keys,
298                     struct _CamelStream *ostream, CamelException *ex)
299 {
300         camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
301                              _("You may not export keys with this cipher"));
302         
303         return -1;
304 }
305
306 /**
307  * camel_cipher_export_keys:
308  * @context: Cipher Context
309  * @keys: an array of key ids
310  * @ostream: output stream
311  * @ex: exception
312  *
313  * Exports the keys/certificates in @keys to the stream @ostream from
314  * the key/certificate database controlled by @ctx.
315  *
316  * Returns 0 on success or -1 on fail.
317  **/
318 int
319 camel_cipher_export_keys (CamelCipherContext *context, GPtrArray *keys,
320                           struct _CamelStream *ostream, CamelException *ex)
321 {
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);
325         
326         return CCC_CLASS (context)->export_keys (context, keys, ostream, ex);
327 }
328
329 static CamelCipherHash
330 cipher_id_to_hash(CamelCipherContext *context, const char *id)
331 {
332         return CAMEL_CIPHER_HASH_DEFAULT;
333 }
334
335 /* a couple of util functions */
336 CamelCipherHash
337 camel_cipher_id_to_hash(CamelCipherContext *context, const char *id)
338 {
339         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), CAMEL_CIPHER_HASH_DEFAULT);
340         
341         return CCC_CLASS (context)->id_to_hash (context, id);
342 }
343
344 static const char *
345 cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash)
346 {
347         return NULL;
348 }
349
350 const char *
351 camel_cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash)
352 {
353         g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL);
354         
355         return CCC_CLASS (context)->hash_to_id (context, hash);
356 }
357
358 /* Cipher Validity stuff */
359 static void
360 ccv_certinfo_free(CamelCipherCertInfo *info)
361 {
362         g_free(info->name);
363         g_free(info->email);
364         g_free(info);
365 }
366
367 CamelCipherValidity *
368 camel_cipher_validity_new (void)
369 {
370         CamelCipherValidity *validity;
371         
372         validity = g_malloc(sizeof(*validity));
373         camel_cipher_validity_init(validity);
374
375         return validity;
376 }
377
378 void
379 camel_cipher_validity_init (CamelCipherValidity *validity)
380 {
381         g_assert (validity != NULL);
382
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);
387 }
388
389 gboolean
390 camel_cipher_validity_get_valid (CamelCipherValidity *validity)
391 {
392         return validity != NULL
393                 && validity->sign.status == CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
394 }
395
396 void
397 camel_cipher_validity_set_valid (CamelCipherValidity *validity, gboolean valid)
398 {
399         g_assert (validity != NULL);
400         
401         validity->sign.status = valid?CAMEL_CIPHER_VALIDITY_SIGN_GOOD:CAMEL_CIPHER_VALIDITY_SIGN_BAD;
402 }
403
404 gchar *
405 camel_cipher_validity_get_description (CamelCipherValidity *validity)
406 {
407         if (validity == NULL)
408                 return NULL;
409         
410         return validity->sign.description;
411 }
412
413 void
414 camel_cipher_validity_set_description (CamelCipherValidity *validity, const gchar *description)
415 {
416         g_assert (validity != NULL);
417         
418         g_free(validity->sign.description);
419         validity->sign.description = g_strdup(description);
420 }
421
422 void
423 camel_cipher_validity_clear (CamelCipherValidity *validity)
424 {
425         g_assert (validity != NULL);
426
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);
431 }
432
433 CamelCipherValidity *
434 camel_cipher_validity_clone(CamelCipherValidity *vin)
435 {
436         CamelCipherValidity *vo;
437         CamelCipherCertInfo *info;
438
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);
444
445         info = (CamelCipherCertInfo *)vin->sign.signers.head;
446         while (info->next) {
447                 camel_cipher_validity_add_certinfo(vo, CAMEL_CIPHER_VALIDITY_SIGN, info->name, info->email);
448                 info = info->next;
449         }
450
451         info = (CamelCipherCertInfo *)vin->encrypt.encrypters.head;
452         while (info->next) {
453                 camel_cipher_validity_add_certinfo(vo, CAMEL_CIPHER_VALIDITY_ENCRYPT, info->name, info->email);
454                 info = info->next;
455         }
456
457         return vo;
458 }
459
460 /**
461  * camel_cipher_validity_add_certinfo:
462  * @vin: 
463  * @mode: 
464  * @name: 
465  * @email: 
466  * 
467  * Add a cert info to the signer or encrypter info.
468  **/
469 void
470 camel_cipher_validity_add_certinfo(CamelCipherValidity *vin, enum _camel_cipher_validity_mode_t mode, const char *name, const char *email)
471 {
472         CamelCipherCertInfo *info;
473         EDList *list;
474
475         info = g_malloc0(sizeof(*info));
476         info->name = g_strdup(name);
477         info->email = g_strdup(email);
478
479         list = (mode==CAMEL_CIPHER_VALIDITY_SIGN)?&vin->sign.signers:&vin->encrypt.encrypters;
480         e_dlist_addtail(list, (EDListNode *)info);
481
482         printf("adding certinfo %s <%s>\n", name?name:"unset", email?email:"unset");
483 }
484
485 /**
486  * camel_cipher_validity_envelope:
487  * @parent:
488  * @valid:
489  * 
490  * Calculate a conglomerate validity based on wrapping one secure part inside
491  * another one.
492  **/
493 void
494 camel_cipher_validity_envelope(CamelCipherValidity *parent, CamelCipherValidity *valid)
495 {
496         CamelCipherCertInfo *info;
497
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;
506                 while (info->next) {
507                         camel_cipher_validity_add_certinfo(parent, CAMEL_CIPHER_VALIDITY_ENCRYPT, info->name, info->email);
508                         info = info->next;
509                 }
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;
518                 while (info->next) {
519                         camel_cipher_validity_add_certinfo(parent, CAMEL_CIPHER_VALIDITY_SIGN, info->name, info->email);
520                         info = info->next;
521                 }
522         }
523         /* Otherwise, I dunno - what do you do? */
524 }
525
526 void
527 camel_cipher_validity_free (CamelCipherValidity *validity)
528 {
529         CamelCipherValidity *child;
530         CamelCipherCertInfo *info;
531
532         if (validity == NULL)
533                 return;
534
535         while ((child = (CamelCipherValidity *)e_dlist_remhead(&validity->children)))
536                 camel_cipher_validity_free(child);
537
538         while ((info = (CamelCipherCertInfo *)e_dlist_remhead(&validity->sign.signers)))
539                 ccv_certinfo_free(info);
540
541         while ((info = (CamelCipherCertInfo *)e_dlist_remhead(&validity->encrypt.encrypters)))
542                 ccv_certinfo_free(info);
543
544         camel_cipher_validity_clear(validity);
545         g_free(validity);
546 }
547
548 /* ********************************************************************** */
549
550 static void
551 camel_cipher_context_init (CamelCipherContext *context)
552 {
553         context->priv = g_new0 (struct _CamelCipherContextPrivate, 1);
554         context->priv->lock = g_mutex_new ();
555 }
556
557 static void
558 camel_cipher_context_finalise (CamelObject *o)
559 {
560         CamelCipherContext *context = (CamelCipherContext *)o;
561         
562         camel_object_unref (CAMEL_OBJECT (context->session));
563         
564         g_mutex_free (context->priv->lock);
565         
566         g_free (context->priv);
567 }
568
569 static void
570 camel_cipher_context_class_init (CamelCipherContextClass *camel_cipher_context_class)
571 {
572         parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
573         
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;
582 }
583
584 CamelType
585 camel_cipher_context_get_type (void)
586 {
587         static CamelType type = CAMEL_INVALID_TYPE;
588         
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,
595                                             NULL,
596                                             (CamelObjectInitFunc) camel_cipher_context_init,
597                                             (CamelObjectFinalizeFunc) camel_cipher_context_finalise);
598         }
599         
600         return type;
601 }
602
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. */
607 static void
608 cc_prepare_sign(CamelMimePart *part)
609 {
610         CamelDataWrapper *dw;
611         CamelTransferEncoding encoding;
612         int parts, i;
613         
614         dw = camel_medium_get_content_object((CamelMedium *)part);
615         if (!dw)
616                 return;
617         
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);
624         } else {
625                 encoding = camel_mime_part_get_encoding(part);
626
627                 if (encoding != CAMEL_TRANSFER_ENCODING_BASE64
628                     && encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
629                         camel_mime_part_set_encoding(part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
630                 }
631         }
632 }
633
634 /**
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.
639  * 
640  * Writes a part to a stream in a canonicalised format, suitable for signing/encrypting.
641  *
642  * The transfer encoding paramaters for the part may be changed by this function.
643  * 
644  * Return value: -1 on error;
645  **/
646 int
647 camel_cipher_canonical_to_stream(CamelMimePart *part, guint32 flags, CamelStream *ostream)
648 {
649         CamelStreamFilter *filter;
650         CamelMimeFilter *canon;
651         int res = -1;
652
653         if (flags & (CAMEL_MIME_FILTER_CANON_FROM|CAMEL_MIME_FILTER_CANON_STRIP))
654                 cc_prepare_sign(part);
655
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);
660
661         if (camel_data_wrapper_write_to_stream((CamelDataWrapper *)part, (CamelStream *)filter) != -1
662             && camel_stream_flush((CamelStream *)filter) != -1)
663                 res = 0;
664
665         camel_object_unref(filter);
666         camel_stream_reset(ostream);
667
668         return res;
669 }