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