1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /*
3  * gmime/gpgme implementation for multipart/signed and multipart/encrypted
4  * Copyright (C) 2011 Albrecht Dreß <albrecht.dress@arcor.de>
5  *
6  * The functions in this module were copied from the original GMime
7  * implementation of multipart/signed and multipart/encrypted parts.
8  * However, instead of using the complex GMime crypto contexts (which have
9  * a varying API over the different versions), this module directly calls
10  * the GpgME backend functions implemented in libbalsa-gpgme.[hc].
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25  * 02111-1307, USA.
26  */
27 
28 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
29 #include "config.h"
30 #endif				/* HAVE_CONFIG_H */
31 
32 #include <string.h>
33 #include <unistd.h>
34 #include <glib/gi18n.h>
35 #include <glib.h>
36 #include <gtk/gtk.h>
37 #include "libbalsa-gpgme.h"
38 #include "gmime-multipart-crypt.h"
39 
40 
41 /**
42  * sign_prepare:
43  * @mime_part: MIME part
44  *
45  * Prepare a part (and all subparts) to be signed. To do this we need
46  * to set the encoding of all parts (that are not already encoded to
47  * either QP or Base64) to QP.
48  **/
49 static void
sign_prepare(GMimeObject * mime_part)50 sign_prepare(GMimeObject * mime_part)
51 {
52     GMimeContentEncoding encoding;
53     GMimeObject *subpart;
54 
55     if (GMIME_IS_MULTIPART(mime_part)) {
56         GMimeMultipart *multipart;
57         int i, n;
58 
59 	multipart = (GMimeMultipart *) mime_part;
60 
61 	if (GMIME_IS_MULTIPART_SIGNED(multipart) ||
62 	    GMIME_IS_MULTIPART_ENCRYPTED(multipart)) {
63 	    /* must not modify these parts as they must be treated as opaque */
64 	    return;
65 	}
66 
67 	n = g_mime_multipart_get_count(multipart);
68 	for (i = 0; i < n; i++) {
69 	    subpart = g_mime_multipart_get_part(multipart, i);
70 	    sign_prepare(subpart);
71 	}
72     } else if (GMIME_IS_MESSAGE_PART(mime_part)) {
73 	subpart = GMIME_MESSAGE_PART(mime_part)->message->mime_part;
74 	sign_prepare(subpart);
75     } else {
76 	encoding = g_mime_part_get_content_encoding(GMIME_PART(mime_part));
77 	if (encoding != GMIME_CONTENT_ENCODING_BASE64)
78 	    g_mime_part_set_content_encoding(GMIME_PART(mime_part),
79 					     GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE);
80     }
81 }
82 
83 
84 int
g_mime_gpgme_mps_sign(GMimeMultipartSigned * mps,GMimeObject * content,const gchar * userid,gpgme_protocol_t protocol,GtkWindow * parent,GError ** err)85 g_mime_gpgme_mps_sign(GMimeMultipartSigned * mps, GMimeObject * content,
86 		      const gchar * userid, gpgme_protocol_t protocol,
87 		      GtkWindow * parent, GError ** err)
88 {
89     GMimeStream *stream;
90     GMimeStream *filtered;
91     GMimeStream *sigstream;
92     GMimeFilter *filter;
93     GMimeContentType *content_type;
94     GMimeDataWrapper *wrapper;
95     GMimeParser *parser;
96     GMimePart *signature;
97     const gchar *sig_type;
98     gpgme_hash_algo_t hash_algo;
99 
100     g_return_val_if_fail(GMIME_IS_MULTIPART_SIGNED(mps), -1);
101     g_return_val_if_fail(GMIME_IS_OBJECT(content), -1);
102 
103     /* Prepare all the parts for signing... */
104     sign_prepare(content);
105 
106     /* get the cleartext */
107     stream = g_mime_stream_mem_new();
108     filtered = g_mime_stream_filter_new(stream);
109 
110     /* Note: see rfc3156, section 3 - second note */
111     filter = g_mime_filter_from_new(GMIME_FILTER_FROM_MODE_ARMOR);
112     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
113     g_object_unref(filter);
114 
115     /* Note: see rfc3156, section 5.4 (this is the main difference between rfc2015 and rfc3156) */
116     filter = g_mime_filter_strip_new();
117     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
118     g_object_unref(filter);
119 
120     g_mime_object_write_to_stream(content, filtered);
121     g_mime_stream_flush(filtered);
122     g_object_unref(filtered);
123     g_mime_stream_reset(stream);
124 
125     /* Note: see rfc2015 or rfc3156, section 5.1 */
126     filtered = g_mime_stream_filter_new(stream);
127     filter = g_mime_filter_crlf_new(TRUE, FALSE);
128     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered), filter);
129     g_object_unref(filter);
130 
131     /* construct the signature stream */
132     sigstream = g_mime_stream_mem_new();
133 
134     /* sign the content stream */
135     hash_algo =
136 	libbalsa_gpgme_sign(userid, filtered, sigstream, protocol, FALSE,
137 			    parent, err);
138     if (hash_algo == GPGME_MD_NONE) {
139 	g_object_unref(sigstream);
140 	g_object_unref(filtered);
141 	g_object_unref(stream);
142 	return -1;
143     }
144 
145     g_object_unref(filtered);
146     g_mime_stream_reset(sigstream);
147     g_mime_stream_reset(stream);
148 
149     /* set the multipart/signed protocol and micalg */
150     content_type = g_mime_object_get_content_type(GMIME_OBJECT(mps));
151     if (protocol == GPGME_PROTOCOL_OpenPGP) {
152 	gchar *micalg;
153 
154 	micalg =
155 	    g_strdup_printf("PGP-%s", gpgme_hash_algo_name(hash_algo));
156 	g_mime_content_type_set_parameter(content_type, "micalg", micalg);
157 	g_free(micalg);
158 	sig_type = "application/pgp-signature";
159     } else {
160 	g_mime_content_type_set_parameter(content_type, "micalg",
161 					  gpgme_hash_algo_name(hash_algo));
162 	sig_type = "application/pkcs7-signature";
163     }
164     g_mime_content_type_set_parameter(content_type, "protocol", sig_type);
165     g_mime_multipart_set_boundary(GMIME_MULTIPART(mps), NULL);
166 
167     /* construct the content part */
168     parser = g_mime_parser_new_with_stream(stream);
169     content = g_mime_parser_construct_part(parser);
170     g_object_unref(stream);
171     g_object_unref(parser);
172 
173     /* construct the signature part */
174     content_type = g_mime_content_type_new_from_string(sig_type);
175     signature =
176 	g_mime_part_new_with_type(content_type->type,
177 				  content_type->subtype);
178     g_object_unref(content_type);
179 
180     wrapper = g_mime_data_wrapper_new();
181     g_mime_data_wrapper_set_stream(wrapper, sigstream);
182     g_mime_part_set_content_object(signature, wrapper);
183     g_object_unref(sigstream);
184     g_object_unref(wrapper);
185 
186     /* FIXME: temporary hack, this info should probably be set in
187      * the CipherContext class - maybe ::sign can take/output a
188      * GMimePart instead. */
189     if (protocol == GPGME_PROTOCOL_CMS) {
190 	g_mime_part_set_content_encoding(signature,
191 					 GMIME_CONTENT_ENCODING_BASE64);
192 	g_mime_part_set_filename(signature, "smime.p7m");
193     }
194 
195     /* save the content and signature parts */
196     /* FIXME: make sure there aren't any other parts?? */
197     g_mime_multipart_add(GMIME_MULTIPART(mps), content);
198     g_mime_multipart_add(GMIME_MULTIPART(mps), (GMimeObject *) signature);
199     g_object_unref(signature);
200     g_object_unref(content);
201 
202     return 0;
203 }
204 
205 
206 GMimeGpgmeSigstat *
g_mime_gpgme_mps_verify(GMimeMultipartSigned * mps,GError ** error)207 g_mime_gpgme_mps_verify(GMimeMultipartSigned * mps, GError ** error)
208 {
209     const gchar *protocol;
210     gpgme_protocol_t crypto_prot;
211     gchar *content_type;
212     GMimeObject *content;
213     GMimeObject *signature;
214     GMimeStream *stream;
215     GMimeStream *filtered_stream;
216     GMimeFilter *crlf_filter;
217     GMimeDataWrapper *wrapper;
218     GMimeStream *sigstream;
219     GMimeGpgmeSigstat *result;
220 
221     g_return_val_if_fail(GMIME_IS_MULTIPART_SIGNED(mps), NULL);
222 
223     if (g_mime_multipart_get_count((GMimeMultipart *) mps) < 2) {
224 	g_set_error(error, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
225 		    _
226 		    ("Cannot verify multipart/signed part due to missing subparts."));
227 	return NULL;
228     }
229 
230     /* grab the protocol so we can configure the GpgME context */
231     protocol =
232 	g_mime_object_get_content_type_parameter(GMIME_OBJECT(mps),
233 						 "protocol");
234     if (protocol) {
235 	if (g_ascii_strcasecmp("application/pgp-signature", protocol) == 0)
236 	    crypto_prot = GPGME_PROTOCOL_OpenPGP;
237 #if defined(HAVE_SMIME)
238 	else if (g_ascii_strcasecmp
239 		 ("application/pkcs7-signature", protocol) == 0
240 		 || g_ascii_strcasecmp("application/x-pkcs7-signature",
241 				       protocol) == 0)
242 	    crypto_prot = GPGME_PROTOCOL_CMS;
243 #endif
244 	else
245 	    crypto_prot = GPGME_PROTOCOL_UNKNOWN;
246     } else
247 	crypto_prot = GPGME_PROTOCOL_UNKNOWN;
248 
249     /* eject on unknown protocols */
250     if (crypto_prot == GPGME_PROTOCOL_UNKNOWN) {
251 	g_set_error(error, GPGME_ERROR_QUARK, GPG_ERR_INV_VALUE,
252 		    _("unsupported protocol '%s'"), protocol);
253 	return NULL;
254     }
255 
256     signature =
257 	g_mime_multipart_get_part(GMIME_MULTIPART(mps),
258 				  GMIME_MULTIPART_SIGNED_SIGNATURE);
259 
260     /* make sure the protocol matches the signature content-type */
261     content_type = g_mime_content_type_to_string(signature->content_type);
262     if (g_ascii_strcasecmp(content_type, protocol) != 0) {
263 	g_set_error(error, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
264 		    _
265 		    ("Cannot verify multipart/signed part: signature content-type does not match protocol."));
266 	g_free(content_type);
267 	return NULL;
268     }
269     g_free(content_type);
270 
271     content =
272 	g_mime_multipart_get_part(GMIME_MULTIPART(mps),
273 				  GMIME_MULTIPART_SIGNED_CONTENT);
274 
275     /* get the content stream */
276     stream = g_mime_stream_mem_new();
277     filtered_stream = g_mime_stream_filter_new(stream);
278 
279     /* Note: see rfc2015 or rfc3156, section 5.1 */
280     crlf_filter = g_mime_filter_crlf_new(TRUE, FALSE);
281     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
282 			     crlf_filter);
283     g_object_unref(crlf_filter);
284 
285     g_mime_object_write_to_stream(content, filtered_stream);
286     g_mime_stream_flush(filtered_stream);
287     g_object_unref(filtered_stream);
288     g_mime_stream_reset(stream);
289 
290     /* get the signature stream */
291     wrapper = g_mime_part_get_content_object(GMIME_PART(signature));
292 
293     /* FIXME: temporary hack for Balsa to support S/MIME,
294      * ::verify() should probably take a mime part so it can
295      * decode this itself if it needs to. */
296     if (crypto_prot == GPGME_PROTOCOL_CMS) {
297 	sigstream = g_mime_stream_mem_new();
298 	g_mime_data_wrapper_write_to_stream(wrapper, sigstream);
299     } else {
300 	sigstream = g_mime_data_wrapper_get_stream(wrapper);
301     }
302 
303     g_mime_stream_reset(sigstream);
304 
305     /* verify the signature */
306     result =
307 	libbalsa_gpgme_verify(stream, sigstream, crypto_prot, FALSE,
308 			      error);
309     g_object_unref(stream);
310 
311     return result;
312 }
313 
314 
315 int
g_mime_gpgme_mpe_encrypt(GMimeMultipartEncrypted * mpe,GMimeObject * content,GPtrArray * recipients,gboolean trust_all,GtkWindow * parent,GError ** err)316 g_mime_gpgme_mpe_encrypt(GMimeMultipartEncrypted * mpe,
317 			 GMimeObject * content, GPtrArray * recipients,
318 			 gboolean trust_all, GtkWindow * parent,
319 			 GError ** err)
320 {
321     GMimeStream *filtered_stream;
322     GMimeStream *ciphertext;
323     GMimeStream *stream;
324     GMimePart *version_part;
325     GMimePart *encrypted_part;
326     GMimeContentType *content_type;
327     GMimeDataWrapper *wrapper;
328     GMimeFilter *crlf_filter;
329 
330     g_return_val_if_fail(GMIME_IS_MULTIPART_ENCRYPTED(mpe), -1);
331     g_return_val_if_fail(GMIME_IS_OBJECT(content), -1);
332 
333     /* get the cleartext */
334     stream = g_mime_stream_mem_new();
335     filtered_stream = g_mime_stream_filter_new(stream);
336 
337     crlf_filter = g_mime_filter_crlf_new(TRUE, FALSE);
338     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
339 			     crlf_filter);
340     g_object_unref(crlf_filter);
341 
342     g_mime_object_write_to_stream(content, filtered_stream);
343     g_mime_stream_flush(filtered_stream);
344     g_object_unref(filtered_stream);
345 
346     /* reset the content stream */
347     g_mime_stream_reset(stream);
348 
349     /* encrypt the content stream */
350     ciphertext = g_mime_stream_mem_new();
351     if (libbalsa_gpgme_encrypt
352 	(recipients, NULL, stream, ciphertext, GPGME_PROTOCOL_OpenPGP,
353 	 FALSE, trust_all, parent, err) == -1) {
354 	g_object_unref(ciphertext);
355 	g_object_unref(stream);
356 	return -1;
357     }
358 
359     g_object_unref(stream);
360     g_mime_stream_reset(ciphertext);
361 
362     /* construct the version part */
363     content_type =
364 	g_mime_content_type_new_from_string("application/pgp-encrypted");
365     version_part =
366 	g_mime_part_new_with_type(content_type->type,
367 				  content_type->subtype);
368     g_object_unref(content_type);
369 
370     content_type =
371 	g_mime_content_type_new_from_string("application/pgp-encrypted");
372     g_mime_object_set_content_type(GMIME_OBJECT(version_part),
373 				   content_type);
374     g_mime_part_set_content_encoding(version_part,
375 				     GMIME_CONTENT_ENCODING_7BIT);
376     stream =
377 	g_mime_stream_mem_new_with_buffer("Version: 1\n",
378 					  strlen("Version: 1\n"));
379     wrapper =
380 	g_mime_data_wrapper_new_with_stream(stream,
381 					    GMIME_CONTENT_ENCODING_7BIT);
382     g_mime_part_set_content_object(version_part, wrapper);
383     g_object_unref(wrapper);
384     g_object_unref(stream);
385 
386 #if !defined(HAVE_GMIME_2_6)
387     mpe->decrypted = content;
388     g_object_ref(content);
389 #endif
390 
391     /* construct the encrypted mime part */
392     encrypted_part =
393 	g_mime_part_new_with_type("application", "octet-stream");
394     g_mime_part_set_content_encoding(encrypted_part,
395 				     GMIME_CONTENT_ENCODING_7BIT);
396     wrapper =
397 	g_mime_data_wrapper_new_with_stream(ciphertext,
398 					    GMIME_CONTENT_ENCODING_7BIT);
399     g_mime_part_set_content_object(encrypted_part, wrapper);
400     g_object_unref(ciphertext);
401     g_object_unref(wrapper);
402 
403     /* save the version and encrypted parts */
404     /* FIXME: make sure there aren't any other parts?? */
405     g_mime_multipart_add(GMIME_MULTIPART(mpe), GMIME_OBJECT(version_part));
406     g_mime_multipart_add(GMIME_MULTIPART(mpe),
407 			 GMIME_OBJECT(encrypted_part));
408     g_object_unref(encrypted_part);
409     g_object_unref(version_part);
410 
411     /* set the content-type params for this multipart/encrypted part */
412     g_mime_object_set_content_type_parameter(GMIME_OBJECT(mpe), "protocol",
413 					     "application/pgp-encrypted");
414     g_mime_multipart_set_boundary(GMIME_MULTIPART(mpe), NULL);
415 
416     return 0;
417 }
418 
419 
420 static GMimeStream *
g_mime_data_wrapper_get_decoded_stream(GMimeDataWrapper * wrapper)421 g_mime_data_wrapper_get_decoded_stream(GMimeDataWrapper * wrapper)
422 {
423     GMimeStream *decoded_stream;
424     GMimeFilter *decoder;
425 
426     g_mime_stream_reset(wrapper->stream);
427 
428     switch (wrapper->encoding) {
429     case GMIME_CONTENT_ENCODING_BASE64:
430     case GMIME_CONTENT_ENCODING_QUOTEDPRINTABLE:
431     case GMIME_CONTENT_ENCODING_UUENCODE:
432 	decoder = g_mime_filter_basic_new(wrapper->encoding, FALSE);
433 	decoded_stream = g_mime_stream_filter_new(wrapper->stream);
434 	g_mime_stream_filter_add(GMIME_STREAM_FILTER(decoded_stream),
435 				 decoder);
436 	g_object_unref(decoder);
437 	break;
438     default:
439 	decoded_stream = wrapper->stream;
440 	g_object_ref(wrapper->stream);
441 	break;
442     }
443 
444     return decoded_stream;
445 }
446 
447 
448 GMimeObject *
g_mime_gpgme_mpe_decrypt(GMimeMultipartEncrypted * mpe,GMimeGpgmeSigstat ** signature,GtkWindow * parent,GError ** err)449 g_mime_gpgme_mpe_decrypt(GMimeMultipartEncrypted * mpe,
450 			 GMimeGpgmeSigstat ** signature,
451 			 GtkWindow * parent, GError ** err)
452 {
453     GMimeObject *decrypted, *version, *encrypted;
454     GMimeStream *stream, *ciphertext;
455     GMimeStream *filtered_stream;
456     GMimeContentType *mime_type;
457     GMimeGpgmeSigstat *sigstat;
458     GMimeDataWrapper *wrapper;
459     GMimeFilter *crlf_filter;
460     GMimeParser *parser;
461     const char *protocol;
462     char *content_type;
463 
464     g_return_val_if_fail(GMIME_IS_MULTIPART_ENCRYPTED(mpe), NULL);
465 
466 #if !defined(HAVE_GMIME_2_6)
467     if (mpe->decrypted) {
468 	/* we seem to have already decrypted the part */
469 	return mpe->decrypted;
470     }
471 #endif
472 
473     if (signature && *signature) {
474 	g_object_unref(G_OBJECT(*signature));
475 	*signature = NULL;
476     }
477 
478     protocol =
479 	g_mime_object_get_content_type_parameter(GMIME_OBJECT(mpe),
480 						 "protocol");
481 
482     /* make sure the protocol is present and matches the cipher encrypt protocol */
483     if (!protocol
484 	|| g_ascii_strcasecmp("application/pgp-encrypted",
485 			      protocol) != 0) {
486 	g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR,
487 		    _
488 		    ("Cannot decrypt multipart/encrypted part: unsupported encryption protocol '%s'."),
489 		    protocol ? protocol : _("(none)"));
490 	return NULL;
491     }
492 
493     version =
494 	g_mime_multipart_get_part(GMIME_MULTIPART(mpe),
495 				  GMIME_MULTIPART_ENCRYPTED_VERSION);
496 
497     /* make sure the protocol matches the version part's content-type */
498     content_type = g_mime_content_type_to_string(version->content_type);
499     if (g_ascii_strcasecmp(content_type, protocol) != 0) {
500 	g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR, "%s",
501 		    _
502 		    ("Cannot decrypt multipart/encrypted part: content-type does not match protocol."));
503 	g_free(content_type);
504 	return NULL;
505     }
506     g_free(content_type);
507 
508     /* get the encrypted part and check that it is of type application/octet-stream */
509     encrypted =
510 	g_mime_multipart_get_part(GMIME_MULTIPART(mpe),
511 				  GMIME_MULTIPART_ENCRYPTED_CONTENT);
512     mime_type = g_mime_object_get_content_type(encrypted);
513     if (!g_mime_content_type_is_type
514 	(mime_type, "application", "octet-stream")) {
515 	g_set_error(err, GMIME_ERROR, GMIME_ERROR_PROTOCOL_ERROR, "%s",
516 		    _
517 		    ("Cannot decrypt multipart/encrypted part: unexpected content type"));
518 	return NULL;
519     }
520 
521     /* get the ciphertext stream */
522     wrapper = g_mime_part_get_content_object(GMIME_PART(encrypted));
523     ciphertext = g_mime_data_wrapper_get_decoded_stream(wrapper);
524     g_mime_stream_reset(ciphertext);
525 
526     stream = g_mime_stream_mem_new();
527     filtered_stream = g_mime_stream_filter_new(stream);
528     crlf_filter = g_mime_filter_crlf_new(FALSE, FALSE);
529     g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered_stream),
530 			     crlf_filter);
531     g_object_unref(crlf_filter);
532 
533     /* get the cleartext */
534     sigstat =
535 	libbalsa_gpgme_decrypt(ciphertext, filtered_stream,
536 			       GPGME_PROTOCOL_OpenPGP, parent, err);
537     if (!sigstat) {
538 	g_object_unref(filtered_stream);
539 	g_object_unref(ciphertext);
540 	g_object_unref(stream);
541 	return NULL;
542     }
543 
544     g_mime_stream_flush(filtered_stream);
545     g_object_unref(filtered_stream);
546     g_object_unref(ciphertext);
547 
548     g_mime_stream_reset(stream);
549     parser = g_mime_parser_new();
550     g_mime_parser_init_with_stream(parser, stream);
551     g_object_unref(stream);
552 
553     decrypted = g_mime_parser_construct_part(parser);
554     g_object_unref(parser);
555 
556     if (!decrypted) {
557 	g_set_error(err, GMIME_ERROR, GMIME_ERROR_PARSE_ERROR, "%s",
558 		    _
559 		    ("Cannot decrypt multipart/encrypted part: failed to parse decrypted content"));
560 	g_object_unref(G_OBJECT(sigstat));
561 	return NULL;
562     }
563 
564 
565     /* cache the decrypted part */
566 #if !defined(HAVE_GMIME_2_6)
567     mpe->decrypted = decrypted;
568 #endif
569     if (signature) {
570 	if (sigstat->status != GPG_ERR_NOT_SIGNED)
571 	    *signature = sigstat;
572 	else
573 	    g_object_unref(G_OBJECT(sigstat));
574     }
575 
576     return decrypted;
577 }
578