Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / gmime-pkcs7-context.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "gmime-pkcs7-context.h"
31 #ifdef ENABLE_SMIME
32 #include "gmime-filter-charset.h"
33 #include "gmime-stream-filter.h"
34 #include "gmime-stream-pipe.h"
35 #include "gmime-stream-mem.h"
36 #include "gmime-stream-fs.h"
37 #include "gmime-charset.h"
38 #endif /* ENABLE_SMIME */
39 #include "gmime-error.h"
40
41 #ifdef ENABLE_SMIME
42 #include <gpgme.h>
43 #endif
44
45 #ifdef ENABLE_DEBUG
46 #define d(x) x
47 #else
48 #define d(x)
49 #endif
50
51 #define _(x) x
52
53
54 /**
55  * SECTION: gmime-pkcs7-context
56  * @title: GMimePkcs7Context
57  * @short_description: PKCS7 crypto contexts
58  * @see_also: #GMimeCryptoContext
59  *
60  * A #GMimePkcs7Context is a #GMimeCryptoContext that uses GnuPG to do
61  * all of the encryption and digital signatures.
62  **/
63
64 typedef struct _GMimePkcs7ContextPrivate {
65         gboolean always_trust;
66 #ifdef ENABLE_SMIME
67         gpgme_ctx_t ctx;
68 #endif
69 } Pkcs7Ctx;
70
71 static void g_mime_pkcs7_context_class_init (GMimePkcs7ContextClass *klass);
72 static void g_mime_pkcs7_context_init (GMimePkcs7Context *ctx, GMimePkcs7ContextClass *klass);
73 static void g_mime_pkcs7_context_finalize (GObject *object);
74
75 static GMimeDigestAlgo pkcs7_digest_id (GMimeCryptoContext *ctx, const char *name);
76
77 static const char *pkcs7_digest_name (GMimeCryptoContext *ctx, GMimeDigestAlgo digest);
78
79 static const char *pkcs7_get_signature_protocol (GMimeCryptoContext *ctx);
80
81 static const char *pkcs7_get_encryption_protocol (GMimeCryptoContext *ctx);
82
83 static const char *pkcs7_get_key_exchange_protocol (GMimeCryptoContext *ctx);
84
85 static int pkcs7_sign (GMimeCryptoContext *ctx, const char *userid,
86                        GMimeDigestAlgo digest, GMimeStream *istream,
87                        GMimeStream *ostream, GError **err);
88         
89 static GMimeSignatureList *pkcs7_verify (GMimeCryptoContext *ctx, GMimeDigestAlgo digest,
90                                          GMimeStream *istream, GMimeStream *sigstream,
91                                          GError **err);
92
93 static int pkcs7_encrypt (GMimeCryptoContext *ctx, gboolean sign, const char *userid,
94                           GMimeDigestAlgo digest, GPtrArray *recipients, GMimeStream *istream,
95                           GMimeStream *ostream, GError **err);
96
97 static GMimeDecryptResult *pkcs7_decrypt (GMimeCryptoContext *ctx, GMimeStream *istream,
98                                           GMimeStream *ostream, GError **err);
99
100 static int pkcs7_import_keys (GMimeCryptoContext *ctx, GMimeStream *istream,
101                               GError **err);
102
103 static int pkcs7_export_keys (GMimeCryptoContext *ctx, GPtrArray *keys,
104                               GMimeStream *ostream, GError **err);
105
106
107 static GMimeCryptoContextClass *parent_class = NULL;
108
109
110 GType
111 g_mime_pkcs7_context_get_type (void)
112 {
113         static GType type = 0;
114         
115         if (!type) {
116                 static const GTypeInfo info = {
117                         sizeof (GMimePkcs7ContextClass),
118                         NULL, /* base_class_init */
119                         NULL, /* base_class_finalize */
120                         (GClassInitFunc) g_mime_pkcs7_context_class_init,
121                         NULL, /* class_finalize */
122                         NULL, /* class_data */
123                         sizeof (GMimePkcs7Context),
124                         0,    /* n_preallocs */
125                         (GInstanceInitFunc) g_mime_pkcs7_context_init,
126                 };
127                 
128                 type = g_type_register_static (GMIME_TYPE_CRYPTO_CONTEXT, "GMimePkcs7Context", &info, 0);
129         }
130         
131         return type;
132 }
133
134
135 static void
136 g_mime_pkcs7_context_class_init (GMimePkcs7ContextClass *klass)
137 {
138         GObjectClass *object_class = G_OBJECT_CLASS (klass);
139         GMimeCryptoContextClass *crypto_class = GMIME_CRYPTO_CONTEXT_CLASS (klass);
140         
141         parent_class = g_type_class_ref (G_TYPE_OBJECT);
142         
143         object_class->finalize = g_mime_pkcs7_context_finalize;
144         
145         crypto_class->digest_id = pkcs7_digest_id;
146         crypto_class->digest_name = pkcs7_digest_name;
147         crypto_class->sign = pkcs7_sign;
148         crypto_class->verify = pkcs7_verify;
149         crypto_class->encrypt = pkcs7_encrypt;
150         crypto_class->decrypt = pkcs7_decrypt;
151         crypto_class->import_keys = pkcs7_import_keys;
152         crypto_class->export_keys = pkcs7_export_keys;
153         crypto_class->get_signature_protocol = pkcs7_get_signature_protocol;
154         crypto_class->get_encryption_protocol = pkcs7_get_encryption_protocol;
155         crypto_class->get_key_exchange_protocol = pkcs7_get_key_exchange_protocol;
156 }
157
158 static void
159 g_mime_pkcs7_context_init (GMimePkcs7Context *ctx, GMimePkcs7ContextClass *klass)
160 {
161         ctx->priv = g_slice_new (Pkcs7Ctx);
162         ctx->priv->always_trust = FALSE;
163 #ifdef ENABLE_SMIME
164         ctx->priv->ctx = NULL;
165 #endif
166 }
167
168 static void
169 g_mime_pkcs7_context_finalize (GObject *object)
170 {
171         GMimePkcs7Context *ctx = (GMimePkcs7Context *) object;
172         
173 #ifdef ENABLE_SMIME
174         if (ctx->priv->ctx)
175                 gpgme_release (ctx->priv->ctx);
176 #endif
177         
178         g_slice_free (Pkcs7Ctx, ctx->priv);
179         
180         G_OBJECT_CLASS (parent_class)->finalize (object);
181 }
182
183 static GMimeDigestAlgo
184 pkcs7_digest_id (GMimeCryptoContext *ctx, const char *name)
185 {
186         if (name == NULL)
187                 return GMIME_DIGEST_ALGO_DEFAULT;
188         
189         if (!g_ascii_strcasecmp (name, "md2"))
190                 return GMIME_DIGEST_ALGO_MD2;
191         else if (!g_ascii_strcasecmp (name, "md4"))
192                 return GMIME_DIGEST_ALGO_MD4;
193         else if (!g_ascii_strcasecmp (name, "md5"))
194                 return GMIME_DIGEST_ALGO_MD5;
195         else if (!g_ascii_strcasecmp (name, "sha1"))
196                 return GMIME_DIGEST_ALGO_SHA1;
197         else if (!g_ascii_strcasecmp (name, "sha224"))
198                 return GMIME_DIGEST_ALGO_SHA224;
199         else if (!g_ascii_strcasecmp (name, "sha256"))
200                 return GMIME_DIGEST_ALGO_SHA256;
201         else if (!g_ascii_strcasecmp (name, "sha384"))
202                 return GMIME_DIGEST_ALGO_SHA384;
203         else if (!g_ascii_strcasecmp (name, "sha512"))
204                 return GMIME_DIGEST_ALGO_SHA512;
205         else if (!g_ascii_strcasecmp (name, "ripemd160"))
206                 return GMIME_DIGEST_ALGO_RIPEMD160;
207         else if (!g_ascii_strcasecmp (name, "tiger192"))
208                 return GMIME_DIGEST_ALGO_TIGER192;
209         else if (!g_ascii_strcasecmp (name, "haval-5-160"))
210                 return GMIME_DIGEST_ALGO_HAVAL5160;
211         
212         return GMIME_DIGEST_ALGO_DEFAULT;
213 }
214
215 static const char *
216 pkcs7_digest_name (GMimeCryptoContext *ctx, GMimeDigestAlgo digest)
217 {
218         switch (digest) {
219         case GMIME_DIGEST_ALGO_MD2:
220                 return "md2";
221         case GMIME_DIGEST_ALGO_MD4:
222                 return "md4";
223         case GMIME_DIGEST_ALGO_MD5:
224                 return "md5";
225         case GMIME_DIGEST_ALGO_SHA1:
226                 return "sha1";
227         case GMIME_DIGEST_ALGO_SHA224:
228                 return "sha224";
229         case GMIME_DIGEST_ALGO_SHA256:
230                 return "sha256";
231         case GMIME_DIGEST_ALGO_SHA384:
232                 return "sha384";
233         case GMIME_DIGEST_ALGO_SHA512:
234                 return "sha512";
235         case GMIME_DIGEST_ALGO_RIPEMD160:
236                 return "ripemd160";
237         case GMIME_DIGEST_ALGO_TIGER192:
238                 return "tiger192";
239         case GMIME_DIGEST_ALGO_HAVAL5160:
240                 return "haval-5-160";
241         default:
242                 return "sha1";
243         }
244 }
245
246 static const char *
247 pkcs7_get_signature_protocol (GMimeCryptoContext *ctx)
248 {
249         return "application/pkcs7-signature";
250 }
251
252 static const char *
253 pkcs7_get_encryption_protocol (GMimeCryptoContext *ctx)
254 {
255         return "application/pkcs7-mime";
256 }
257
258 static const char *
259 pkcs7_get_key_exchange_protocol (GMimeCryptoContext *ctx)
260 {
261         return "application/pkcs7-keys";
262 }
263
264 #ifdef ENABLE_SMIME
265 static gpgme_error_t
266 pkcs7_passphrase_cb (void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd)
267 {
268         GMimeCryptoContext *context = (GMimeCryptoContext *) hook;
269         GMimeStream *stream;
270         gpgme_error_t error;
271         GError *err = NULL;
272         gboolean rv;
273         
274         if (context->request_passwd) {
275                 stream = g_mime_stream_pipe_new (fd);
276                 rv = context->request_passwd (context, uid_hint, passphrase_info, prev_was_bad, stream, &err);
277                 g_object_unref (stream);
278         } else {
279                 return GPG_ERR_GENERAL;
280         }
281         
282         if (!rv) {
283                 error = GPG_ERR_CANCELED;
284                 g_error_free (err);
285         } else {
286                 error = GPG_ERR_NO_ERROR;
287         }
288         
289         return error;
290 }
291
292 static ssize_t
293 pkcs7_stream_read (void *stream, void *buffer, size_t size)
294 {
295         return g_mime_stream_read ((GMimeStream *) stream, (char *) buffer, size);
296 }
297
298 static ssize_t
299 pkcs7_stream_write (void *stream, const void *buffer, size_t size)
300 {
301         return g_mime_stream_write ((GMimeStream *) stream, (const char *) buffer, size);
302 }
303
304 static off_t
305 pkcs7_stream_seek (void *stream, off_t offset, int whence)
306 {
307         switch (whence) {
308         case SEEK_SET:
309                 return (off_t) g_mime_stream_seek ((GMimeStream *) stream, (gint64) offset, GMIME_STREAM_SEEK_SET);
310         case SEEK_CUR:
311                 return (off_t) g_mime_stream_seek ((GMimeStream *) stream, (gint64) offset, GMIME_STREAM_SEEK_CUR);
312         case SEEK_END:
313                 return (off_t) g_mime_stream_seek ((GMimeStream *) stream, (gint64) offset, GMIME_STREAM_SEEK_END);
314         default:
315                 return -1;
316         }
317 }
318
319 static void
320 pkcs7_stream_free (void *stream)
321 {
322         /* no-op */
323 }
324
325 static struct gpgme_data_cbs pkcs7_stream_funcs = {
326         pkcs7_stream_read,
327         pkcs7_stream_write,
328         pkcs7_stream_seek,
329         pkcs7_stream_free
330 };
331
332
333
334 #define KEY_IS_OK(k)   (!((k)->expired || (k)->revoked ||       \
335                           (k)->disabled || (k)->invalid))
336
337 static gpgme_key_t
338 pkcs7_get_key_by_name (Pkcs7Ctx *pkcs7, const char *name, gboolean secret, GError **err)
339 {
340         time_t now = time (NULL);
341         gpgme_key_t key = NULL;
342         gpgme_subkey_t subkey;
343         gboolean bad = FALSE;
344         gpgme_error_t error;
345         int errval = 0;
346         
347         if ((error = gpgme_op_keylist_start (pkcs7->ctx, name, secret)) != GPG_ERR_NO_ERROR) {
348                 if (secret)
349                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not list secret keys for \"%s\""), name);
350                 else
351                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not list keys for \"%s\""), name);
352                 return NULL;
353         }
354         
355         while ((error = gpgme_op_keylist_next (pkcs7->ctx, &key)) == GPG_ERR_NO_ERROR) {
356                 /* check if this key and the relevant subkey are usable */
357                 if (KEY_IS_OK (key)) {
358                         subkey = key->subkeys;
359                         
360                         while (subkey && ((secret && !subkey->can_sign) ||
361                                           (!secret && !subkey->can_encrypt)))
362                                 subkey = subkey->next;
363                         
364                         if (subkey && KEY_IS_OK (subkey) && 
365                             (subkey->expires == 0 || subkey->expires > now))
366                                 break;
367                         
368                         if (subkey->expired)
369                                 errval = GPG_ERR_KEY_EXPIRED;
370                         else
371                                 errval = GPG_ERR_BAD_KEY;
372                 } else {
373                         if (key->expired)
374                                 errval = GPG_ERR_KEY_EXPIRED;
375                         else
376                                 errval = GPG_ERR_BAD_KEY;
377                 }
378                 
379                 gpgme_key_unref (key);
380                 bad = TRUE;
381                 key = NULL;
382         }
383         
384         gpgme_op_keylist_end (pkcs7->ctx);
385         
386         if (error != GPG_ERR_NO_ERROR && error != GPG_ERR_EOF) {
387                 if (secret)
388                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not list secret keys for \"%s\""), name);
389                 else
390                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not list keys for \"%s\""), name);
391                 
392                 return NULL;
393         }
394         
395         if (!key) {
396                 if (strchr (name, '@')) {
397                         if (bad)
398                                 g_set_error (err, GMIME_GPGME_ERROR, errval,
399                                              _("A key for %s is present, but it is expired, disabled, revoked or invalid"),
400                                              name);
401                         else
402                                 g_set_error (err, GMIME_GPGME_ERROR, GPG_ERR_NOT_FOUND,
403                                              _("Could not find a key for %s"), name);
404                 } else {
405                         if (bad)
406                                 g_set_error (err, GMIME_GPGME_ERROR, errval,
407                                              _("A key with id %s is present, but it is expired, disabled, revoked or invalid"),
408                                              name);
409                         else
410                                 g_set_error (err, GMIME_GPGME_ERROR, GPG_ERR_NOT_FOUND,
411                                              _("Could not find a key with id %s"), name);
412                 }
413                 
414                 return NULL;
415         }
416         
417         return key;
418 }
419
420 static gboolean
421 pkcs7_add_signer (Pkcs7Ctx *pkcs7, const char *signer, GError **err)
422 {
423         gpgme_key_t key = NULL;
424         
425         if (!(key = pkcs7_get_key_by_name (pkcs7, signer, TRUE, err)))
426                 return FALSE;
427         
428         /* set the key (the previous operation guaranteed that it exists, no need
429          * 2 check return values...) */
430         gpgme_signers_add (pkcs7->ctx, key);
431         gpgme_key_unref (key);
432         
433         return TRUE;
434 }
435 #endif /* ENABLE_SMIME */
436
437 static int
438 pkcs7_sign (GMimeCryptoContext *context, const char *userid, GMimeDigestAlgo digest,
439             GMimeStream *istream, GMimeStream *ostream, GError **err)
440 {
441 #ifdef ENABLE_SMIME
442         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
443         Pkcs7Ctx *pkcs7 = ctx->priv;
444         gpgme_sign_result_t result;
445         gpgme_data_t input, output;
446         gpgme_error_t error;
447         
448         if (!pkcs7_add_signer (pkcs7, userid, err))
449                 return -1;
450         
451         gpgme_set_armor (pkcs7->ctx, FALSE);
452         
453         if ((error = gpgme_data_new_from_cbs (&input, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
454                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
455                 return -1;
456         }
457         
458         if ((error = gpgme_data_new_from_cbs (&output, &pkcs7_stream_funcs, ostream)) != GPG_ERR_NO_ERROR) {
459                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open output stream"));
460                 gpgme_data_release (input);
461                 return -1;
462         }
463         
464         /* sign the input stream */
465         if ((error = gpgme_op_sign (pkcs7->ctx, input, output, GPGME_SIG_MODE_DETACH)) != GPG_ERR_NO_ERROR) {
466                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Signing failed"));
467                 gpgme_data_release (output);
468                 gpgme_data_release (input);
469                 return -1;
470         }
471         
472         gpgme_data_release (output);
473         gpgme_data_release (input);
474         
475         /* return the digest algorithm used for signing */
476         result = gpgme_op_sign_result (pkcs7->ctx);
477         
478         return pkcs7_digest_id (context, gpgme_hash_algo_name (result->signatures->hash_algo));
479 #else
480         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
481         
482         return -1;
483 #endif /* ENABLE_SMIME */
484 }
485
486 #ifdef ENABLE_SMIME
487 static GMimeCertificateTrust
488 pkcs7_trust (gpgme_validity_t trust)
489 {
490         switch (trust) {
491         case GPGME_VALIDITY_UNKNOWN:
492         default:
493                 return GMIME_CERTIFICATE_TRUST_NONE;
494         case GPGME_VALIDITY_UNDEFINED:
495                 return GMIME_CERTIFICATE_TRUST_UNDEFINED;
496         case GPGME_VALIDITY_NEVER:
497                 return GMIME_CERTIFICATE_TRUST_NEVER;
498         case GPGME_VALIDITY_MARGINAL:
499                 return GMIME_CERTIFICATE_TRUST_MARGINAL;
500         case GPGME_VALIDITY_FULL:
501                 return GMIME_CERTIFICATE_TRUST_FULLY;
502         case GPGME_VALIDITY_ULTIMATE:
503                 return GMIME_CERTIFICATE_TRUST_ULTIMATE;
504         }
505 }
506
507 static GMimeSignatureList *
508 pkcs7_get_signatures (Pkcs7Ctx *pkcs7, gboolean verify)
509 {
510         GMimeSignatureList *signatures;
511         GMimeSignature *signature;
512         gpgme_verify_result_t result;
513         gpgme_subkey_t subkey;
514         gpgme_signature_t sig;
515         gpgme_user_id_t uid;
516         gpgme_key_t key;
517         
518         /* get the signature verification results from GpgMe */
519         if (!(result = gpgme_op_verify_result (pkcs7->ctx)) || !result->signatures)
520                 return verify ? g_mime_signature_list_new () : NULL;
521         
522         /* create a new signature list to return */
523         signatures = g_mime_signature_list_new ();
524         
525         sig = result->signatures;
526         
527         while (sig != NULL) {
528                 signature = g_mime_signature_new ();
529                 g_mime_signature_list_add (signatures, signature);
530                 
531                 if (sig->status != GPG_ERR_NO_ERROR)
532                         g_mime_signature_set_status (signature, GMIME_SIGNATURE_STATUS_ERROR);
533                 else
534                         g_mime_signature_set_status (signature, GMIME_SIGNATURE_STATUS_GOOD);
535                 
536                 g_mime_certificate_set_pubkey_algo (signature->cert, sig->pubkey_algo);
537                 g_mime_certificate_set_digest_algo (signature->cert, sig->hash_algo);
538                 g_mime_certificate_set_fingerprint (signature->cert, sig->fpr);
539                 g_mime_signature_set_expires (signature, sig->exp_timestamp);
540                 g_mime_signature_set_created (signature, sig->timestamp);
541                 
542                 if (sig->exp_timestamp != 0 && sig->exp_timestamp <= time (NULL)) {
543                         /* signature expired, automatically results in a BAD signature */
544                         signature->errors |= GMIME_SIGNATURE_ERROR_EXPSIG;
545                         signature->status = GMIME_SIGNATURE_STATUS_BAD;
546                 }
547                 
548                 if (gpgme_get_key (pkcs7->ctx, sig->fpr, &key, 0) == GPG_ERR_NO_ERROR && key) {
549                         /* get more signer info from their signing key */
550                         g_mime_certificate_set_trust (signature->cert, pkcs7_trust (key->owner_trust));
551                         g_mime_certificate_set_issuer_serial (signature->cert, key->issuer_serial);
552                         g_mime_certificate_set_issuer_name (signature->cert, key->issuer_name);
553                         
554                         /* get the keyid, name, and email address */
555                         uid = key->uids;
556                         while (uid) {
557                                 if (uid->name && *uid->name)
558                                         g_mime_certificate_set_name (signature->cert, uid->name);
559                                 
560                                 if (uid->email && *uid->email)
561                                         g_mime_certificate_set_email (signature->cert, uid->email);
562                                 
563                                 if (uid->uid && *uid->uid)
564                                         g_mime_certificate_set_key_id (signature->cert, uid->uid);
565                                 
566                                 if (signature->cert->name && signature->cert->email && signature->cert->keyid)
567                                         break;
568                                 
569                                 uid = uid->next;
570                         }
571                         
572                         /* get the subkey used for signing */
573                         subkey = key->subkeys;
574                         while (subkey && !subkey->can_sign)
575                                 subkey = subkey->next;
576                         
577                         if (subkey) {
578                                 g_mime_certificate_set_created (signature->cert, subkey->timestamp);
579                                 g_mime_certificate_set_expires (signature->cert, subkey->expires);
580                                 
581                                 if (subkey->revoked) {
582                                         /* signer's key has been revoked, automatic BAD status */
583                                         signature->errors |= GMIME_SIGNATURE_ERROR_REVKEYSIG;
584                                         signature->status = GMIME_SIGNATURE_STATUS_BAD;
585                                 }
586                                 
587                                 if (subkey->expired) {
588                                         /* signer's key has expired, automatic BAD status */
589                                         signature->errors |= GMIME_SIGNATURE_ERROR_EXPKEYSIG;
590                                         signature->status = GMIME_SIGNATURE_STATUS_BAD;
591                                 }
592                         } else {
593                                 /* If we don't have the subkey used by the signer, then we can't
594                                  * tell what the status is, so set to ERROR if it hasn't already
595                                  * been designated as BAD. */
596                                 if (signature->status != GMIME_SIGNATURE_STATUS_BAD)
597                                         signature->status = GMIME_SIGNATURE_STATUS_ERROR;
598                                 signature->errors |= GMIME_SIGNATURE_ERROR_NO_PUBKEY;
599                         }
600                         
601                         gpgme_key_unref (key);
602                 } else {
603                         /* If we don't have the signer's public key, then we can't tell what
604                          * the status is, so set it to ERROR if it hasn't already been
605                          * designated as BAD. */
606                         g_mime_certificate_set_trust (signature->cert, GMIME_CERTIFICATE_TRUST_UNDEFINED);
607                         if (signature->status != GMIME_SIGNATURE_STATUS_BAD)
608                                 signature->status = GMIME_SIGNATURE_STATUS_ERROR;
609                         signature->errors |= GMIME_SIGNATURE_ERROR_NO_PUBKEY;
610                 }
611                 
612                 sig = sig->next;
613         }
614         
615         return signatures;
616 }
617 #endif /* ENABLE_SMIME */
618
619 static GMimeSignatureList *
620 pkcs7_verify (GMimeCryptoContext *context, GMimeDigestAlgo digest,
621               GMimeStream *istream, GMimeStream *sigstream,
622               GError **err)
623 {
624 #ifdef ENABLE_SMIME
625         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
626         gpgme_data_t message, signature;
627         Pkcs7Ctx *pkcs7 = ctx->priv;
628         gpgme_error_t error;
629         
630         if ((error = gpgme_data_new_from_cbs (&message, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
631                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
632                 return NULL;
633         }
634         
635         /* if @sigstream is non-NULL, then it is a detached signature */
636         if (sigstream != NULL) {
637                 if ((error = gpgme_data_new_from_cbs (&signature, &pkcs7_stream_funcs, sigstream)) != GPG_ERR_NO_ERROR) {
638                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open signature stream"));
639                         gpgme_data_release (message);
640                         return NULL;
641                 }
642         } else {
643                 signature = NULL;
644         }
645         
646         if ((error = gpgme_op_verify (pkcs7->ctx, signature, message, NULL)) != GPG_ERR_NO_ERROR) {
647                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not verify pkcs7 signature"));
648                 if (signature)
649                         gpgme_data_release (signature);
650                 gpgme_data_release (message);
651                 return NULL;
652         }
653         
654         if (signature)
655                 gpgme_data_release (signature);
656         
657         if (message)
658                 gpgme_data_release (message);
659         
660         /* get/return the pkcs7 signatures */
661         return pkcs7_get_signatures (pkcs7, TRUE);
662 #else
663         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
664         
665         return NULL;
666 #endif /* ENABLE_SMIME */
667 }
668
669 #ifdef ENABLE_SMIME
670 static void
671 key_list_free (gpgme_key_t *keys)
672 {
673         gpgme_key_t *key = keys;
674         
675         while (key != NULL) {
676                 gpgme_key_unref (*key);
677                 key++;
678         }
679         
680         g_free (keys);
681 }
682 #endif /* ENABLE_SMIME */
683
684 static int
685 pkcs7_encrypt (GMimeCryptoContext *context, gboolean sign, const char *userid,
686                GMimeDigestAlgo digest, GPtrArray *recipients, GMimeStream *istream,
687                GMimeStream *ostream, GError **err)
688 {
689 #ifdef ENABLE_SMIME
690         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
691         Pkcs7Ctx *pkcs7 = ctx->priv;
692         gpgme_data_t input, output;
693         gpgme_error_t error;
694         gpgme_key_t *rcpts;
695         gpgme_key_t key;
696         guint i;
697         
698         if (sign) {
699                 g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED,
700                              _("Cannot sign and encrypt a stream at the same time using pkcs7"));
701                 return -1;
702         }
703         
704         /* create an array of recipient keys for GpgMe */
705         rcpts = g_new0 (gpgme_key_t, recipients->len + 1);
706         for (i = 0; i < recipients->len; i++) {
707                 if (!(key = pkcs7_get_key_by_name (pkcs7, recipients->pdata[i], FALSE, err))) {
708                         key_list_free (rcpts);
709                         return -1;
710                 }
711                 
712                 rcpts[i] = key;
713         }
714         
715         if ((error = gpgme_data_new_from_cbs (&input, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
716                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
717                 key_list_free (rcpts);
718                 return -1;
719         }
720         
721         if ((error = gpgme_data_new_from_cbs (&output, &pkcs7_stream_funcs, ostream)) != GPG_ERR_NO_ERROR) {
722                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open output stream"));
723                 gpgme_data_release (input);
724                 key_list_free (rcpts);
725                 return -1;
726         }
727         
728         /* encrypt the input stream */
729         error = gpgme_op_encrypt (pkcs7->ctx, rcpts, GPGME_ENCRYPT_ALWAYS_TRUST, input, output);
730         gpgme_data_release (output);
731         gpgme_data_release (input);
732         key_list_free (rcpts);
733         
734         if (error != GPG_ERR_NO_ERROR) {
735                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Encryption failed"));
736                 return -1;
737         }
738         
739         return 0;
740 #else
741         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
742         
743         return -1;
744 #endif /* ENABLE_SMIME */
745 }
746
747 #ifdef ENABLE_SMIME
748 static GMimeDecryptResult *
749 pkcs7_get_decrypt_result (Pkcs7Ctx *pkcs7)
750 {
751         GMimeDecryptResult *result;
752         gpgme_decrypt_result_t res;
753         gpgme_recipient_t recipient;
754         GMimeCertificate *cert;
755         
756         result = g_mime_decrypt_result_new ();
757         result->recipients = g_mime_certificate_list_new ();
758         result->signatures = pkcs7_get_signatures (pkcs7, FALSE);
759         
760         if (!(res = gpgme_op_decrypt_result (pkcs7->ctx)) || !res->recipients)
761                 return result;
762         
763         recipient = res->recipients;
764         while (recipient != NULL) {
765                 cert = g_mime_certificate_new ();
766                 g_mime_certificate_list_add (result->recipients, cert);
767                 
768                 g_mime_certificate_set_pubkey_algo (cert, recipient->pubkey_algo);
769                 g_mime_certificate_set_key_id (cert, recipient->keyid);
770                 
771                 recipient = recipient->next;
772         }
773         
774         return result;
775 }
776 #endif /* ENABLE_SMIME */
777
778 static GMimeDecryptResult *
779 pkcs7_decrypt (GMimeCryptoContext *context, GMimeStream *istream,
780                GMimeStream *ostream, GError **err)
781 {
782 #ifdef ENABLE_SMIME
783         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
784         GMimeDecryptResult *result;
785         Pkcs7Ctx *pkcs7 = ctx->priv;
786         gpgme_decrypt_result_t res;
787         gpgme_data_t input, output;
788         gpgme_error_t error;
789         
790         if ((error = gpgme_data_new_from_cbs (&input, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
791                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
792                 return NULL;
793         }
794         
795         if ((error = gpgme_data_new_from_cbs (&output, &pkcs7_stream_funcs, ostream)) != GPG_ERR_NO_ERROR) {
796                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open output stream"));
797                 gpgme_data_release (input);
798                 return NULL;
799         }
800         
801         /* decrypt the input stream */
802         if ((error = gpgme_op_decrypt_verify (pkcs7->ctx, input, output)) != GPG_ERR_NO_ERROR) {
803                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Decryption failed"));
804                 gpgme_data_release (output);
805                 gpgme_data_release (input);
806                 return NULL;
807         }
808         
809         gpgme_data_release (output);
810         gpgme_data_release (input);
811         
812         return pkcs7_get_decrypt_result (pkcs7);
813 #else
814         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
815         
816         return NULL;
817 #endif /* ENABLE_SMIME */
818 }
819
820 static int
821 pkcs7_import_keys (GMimeCryptoContext *context, GMimeStream *istream, GError **err)
822 {
823 #ifdef ENABLE_SMIME
824         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
825         Pkcs7Ctx *pkcs7 = ctx->priv;
826         gpgme_data_t keydata;
827         gpgme_error_t error;
828         
829         if ((error = gpgme_data_new_from_cbs (&keydata, &pkcs7_stream_funcs, istream)) != GPG_ERR_NO_ERROR) {
830                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open input stream"));
831                 return -1;
832         }
833         
834         /* import the key(s) */
835         if ((error = gpgme_op_import (pkcs7->ctx, keydata)) != GPG_ERR_NO_ERROR) {
836                 //printf ("import error (%d): %s\n", error & GPG_ERR_CODE_MASK, gpg_strerror (error));
837                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not import key data"));
838                 gpgme_data_release (keydata);
839                 return -1;
840         }
841         
842         gpgme_data_release (keydata);
843         
844         return 0;
845 #else
846         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
847         
848         return -1;
849 #endif /* ENABLE_SMIME */
850 }
851
852 static int
853 pkcs7_export_keys (GMimeCryptoContext *context, GPtrArray *keys, GMimeStream *ostream, GError **err)
854 {
855 #ifdef ENABLE_SMIME
856         GMimePkcs7Context *ctx = (GMimePkcs7Context *) context;
857         Pkcs7Ctx *pkcs7 = ctx->priv;
858         gpgme_data_t keydata;
859         gpgme_error_t error;
860         guint i;
861         
862         if ((error = gpgme_data_new_from_cbs (&keydata, &pkcs7_stream_funcs, ostream)) != GPG_ERR_NO_ERROR) {
863                 g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not open output stream"));
864                 return -1;
865         }
866         
867         /* export the key(s) */
868         for (i = 0; i < keys->len; i++) {
869                 if ((error = gpgme_op_export (pkcs7->ctx, keys->pdata[i], 0, keydata)) != GPG_ERR_NO_ERROR) {
870                         g_set_error (err, GMIME_GPGME_ERROR, error, _("Could not export key data"));
871                         gpgme_data_release (keydata);
872                         return -1;
873                 }
874         }
875         
876         gpgme_data_release (keydata);
877         
878         return 0;
879 #else
880         g_set_error (err, GMIME_ERROR, GMIME_ERROR_NOT_SUPPORTED, _("S/MIME support is not enabled in this build"));
881         
882         return -1;
883 #endif /* ENABLE_SMIME */
884 }
885
886
887 /**
888  * g_mime_pkcs7_context_new:
889  * @request_passwd: a #GMimePasswordRequestFunc
890  *
891  * Creates a new pkcs7 crypto context object.
892  *
893  * Returns: a new pkcs7 crypto context object.
894  **/
895 GMimeCryptoContext *
896 g_mime_pkcs7_context_new (GMimePasswordRequestFunc request_passwd)
897 {
898 #ifdef ENABLE_SMIME
899         GMimeCryptoContext *crypto;
900         GMimePkcs7Context *pkcs7;
901         gpgme_ctx_t ctx;
902         
903         /* make sure GpgMe supports the CMS protocols */
904         if (gpgme_engine_check_version (GPGME_PROTOCOL_CMS) != GPG_ERR_NO_ERROR)
905                 return NULL;
906         
907         /* create the GpgMe context */
908         if (gpgme_new (&ctx) != GPG_ERR_NO_ERROR)
909                 return NULL;
910         
911         pkcs7 = g_object_newv (GMIME_TYPE_PKCS7_CONTEXT, 0, NULL);
912         gpgme_set_passphrase_cb (ctx, pkcs7_passphrase_cb, pkcs7);
913         gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
914         pkcs7->priv->ctx = ctx;
915         
916         crypto = (GMimeCryptoContext *) pkcs7;
917         crypto->request_passwd = request_passwd;
918         
919         return crypto;
920 #else
921         return NULL;
922 #endif /* ENABLE_SMIME */
923 }
924
925
926 /**
927  * g_mime_pkcs7_context_get_always_trust:
928  * @ctx: a #GMimePkcs7Context
929  *
930  * Gets the @always_trust flag on the pkcs7 context.
931  *
932  * Returns: the @always_trust flag on the pkcs7 context.
933  **/
934 gboolean
935 g_mime_pkcs7_context_get_always_trust (GMimePkcs7Context *ctx)
936 {
937         g_return_val_if_fail (GMIME_IS_PKCS7_CONTEXT (ctx), FALSE);
938         
939         return ctx->priv->always_trust;
940 }
941
942
943 /**
944  * g_mime_pkcs7_context_set_always_trust:
945  * @ctx: a #GMimePkcs7Context
946  * @always_trust: always trust flag
947  *
948  * Sets the @always_trust flag on the pkcs7 context which is used for
949  * encryption.
950  **/
951 void
952 g_mime_pkcs7_context_set_always_trust (GMimePkcs7Context *ctx, gboolean always_trust)
953 {
954         g_return_if_fail (GMIME_IS_PKCS7_CONTEXT (ctx));
955         
956         ctx->priv->always_trust = always_trust;
957 }