1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Jeffrey Stedfast <fejj@ximian.com>
18 */
19
20 /* Debug states:
21 * gpg:sign dump canonicalised to-be-signed data to a file
22 * gpg:verify dump canonicalised verification and signature data to file
23 * gpg:status print gpg status-fd output to stdout
24 */
25
26 #include "evolution-data-server-config.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #include <signal.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <ctype.h>
39
40 #include <glib.h>
41 #include <glib/gi18n-lib.h>
42 #include <glib/gstdio.h>
43
44 #ifndef G_OS_WIN32
45 #include <sys/ioctl.h>
46 #include <sys/wait.h>
47 #include <termios.h>
48 #endif
49
50 #include "camel-debug.h"
51 #include "camel-file-utils.h"
52 #include "camel-gpg-context.h"
53 #include "camel-iconv.h"
54 #include "camel-internet-address.h"
55 #include "camel-mime-filter-canon.h"
56 #include "camel-mime-filter-charset.h"
57 #include "camel-mime-part.h"
58 #include "camel-multipart-encrypted.h"
59 #include "camel-multipart-signed.h"
60 #include "camel-operation.h"
61 #include "camel-session.h"
62 #include "camel-stream-filter.h"
63 #include "camel-stream-fs.h"
64 #include "camel-stream-mem.h"
65 #include "camel-stream-null.h"
66 #include "camel-string-utils.h"
67
68 #define d(x)
69
70 #ifdef GPG_LOG
71 static gint logid;
72 #endif
73
74 #define CHECK_CALL(x) G_STMT_START { \
75 if ((x) == -1) { \
76 g_debug ("%s: Call of '" #x "' failed: %s", G_STRFUNC, g_strerror (errno)); \
77 } \
78 } G_STMT_END
79
80 struct _CamelGpgContextPrivate {
81 gboolean always_trust;
82 gboolean prefer_inline;
83 };
84
85 enum {
86 PROP_0,
87 PROP_ALWAYS_TRUST,
88 PROP_PREFER_INLINE
89 };
90
91 G_DEFINE_TYPE_WITH_PRIVATE (CamelGpgContext, camel_gpg_context, CAMEL_TYPE_CIPHER_CONTEXT)
92
93 static const gchar *gpg_ctx_get_executable_name (void);
94
95 typedef struct _GpgRecipientsData {
96 gchar *keyid;
97 gchar *known_key_data;
98 } GpgRecipientsData;
99
100 static void
gpg_recipients_data_free(gpointer ptr)101 gpg_recipients_data_free (gpointer ptr)
102 {
103 GpgRecipientsData *rd = ptr;
104
105 if (rd) {
106 g_free (rd->keyid);
107 g_free (rd->known_key_data);
108 g_slice_free (GpgRecipientsData, rd);
109 }
110 }
111
112 enum _GpgCtxMode {
113 GPG_CTX_MODE_SIGN,
114 GPG_CTX_MODE_VERIFY,
115 GPG_CTX_MODE_ENCRYPT,
116 GPG_CTX_MODE_DECRYPT
117 };
118
119 enum _GpgTrustMetric {
120 GPG_TRUST_NONE,
121 GPG_TRUST_NEVER,
122 GPG_TRUST_UNDEFINED,
123 GPG_TRUST_MARGINAL,
124 GPG_TRUST_FULLY,
125 GPG_TRUST_ULTIMATE
126 };
127
128 struct _GpgCtx {
129 enum _GpgCtxMode mode;
130 CamelSession *session;
131 GCancellable *cancellable;
132 GHashTable *userid_hint;
133 pid_t pid;
134
135 GSList *userids;
136 gchar *sigfile;
137 GPtrArray *recipients; /* GpgRecipientsData * */
138 GSList *recipient_key_files; /* gchar * with filenames of keys in the tmp directory */
139 CamelCipherHash hash;
140
141 gint stdin_fd;
142 gint stdout_fd;
143 gint stderr_fd;
144 gint status_fd;
145 gint passwd_fd; /* only needed for sign/decrypt */
146
147 /* status-fd buffer */
148 guchar *statusbuf;
149 guchar *statusptr;
150 guint statusleft;
151
152 gchar *need_id;
153 gchar *passwd;
154
155 CamelStream *istream;
156 CamelStream *ostream;
157
158 GByteArray *diagbuf;
159 CamelStream *diagnostics;
160
161 gchar *photos_filename;
162 gchar *viewer_cmd;
163
164 GString *decrypt_extra_text; /* Text received during decryption, which is in the blob, but is not encrypted */
165
166 gint exit_status;
167
168 guint exited : 1;
169 guint complete : 1;
170 guint seen_eof1 : 1;
171 guint seen_eof2 : 1;
172 guint always_trust : 1;
173 guint prefer_inline : 1;
174 guint armor : 1;
175 guint need_passwd : 1;
176 guint send_passwd : 1;
177 guint load_photos : 1;
178
179 guint bad_passwds : 2;
180 guint anonymous_recipient : 1;
181
182 guint hadsig : 1;
183 guint badsig : 1;
184 guint errsig : 1;
185 guint goodsig : 1;
186 guint validsig : 1;
187 guint nopubkey : 1;
188 guint nodata : 1;
189 guint trust : 3;
190 guint processing : 1;
191 guint bad_decrypt : 1;
192 guint in_decrypt_stage : 1;
193 guint noseckey : 1;
194 GString *signers;
195 GHashTable *signers_keyid;
196
197 guint diagflushed : 1;
198
199 guint utf8 : 1;
200
201 guint padding : 10;
202 };
203
204 static struct _GpgCtx *
gpg_ctx_new(CamelCipherContext * context,GCancellable * cancellable)205 gpg_ctx_new (CamelCipherContext *context,
206 GCancellable *cancellable)
207 {
208 struct _GpgCtx *gpg;
209 const gchar *charset;
210 CamelStream *stream;
211 CamelSession *session;
212
213 session = camel_cipher_context_get_session (context);
214
215 gpg = g_slice_new0 (struct _GpgCtx);
216 gpg->mode = GPG_CTX_MODE_SIGN;
217 gpg->session = g_object_ref (session);
218 gpg->cancellable = cancellable;
219 gpg->userid_hint = g_hash_table_new (g_str_hash, g_str_equal);
220 gpg->complete = FALSE;
221 gpg->seen_eof1 = TRUE;
222 gpg->seen_eof2 = FALSE;
223 gpg->pid = (pid_t) -1;
224 gpg->exit_status = 0;
225 gpg->exited = FALSE;
226
227 gpg->userids = NULL;
228 gpg->sigfile = NULL;
229 gpg->recipients = NULL;
230 gpg->recipient_key_files = NULL;
231 gpg->hash = CAMEL_CIPHER_HASH_DEFAULT;
232 gpg->always_trust = FALSE;
233 gpg->prefer_inline = FALSE;
234 gpg->armor = FALSE;
235 gpg->load_photos = FALSE;
236 gpg->photos_filename = NULL;
237 gpg->viewer_cmd = NULL;
238
239 gpg->stdin_fd = -1;
240 gpg->stdout_fd = -1;
241 gpg->stderr_fd = -1;
242 gpg->status_fd = -1;
243 gpg->passwd_fd = -1;
244
245 gpg->statusbuf = g_malloc (128);
246 gpg->statusptr = gpg->statusbuf;
247 gpg->statusleft = 128;
248
249 gpg->bad_passwds = 0;
250 gpg->anonymous_recipient = FALSE;
251 gpg->need_passwd = FALSE;
252 gpg->send_passwd = FALSE;
253 gpg->need_id = NULL;
254 gpg->passwd = NULL;
255
256 gpg->nodata = FALSE;
257 gpg->hadsig = FALSE;
258 gpg->badsig = FALSE;
259 gpg->errsig = FALSE;
260 gpg->goodsig = FALSE;
261 gpg->validsig = FALSE;
262 gpg->nopubkey = FALSE;
263 gpg->trust = GPG_TRUST_NONE;
264 gpg->processing = FALSE;
265 gpg->bad_decrypt = FALSE;
266 gpg->in_decrypt_stage = FALSE;
267 gpg->noseckey = FALSE;
268 gpg->signers = NULL;
269 gpg->signers_keyid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
270
271 gpg->istream = NULL;
272 gpg->ostream = NULL;
273 gpg->decrypt_extra_text = NULL;
274
275 gpg->diagbuf = g_byte_array_new ();
276 gpg->diagflushed = FALSE;
277
278 stream = camel_stream_mem_new_with_byte_array (gpg->diagbuf);
279
280 if ((charset = camel_iconv_locale_charset ()) && g_ascii_strcasecmp (charset, "UTF-8") != 0) {
281 CamelMimeFilter *filter;
282 CamelStream *fstream;
283
284 gpg->utf8 = FALSE;
285
286 if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
287 fstream = camel_stream_filter_new (stream);
288 camel_stream_filter_add (
289 CAMEL_STREAM_FILTER (fstream), filter);
290 g_object_unref (filter);
291 g_object_unref (stream);
292
293 stream = (CamelStream *) fstream;
294 }
295 } else {
296 gpg->utf8 = TRUE;
297 }
298
299 gpg->diagnostics = stream;
300
301 return gpg;
302 }
303
304 static void
gpg_ctx_set_mode(struct _GpgCtx * gpg,enum _GpgCtxMode mode)305 gpg_ctx_set_mode (struct _GpgCtx *gpg,
306 enum _GpgCtxMode mode)
307 {
308 gpg->mode = mode;
309 gpg->need_passwd = ((gpg->mode == GPG_CTX_MODE_SIGN) || (gpg->mode == GPG_CTX_MODE_DECRYPT));
310 }
311
312 static void
gpg_ctx_set_hash(struct _GpgCtx * gpg,CamelCipherHash hash)313 gpg_ctx_set_hash (struct _GpgCtx *gpg,
314 CamelCipherHash hash)
315 {
316 gpg->hash = hash;
317 }
318
319 static void
gpg_ctx_set_always_trust(struct _GpgCtx * gpg,gboolean trust)320 gpg_ctx_set_always_trust (struct _GpgCtx *gpg,
321 gboolean trust)
322 {
323 gpg->always_trust = trust;
324 }
325
326 static void
gpg_ctx_set_prefer_inline(struct _GpgCtx * gpg,gboolean prefer_inline)327 gpg_ctx_set_prefer_inline (struct _GpgCtx *gpg,
328 gboolean prefer_inline)
329 {
330 gpg->prefer_inline = prefer_inline;
331 }
332
333 static void
gpg_ctx_set_userid(struct _GpgCtx * gpg,const gchar * userid)334 gpg_ctx_set_userid (struct _GpgCtx *gpg,
335 const gchar *userid)
336 {
337 g_slist_free_full (gpg->userids, g_free);
338 gpg->userids = NULL;
339
340 if (userid && *userid) {
341 gchar **uids = g_strsplit (userid, " ", -1);
342
343 if (!uids) {
344 gpg->userids = g_slist_append (gpg->userids, g_strdup (userid));
345 } else {
346 gint ii;
347
348 for (ii = 0; uids[ii]; ii++) {
349 const gchar *uid = uids[ii];
350
351 if (*uid) {
352 gpg->userids = g_slist_append (gpg->userids, g_strdup (uid));
353 }
354 }
355
356 g_strfreev (uids);
357 }
358 }
359 }
360
361 static void
gpg_ctx_add_recipient(struct _GpgCtx * gpg,const gchar * keyid,const gchar * known_key_data)362 gpg_ctx_add_recipient (struct _GpgCtx *gpg,
363 const gchar *keyid,
364 const gchar *known_key_data)
365 {
366 GpgRecipientsData *rd;
367 gchar *safe_keyid;
368
369 if (gpg->mode != GPG_CTX_MODE_ENCRYPT)
370 return;
371
372 if (!gpg->recipients)
373 gpg->recipients = g_ptr_array_new ();
374
375 g_return_if_fail (keyid != NULL);
376
377 /* If the recipient looks like an email address,
378 * enclose it in brackets to ensure an exact match. */
379 if (strchr (keyid, '@') != NULL) {
380 safe_keyid = g_strdup_printf ("<%s>", keyid);
381 } else {
382 safe_keyid = g_strdup (keyid);
383 }
384
385 rd = g_slice_new0 (GpgRecipientsData);
386 rd->keyid = safe_keyid;
387 rd->known_key_data = g_strdup (known_key_data);
388
389 g_ptr_array_add (gpg->recipients, rd);
390 }
391
392 static void
gpg_ctx_set_sigfile(struct _GpgCtx * gpg,const gchar * sigfile)393 gpg_ctx_set_sigfile (struct _GpgCtx *gpg,
394 const gchar *sigfile)
395 {
396 g_free (gpg->sigfile);
397 gpg->sigfile = g_strdup (sigfile);
398 }
399
400 static void
gpg_ctx_set_armor(struct _GpgCtx * gpg,gboolean armor)401 gpg_ctx_set_armor (struct _GpgCtx *gpg,
402 gboolean armor)
403 {
404 gpg->armor = armor;
405 }
406
407 static void
gpg_ctx_set_load_photos(struct _GpgCtx * gpg,gboolean load_photos)408 gpg_ctx_set_load_photos (struct _GpgCtx *gpg,
409 gboolean load_photos)
410 {
411 gpg->load_photos = load_photos;
412 }
413
414 static void
gpg_ctx_set_istream(struct _GpgCtx * gpg,CamelStream * istream)415 gpg_ctx_set_istream (struct _GpgCtx *gpg,
416 CamelStream *istream)
417 {
418 g_object_ref (istream);
419 if (gpg->istream)
420 g_object_unref (gpg->istream);
421 gpg->istream = istream;
422 }
423
424 static void
gpg_ctx_set_ostream(struct _GpgCtx * gpg,CamelStream * ostream)425 gpg_ctx_set_ostream (struct _GpgCtx *gpg,
426 CamelStream *ostream)
427 {
428 g_object_ref (ostream);
429 if (gpg->ostream)
430 g_object_unref (gpg->ostream);
431 gpg->ostream = ostream;
432 gpg->seen_eof1 = FALSE;
433 }
434
435 static const gchar *
gpg_ctx_get_diagnostics(struct _GpgCtx * gpg)436 gpg_ctx_get_diagnostics (struct _GpgCtx *gpg)
437 {
438 if (!gpg->diagflushed) {
439 gchar *prefix;
440
441 gpg->diagflushed = TRUE;
442 camel_stream_flush (gpg->diagnostics, NULL, NULL);
443 if (gpg->diagbuf->len == 0)
444 return NULL;
445
446 /* Translators: The '%s' is replaced with the actual path and filename of the used gpg, like '/usr/bin/gpg2' */
447 prefix = g_strdup_printf (_("Output from %s:"), gpg_ctx_get_executable_name ());
448
449 if (prefix && *prefix) {
450 g_byte_array_prepend (gpg->diagbuf, (const guint8 *) "\n", 1);
451 g_byte_array_prepend (gpg->diagbuf, (const guint8 *) prefix, strlen (prefix));
452 }
453
454 g_byte_array_append (gpg->diagbuf, (guchar *) "", 1);
455
456 g_free (prefix);
457 }
458
459 if (gpg->diagbuf->len == 0)
460 return NULL;
461
462 return (const gchar *) gpg->diagbuf->data;
463 }
464
465 static void
userid_hint_free(gpointer key,gpointer value,gpointer user_data)466 userid_hint_free (gpointer key,
467 gpointer value,
468 gpointer user_data)
469 {
470 g_free (key);
471 g_free (value);
472 }
473
474 static void
gpg_ctx_free(struct _GpgCtx * gpg)475 gpg_ctx_free (struct _GpgCtx *gpg)
476 {
477 gint i;
478
479 if (gpg == NULL)
480 return;
481
482 if (gpg->session)
483 g_object_unref (gpg->session);
484
485 g_hash_table_foreach (gpg->userid_hint, userid_hint_free, NULL);
486 g_hash_table_destroy (gpg->userid_hint);
487
488 g_slist_free_full (gpg->userids, g_free);
489
490 g_free (gpg->sigfile);
491
492 if (gpg->recipients) {
493 for (i = 0; i < gpg->recipients->len; i++)
494 gpg_recipients_data_free (gpg->recipients->pdata[i]);
495
496 g_ptr_array_free (gpg->recipients, TRUE);
497 }
498
499 if (gpg->recipient_key_files) {
500 GSList *link;
501
502 if (!camel_debug ("gpg:data")) {
503 for (link = gpg->recipient_key_files; link; link = g_slist_next (link)) {
504 g_unlink (link->data);
505 }
506 }
507
508 g_slist_free_full (gpg->recipient_key_files, g_free);
509 gpg->recipient_key_files = NULL;
510 }
511
512 if (gpg->stdin_fd != -1)
513 close (gpg->stdin_fd);
514 if (gpg->stdout_fd != -1)
515 close (gpg->stdout_fd);
516 if (gpg->stderr_fd != -1)
517 close (gpg->stderr_fd);
518 if (gpg->status_fd != -1)
519 close (gpg->status_fd);
520 if (gpg->passwd_fd != -1)
521 close (gpg->passwd_fd);
522
523 g_free (gpg->statusbuf);
524
525 g_free (gpg->need_id);
526
527 if (gpg->passwd) {
528 memset (gpg->passwd, 0, strlen (gpg->passwd));
529 g_free (gpg->passwd);
530 }
531
532 if (gpg->istream)
533 g_object_unref (gpg->istream);
534
535 if (gpg->ostream)
536 g_object_unref (gpg->ostream);
537
538 g_object_unref (gpg->diagnostics);
539
540 if (gpg->signers)
541 g_string_free (gpg->signers, TRUE);
542
543 g_hash_table_destroy (gpg->signers_keyid);
544 if (gpg->photos_filename)
545 g_unlink (gpg->photos_filename);
546
547 g_free (gpg->photos_filename);
548 g_free (gpg->viewer_cmd);
549
550 if (gpg->decrypt_extra_text)
551 g_string_free (gpg->decrypt_extra_text, TRUE);
552
553 g_slice_free (struct _GpgCtx, gpg);
554 }
555
556 static const gchar *
gpg_ctx_get_executable_name(void)557 gpg_ctx_get_executable_name (void)
558 {
559 static gint index = -1;
560 static gchar preset_binary[512 + 1];
561 const gchar *names[] = {
562 "",
563 "gpg2", /* Prefer gpg2, which the seahorse might use too */
564 "gpg",
565 NULL
566 };
567
568 names[0] = preset_binary;
569
570 if (index == -1) {
571 GSettings *settings;
572 gchar *path;
573
574 settings = g_settings_new ("org.gnome.evolution-data-server");
575 path = g_settings_get_string (settings, "camel-gpg-binary");
576 g_clear_object (&settings);
577
578 preset_binary[0] = 0;
579
580 if (path && *path && g_file_test (path, G_FILE_TEST_IS_REGULAR)) {
581 if (strlen (path) > 512) {
582 g_warning ("%s: Path is longer than expected (max 512), ignoring it; value:'%s'", G_STRFUNC, path);
583 } else {
584 strcpy (preset_binary, path);
585 }
586 }
587
588 g_free (path);
589
590 for (index = 0; names[index]; index++) {
591 if (!*(names[index]))
592 continue;
593
594 path = g_find_program_in_path (names[index]);
595
596 if (path) {
597 g_free (path);
598 break;
599 }
600 }
601
602 if (!names[index])
603 index = 1;
604 }
605
606 return names[index];
607 }
608
609 #ifndef G_OS_WIN32
610
611 static const gchar *
gpg_hash_str(CamelCipherHash hash)612 gpg_hash_str (CamelCipherHash hash)
613 {
614 switch (hash) {
615 case CAMEL_CIPHER_HASH_MD2:
616 return "--digest-algo=MD2";
617 case CAMEL_CIPHER_HASH_MD5:
618 return "--digest-algo=MD5";
619 case CAMEL_CIPHER_HASH_SHA1:
620 return "--digest-algo=SHA1";
621 case CAMEL_CIPHER_HASH_SHA256:
622 return "--digest-algo=SHA256";
623 case CAMEL_CIPHER_HASH_SHA384:
624 return "--digest-algo=SHA384";
625 case CAMEL_CIPHER_HASH_SHA512:
626 return "--digest-algo=SHA512";
627 case CAMEL_CIPHER_HASH_RIPEMD160:
628 return "--digest-algo=RIPEMD160";
629 default:
630 return NULL;
631 }
632 }
633
634 static GPtrArray *
gpg_ctx_get_argv(struct _GpgCtx * gpg,gint status_fd,gchar ** sfd,gint passwd_fd,gchar ** pfd)635 gpg_ctx_get_argv (struct _GpgCtx *gpg,
636 gint status_fd,
637 gchar **sfd,
638 gint passwd_fd,
639 gchar **pfd)
640 {
641 const gchar *hash_str;
642 GPtrArray *argv;
643 gchar *buf;
644 gint i;
645
646 argv = g_ptr_array_new ();
647 g_ptr_array_add (argv, (guint8 *) gpg_ctx_get_executable_name ());
648
649 g_ptr_array_add (argv, (guint8 *) "--verbose");
650 g_ptr_array_add (argv, (guint8 *) "--no-secmem-warning");
651 g_ptr_array_add (argv, (guint8 *) "--no-greeting");
652 g_ptr_array_add (argv, (guint8 *) "--no-tty");
653
654 if (passwd_fd == -1) {
655 /* only use batch mode if we don't intend on using the
656 * interactive --command-fd option */
657 g_ptr_array_add (argv, (guint8 *) "--batch");
658 g_ptr_array_add (argv, (guint8 *) "--yes");
659 }
660
661 *sfd = buf = g_strdup_printf ("--status-fd=%d", status_fd);
662 g_ptr_array_add (argv, buf);
663
664 if (passwd_fd != -1) {
665 *pfd = buf = g_strdup_printf ("--command-fd=%d", passwd_fd);
666 g_ptr_array_add (argv, buf);
667 }
668
669 if (gpg->load_photos) {
670 if (!gpg->viewer_cmd) {
671 gint filefd;
672
673 filefd = g_file_open_tmp ("camel-gpg-photo-state-XXXXXX", &gpg->photos_filename, NULL);
674 if (filefd) {
675 gchar *viewer_filename;
676
677 close (filefd);
678
679 viewer_filename = g_build_filename (CAMEL_LIBEXECDIR, "camel-gpg-photo-saver", NULL);
680 gpg->viewer_cmd = g_strdup_printf ("%s --state \"%s\" --photo \"%%i\" --keyid \"%%K\" --type \"%%t\"", viewer_filename, gpg->photos_filename);
681 g_free (viewer_filename);
682 }
683 }
684
685 if (gpg->viewer_cmd) {
686 g_ptr_array_add (argv, (guint8 *) "--verify-options");
687 g_ptr_array_add (argv, (guint8 *) "show-photos");
688
689 g_ptr_array_add (argv, (guint8 *) "--photo-viewer");
690 g_ptr_array_add (argv, (guint8 *) gpg->viewer_cmd);
691 }
692 }
693
694 switch (gpg->mode) {
695 case GPG_CTX_MODE_SIGN:
696 if (gpg->prefer_inline) {
697 g_ptr_array_add (argv, (guint8 *) "--clearsign");
698 } else {
699 g_ptr_array_add (argv, (guint8 *) "--sign");
700 g_ptr_array_add (argv, (guint8 *) "--detach");
701 if (gpg->armor)
702 g_ptr_array_add (argv, (guint8 *) "--armor");
703 }
704 hash_str = gpg_hash_str (gpg->hash);
705 if (hash_str)
706 g_ptr_array_add (argv, (guint8 *) hash_str);
707 if (gpg->userids) {
708 GSList *uiter;
709
710 for (uiter = gpg->userids; uiter; uiter = uiter->next) {
711 g_ptr_array_add (argv, (guint8 *) "-u");
712 g_ptr_array_add (argv, (guint8 *) uiter->data);
713 }
714 }
715 g_ptr_array_add (argv, (guint8 *) "--output");
716 g_ptr_array_add (argv, (guint8 *) "-");
717 break;
718 case GPG_CTX_MODE_VERIFY:
719 if (!camel_session_get_online (gpg->session)) {
720 /* this is a deprecated flag to gpg since 1.0.7 */
721 /*g_ptr_array_add (argv, "--no-auto-key-retrieve");*/
722 g_ptr_array_add (argv, (guint8 *) "--keyserver-options");
723 g_ptr_array_add (argv, (guint8 *) "no-auto-key-retrieve");
724 }
725 g_ptr_array_add (argv, (guint8 *) "--verify");
726 if (gpg->sigfile)
727 g_ptr_array_add (argv, gpg->sigfile);
728 g_ptr_array_add (argv, (guint8 *) "-");
729 break;
730 case GPG_CTX_MODE_ENCRYPT:
731 g_ptr_array_add (argv, (guint8 *) "--encrypt");
732 if (gpg->armor || gpg->prefer_inline)
733 g_ptr_array_add (argv, (guint8 *) "--armor");
734 if (gpg->always_trust)
735 g_ptr_array_add (argv, (guint8 *) "--always-trust");
736 if (gpg->userids) {
737 GSList *uiter;
738
739 for (uiter = gpg->userids; uiter; uiter = uiter->next) {
740 g_ptr_array_add (argv, (guint8 *) "-u");
741 g_ptr_array_add (argv, (guint8 *) uiter->data);
742 }
743 }
744 if (gpg->recipients) {
745 for (i = 0; i < gpg->recipients->len; i++) {
746 const GpgRecipientsData *rd = gpg->recipients->pdata[i];
747 gboolean add_with_keyid = TRUE;
748
749 if (!rd)
750 continue;
751
752 if (rd->known_key_data && *(rd->known_key_data)) {
753 gsize len = 0;
754 guchar *data;
755
756 data = g_base64_decode (rd->known_key_data, &len);
757 if (data && len) {
758 gchar *filename = NULL;
759 gint filefd;
760
761 filefd = g_file_open_tmp ("camel-gpg-key-XXXXXX", &filename, NULL);
762 if (filefd) {
763 gpg->recipient_key_files = g_slist_prepend (gpg->recipient_key_files, filename);
764
765 if (camel_write (filefd, (const gchar *) data, len, gpg->cancellable, NULL) == len) {
766 add_with_keyid = FALSE;
767 g_ptr_array_add (argv, (guint8 *) "--recipient-file");
768 g_ptr_array_add (argv, (guint8 *) filename);
769 }
770
771 close (filefd);
772 }
773 }
774
775 g_free (data);
776 }
777
778 if (add_with_keyid) {
779 g_ptr_array_add (argv, (guint8 *) "-r");
780 g_ptr_array_add (argv, rd->keyid);
781 }
782 }
783 }
784 g_ptr_array_add (argv, (guint8 *) "--output");
785 g_ptr_array_add (argv, (guint8 *) "-");
786 break;
787 case GPG_CTX_MODE_DECRYPT:
788 g_ptr_array_add (argv, (guint8 *) "--decrypt");
789 g_ptr_array_add (argv, (guint8 *) "--output");
790 g_ptr_array_add (argv, (guint8 *) "-");
791 break;
792 }
793
794 g_ptr_array_add (argv, NULL);
795
796 return argv;
797 }
798
799 #endif
800
801 static gboolean
gpg_ctx_op_start(struct _GpgCtx * gpg,GError ** error)802 gpg_ctx_op_start (struct _GpgCtx *gpg,
803 GError **error)
804 {
805 #ifndef G_OS_WIN32
806 gchar *status_fd = NULL, *passwd_fd = NULL;
807 gint i, maxfd, errnosave, fds[10];
808 GPtrArray *argv;
809 gint flags;
810
811 for (i = 0; i < 10; i++)
812 fds[i] = -1;
813
814 maxfd = gpg->need_passwd ? 10 : 8;
815 for (i = 0; i < maxfd; i += 2) {
816 if (pipe (fds + i) == -1)
817 goto exception;
818 }
819
820 argv = gpg_ctx_get_argv (gpg, fds[7], &status_fd, fds[8], &passwd_fd);
821
822 if (camel_debug_start ("gpg")) {
823 guint ii;
824
825 printf ("[GPG] going to execute:");
826 for (ii = 0; ii < argv->len; ii++) {
827 if (!argv->pdata[ii])
828 break;
829
830 printf (" %s", (const gchar *) argv->pdata[ii]);
831 }
832 printf ("\n");
833 camel_debug_end ();
834 }
835
836 if (!(gpg->pid = fork ())) {
837 /* child process */
838
839 if ((dup2 (fds[0], STDIN_FILENO) < 0 ) ||
840 (dup2 (fds[3], STDOUT_FILENO) < 0 ) ||
841 (dup2 (fds[5], STDERR_FILENO) < 0 )) {
842 _exit (255);
843 }
844
845 /* Dissociate from camel's controlling terminal so
846 * that gpg won't be able to read from it.
847 */
848 setsid ();
849
850 maxfd = sysconf (_SC_OPEN_MAX);
851 /* Loop over all fds. */
852 for (i = 3; i < maxfd; i++) {
853 /* don't close the status-fd or passwd-fd */
854 if (i != fds[7] && i != fds[8]) {
855 if (fcntl (i, F_SETFD, FD_CLOEXEC) == -1) {
856 /* Do nothing here. Cannot use CHECK_CALL() macro here, because
857 it makes the process stuck, possibly due to the debug print. */
858 }
859 }
860 }
861
862 /* run gpg */
863 execvp (gpg_ctx_get_executable_name (), (gchar **) argv->pdata);
864 _exit (255);
865 } else if (gpg->pid < 0) {
866 g_ptr_array_free (argv, TRUE);
867 g_free (status_fd);
868 g_free (passwd_fd);
869 goto exception;
870 }
871
872 g_ptr_array_free (argv, TRUE);
873 g_free (status_fd);
874 g_free (passwd_fd);
875
876 /* Parent */
877 close (fds[0]);
878 gpg->stdin_fd = fds[1];
879 gpg->stdout_fd = fds[2];
880 close (fds[3]);
881 gpg->stderr_fd = fds[4];
882 close (fds[5]);
883 gpg->status_fd = fds[6];
884 close (fds[7]);
885
886 if (gpg->need_passwd) {
887 close (fds[8]);
888 gpg->passwd_fd = fds[9];
889 flags = fcntl (gpg->passwd_fd, F_GETFL);
890 CHECK_CALL (fcntl (gpg->passwd_fd, F_SETFL, flags | O_NONBLOCK));
891 }
892
893 flags = fcntl (gpg->stdin_fd, F_GETFL);
894 CHECK_CALL (fcntl (gpg->stdin_fd, F_SETFL, flags | O_NONBLOCK));
895
896 flags = fcntl (gpg->stdout_fd, F_GETFL);
897 CHECK_CALL (fcntl (gpg->stdout_fd, F_SETFL, flags | O_NONBLOCK));
898
899 flags = fcntl (gpg->stderr_fd, F_GETFL);
900 CHECK_CALL (fcntl (gpg->stderr_fd, F_SETFL, flags | O_NONBLOCK));
901
902 flags = fcntl (gpg->status_fd, F_GETFL);
903 CHECK_CALL (fcntl (gpg->status_fd, F_SETFL, flags | O_NONBLOCK));
904
905 return TRUE;
906
907 exception:
908
909 errnosave = errno;
910
911 for (i = 0; i < 10; i++) {
912 if (fds[i] != -1)
913 close (fds[i]);
914 }
915
916 errno = errnosave;
917 #else
918 /* FIXME: Port me */
919 g_warning ("%s: Not implemented", G_STRFUNC);
920
921 errno = EINVAL;
922 #endif
923
924 if (errno != 0)
925 g_set_error (
926 error, G_IO_ERROR,
927 g_io_error_from_errno (errno),
928 _("Failed to execute gpg: %s"),
929 g_strerror (errno));
930 else
931 g_set_error (
932 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
933 _("Failed to execute gpg: %s"), _("Unknown"));
934
935 return FALSE;
936 }
937
938 #ifndef G_OS_WIN32
939
940 static const gchar *
next_token(const gchar * in,gchar ** token)941 next_token (const gchar *in,
942 gchar **token)
943 {
944 const gchar *start, *inptr = in;
945
946 while (*inptr == ' ')
947 inptr++;
948
949 if (*inptr == '\0' || *inptr == '\n') {
950 if (token)
951 *token = NULL;
952 return inptr;
953 }
954
955 start = inptr;
956 while (*inptr && *inptr != ' ' && *inptr != '\n')
957 inptr++;
958
959 if (token)
960 *token = g_strndup (start, inptr - start);
961
962 return inptr;
963 }
964
965 static void
gpg_ctx_extract_signer_from_status(struct _GpgCtx * gpg,const gchar * status)966 gpg_ctx_extract_signer_from_status (struct _GpgCtx *gpg,
967 const gchar *status)
968 {
969 const gchar *tmp;
970
971 g_return_if_fail (gpg != NULL);
972 g_return_if_fail (status != NULL);
973
974 /* there's a key ID, then the email address */
975 tmp = status;
976
977 status = strchr (status, ' ');
978 if (status) {
979 gchar *keyid;
980 const gchar *str = status + 1;
981 const gchar *eml = strchr (str, '<');
982
983 keyid = g_strndup (tmp, status - tmp);
984
985 if (eml && eml > str) {
986 eml--;
987 if (strchr (str, ' ') >= eml)
988 eml = NULL;
989 } else {
990 eml = NULL;
991 }
992
993 if (gpg->signers) {
994 g_string_append (gpg->signers, ", ");
995 } else {
996 gpg->signers = g_string_new ("");
997 }
998
999 if (eml) {
1000 g_string_append_c (gpg->signers, '\"');
1001 g_string_append_len (gpg->signers, str, eml - str);
1002 g_string_append_c (gpg->signers, '\"');
1003 g_string_append (gpg->signers, eml);
1004 } else {
1005 g_string_append (gpg->signers, str);
1006 }
1007
1008 g_hash_table_insert (gpg->signers_keyid, g_strdup (str), keyid);
1009 }
1010 }
1011
1012 static gint
gpg_ctx_parse_status(struct _GpgCtx * gpg,GError ** error)1013 gpg_ctx_parse_status (struct _GpgCtx *gpg,
1014 GError **error)
1015 {
1016 register guchar *inptr;
1017 const guchar *status;
1018 gsize nread, nwritten;
1019 gint len;
1020
1021 parse:
1022
1023 inptr = gpg->statusbuf;
1024 while (inptr < gpg->statusptr && *inptr != '\n')
1025 inptr++;
1026
1027 if (*inptr != '\n') {
1028 /* we don't have enough data buffered to parse this status line */
1029 return 0;
1030 }
1031
1032 *inptr++ = '\0';
1033 status = gpg->statusbuf;
1034
1035 if (camel_debug ("gpg:status"))
1036 printf ("status: %s\n", status);
1037
1038 if (strncmp ((const gchar *) status, "[GNUPG:] ", 9) != 0) {
1039 gchar *message;
1040
1041 message = g_locale_to_utf8 (
1042 (const gchar *) status, -1, NULL, NULL, NULL);
1043 g_set_error (
1044 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1045 _("Unexpected GnuPG status message encountered:\n\n%s"),
1046 message);
1047 g_free (message);
1048
1049 return -1;
1050 }
1051
1052 status += 9;
1053
1054 if (!strncmp ((gchar *) status, "ENC_TO ", 7)) {
1055 gchar *key = NULL;
1056
1057 status += 7;
1058
1059 next_token ((gchar *) status, &key);
1060 if (key) {
1061 gboolean all_zero = *key == '0';
1062 gint i = 0;
1063
1064 while (key[i] && all_zero) {
1065 all_zero = key[i] == '0';
1066 i++;
1067 }
1068
1069 gpg->anonymous_recipient = all_zero;
1070
1071 g_free (key);
1072 }
1073 } else if (!strncmp ((gchar *) status, "USERID_HINT ", 12)) {
1074 gchar *hint, *user;
1075
1076 status += 12;
1077 status = (const guchar *) next_token ((gchar *) status, &hint);
1078 if (!hint) {
1079 g_set_error (
1080 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1081 _("Failed to parse gpg userid hint."));
1082 return -1;
1083 }
1084
1085 if (g_hash_table_lookup (gpg->userid_hint, hint)) {
1086 /* we already have this userid hint... */
1087 g_free (hint);
1088 goto recycle;
1089 }
1090
1091 if (gpg->utf8 || !(user = g_locale_to_utf8 ((gchar *) status, -1, &nread, &nwritten, NULL)))
1092 user = g_strdup ((gchar *) status);
1093
1094 g_strstrip (user);
1095
1096 g_hash_table_insert (gpg->userid_hint, hint, user);
1097 } else if (!strncmp ((gchar *) status, "NEED_PASSPHRASE ", 16)) {
1098 gchar *userid;
1099
1100 status += 16;
1101
1102 next_token ((gchar *) status, &userid);
1103 if (!userid) {
1104 g_set_error (
1105 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1106 _("Failed to parse gpg passphrase request."));
1107 return -1;
1108 }
1109
1110 g_free (gpg->need_id);
1111 gpg->need_id = userid;
1112 } else if (!strncmp ((gchar *) status, "NEED_PASSPHRASE_PIN ", 20)) {
1113 gchar *userid;
1114
1115 status += 20;
1116
1117 next_token ((gchar *) status, &userid);
1118 if (!userid) {
1119 g_set_error (
1120 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1121 _("Failed to parse gpg passphrase request."));
1122 return -1;
1123 }
1124
1125 g_free (gpg->need_id);
1126 gpg->need_id = userid;
1127 } else if (!strncmp ((gchar *) status, "GET_HIDDEN ", 11)) {
1128 const gchar *name = NULL;
1129 gchar *prompt, *passwd;
1130 guint32 flags;
1131 GError *local_error = NULL;
1132
1133 status += 11;
1134
1135 if (gpg->need_id && !(name = g_hash_table_lookup (gpg->userid_hint, gpg->need_id)))
1136 name = gpg->need_id;
1137 else if (!name)
1138 name = "";
1139
1140 if (!strncmp ((gchar *) status, "passphrase.pin.ask", 18)) {
1141 prompt = g_markup_printf_escaped (
1142 _("You need a PIN to unlock the key for your\n"
1143 "SmartCard: “%s”"), name);
1144 } else if (!strncmp ((gchar *) status, "passphrase.enter", 16)) {
1145 prompt = g_markup_printf_escaped (
1146 _("You need a passphrase to unlock the key for\n"
1147 "user: “%s”"), name);
1148 } else {
1149 next_token ((gchar *) status, &prompt);
1150 g_set_error (
1151 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1152 _("Unexpected request from GnuPG for “%s”"),
1153 prompt);
1154 g_free (prompt);
1155 return -1;
1156 }
1157
1158 if (gpg->anonymous_recipient) {
1159 gchar *tmp = prompt;
1160
1161 /* FIXME Reword prompt message. */
1162 prompt = g_strconcat (
1163 tmp, "\n",
1164 _("Note the encrypted content doesn’t contain "
1165 "information about a recipient, thus there "
1166 "will be a password prompt for each of stored "
1167 "private key."), NULL);
1168
1169 g_free (tmp);
1170 }
1171
1172 flags = CAMEL_SESSION_PASSWORD_SECRET | CAMEL_SESSION_PASSPHRASE;
1173 if ((passwd = camel_session_get_password (gpg->session, NULL, prompt, gpg->need_id, flags, &local_error))) {
1174 if (!gpg->utf8) {
1175 gchar *opasswd = passwd;
1176
1177 if ((passwd = g_locale_to_utf8 (passwd, -1, &nread, &nwritten, NULL))) {
1178 memset (opasswd, 0, strlen (opasswd));
1179 g_free (opasswd);
1180 } else {
1181 passwd = opasswd;
1182 }
1183 }
1184
1185 gpg->passwd = g_strdup_printf ("%s\n", passwd);
1186 memset (passwd, 0, strlen (passwd));
1187 g_free (passwd);
1188
1189 gpg->send_passwd = TRUE;
1190 } else {
1191 if (local_error == NULL)
1192 g_set_error (
1193 error, G_IO_ERROR,
1194 G_IO_ERROR_CANCELLED,
1195 _("Cancelled"));
1196 g_propagate_error (error, local_error);
1197
1198 return -1;
1199 }
1200
1201 g_free (prompt);
1202 } else if (!strncmp ((gchar *) status, "GOOD_PASSPHRASE", 15)) {
1203 gpg->bad_passwds = 0;
1204 } else if (!strncmp ((gchar *) status, "BAD_PASSPHRASE", 14)) {
1205 /* with anonymous recipient is user asked for his/her password for each stored key,
1206 * thus here cannot be counted wrong passwords */
1207 if (!gpg->anonymous_recipient) {
1208 gpg->bad_passwds++;
1209
1210 camel_session_forget_password (gpg->session, NULL, gpg->need_id, error);
1211
1212 if (gpg->bad_passwds == 3) {
1213 g_set_error (
1214 error, CAMEL_SERVICE_ERROR,
1215 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1216 _("Failed to unlock secret key: "
1217 "3 bad passphrases given."));
1218 return -1;
1219 }
1220 }
1221 } else if (!strncmp ((const gchar *) status, "UNEXPECTED ", 11)) {
1222 /* this is an error */
1223 gchar *message;
1224
1225 message = g_locale_to_utf8 (
1226 (const gchar *) status + 11, -1, NULL, NULL, NULL);
1227 g_set_error (
1228 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1229 _("Unexpected response from GnuPG: %s"), message);
1230 g_free (message);
1231
1232 return -1;
1233 } else if (!strncmp ((gchar *) status, "NODATA", 6)) {
1234 /* this is an error */
1235 /* But we ignore it anyway, we should get other response codes to say why */
1236 gpg->nodata = TRUE;
1237 } else {
1238 if (!strncmp ((gchar *) status, "BEGIN_", 6)) {
1239 gpg->processing = TRUE;
1240 } else if (!strncmp ((gchar *) status, "END_", 4)) {
1241 gpg->processing = FALSE;
1242 }
1243
1244 /* check to see if we are complete */
1245 switch (gpg->mode) {
1246 case GPG_CTX_MODE_SIGN:
1247 if (!strncmp ((gchar *) status, "SIG_CREATED ", 12)) {
1248 /* SIG_CREATED <type> <pubkey algo> <hash algo> <class> <timestamp> <key fpr> */
1249 const gchar *str, *p;
1250 gint i = 0;
1251
1252 str = (const gchar *) status + 12;
1253 while (p = strchr (str, ' '), i < 2 && p) {
1254 str = p + 1;
1255 i++;
1256 }
1257
1258 if (*str && i == 2) {
1259 struct {
1260 gint gpg_hash_algo;
1261 CamelCipherHash camel_hash_algo;
1262 } hash_algos[] = {
1263 /* the rest are deprecated/not supported by gpg any more */
1264 { 2, CAMEL_CIPHER_HASH_SHA1 },
1265 { 3, CAMEL_CIPHER_HASH_RIPEMD160 },
1266 { 8, CAMEL_CIPHER_HASH_SHA256 },
1267 { 9, CAMEL_CIPHER_HASH_SHA384 },
1268 { 10, CAMEL_CIPHER_HASH_SHA512 }
1269 };
1270
1271 gint gpg_hash = strtoul (str, NULL, 10);
1272
1273 for (i = 0; i < G_N_ELEMENTS (hash_algos); i++) {
1274 if (hash_algos[i].gpg_hash_algo == gpg_hash) {
1275 gpg->hash = hash_algos[i].camel_hash_algo;
1276 break;
1277 }
1278 }
1279 }
1280 }
1281 break;
1282 case GPG_CTX_MODE_DECRYPT:
1283 if (!strncmp ((gchar *) status, "BEGIN_DECRYPTION", 16)) {
1284 gpg->bad_decrypt = FALSE;
1285 /* Mark it's expected to get decrypted data on stdout */
1286 gpg->in_decrypt_stage = TRUE;
1287 break;
1288 } else if (!strncmp ((gchar *) status, "END_DECRYPTION", 14)) {
1289 /* Mark to no longer expect decrypted data */
1290 gpg->in_decrypt_stage = FALSE;
1291 break;
1292 } else if (!strncmp ((gchar *) status, "NO_SECKEY ", 10)) {
1293 gpg->noseckey = TRUE;
1294 break;
1295 } else if (!strncmp ((gchar *) status, "DECRYPTION_FAILED", 17)) {
1296 gpg->bad_decrypt = TRUE;
1297 break;
1298 }
1299 /* let if fall through to verify possible signatures too */
1300 /* break; */
1301 /* falls through */
1302 case GPG_CTX_MODE_VERIFY:
1303 if (!strncmp ((gchar *) status, "TRUST_", 6)) {
1304 status += 6;
1305 if (!strncmp ((gchar *) status, "NEVER", 5)) {
1306 gpg->trust = GPG_TRUST_NEVER;
1307 } else if (!strncmp ((gchar *) status, "MARGINAL", 8)) {
1308 gpg->trust = GPG_TRUST_MARGINAL;
1309 } else if (!strncmp ((gchar *) status, "FULLY", 5)) {
1310 gpg->trust = GPG_TRUST_FULLY;
1311 } else if (!strncmp ((gchar *) status, "ULTIMATE", 8)) {
1312 gpg->trust = GPG_TRUST_ULTIMATE;
1313 } else if (!strncmp ((gchar *) status, "UNDEFINED", 9)) {
1314 gpg->trust = GPG_TRUST_UNDEFINED;
1315 }
1316 } else if (!strncmp ((gchar *) status, "GOODSIG ", 8)) {
1317 gpg->goodsig = TRUE;
1318 gpg->hadsig = TRUE;
1319
1320 gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 8);
1321 } else if (!strncmp ((gchar *) status, "EXPKEYSIG ", 10)) {
1322 gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 10);
1323 } else if (!strncmp ((gchar *) status, "VALIDSIG ", 9)) {
1324 gpg->validsig = TRUE;
1325 } else if (!strncmp ((gchar *) status, "BADSIG ", 7)) {
1326 gpg->badsig = FALSE;
1327 gpg->hadsig = TRUE;
1328
1329 gpg_ctx_extract_signer_from_status (gpg, (const gchar *) status + 7);
1330 } else if (!strncmp ((gchar *) status, "ERRSIG ", 7)) {
1331 /* Note: NO_PUBKEY often comes after an ERRSIG */
1332 gpg->errsig = FALSE;
1333 gpg->hadsig = TRUE;
1334 } else if (!strncmp ((gchar *) status, "NO_PUBKEY ", 10)) {
1335 gpg->nopubkey = TRUE;
1336 }
1337 break;
1338 case GPG_CTX_MODE_ENCRYPT:
1339 if (!strncmp ((gchar *) status, "BEGIN_ENCRYPTION", 16)) {
1340 /* nothing to do... but we know to expect data on stdout soon */
1341 } else if (!strncmp ((gchar *) status, "END_ENCRYPTION", 14)) {
1342 /* nothing to do, but we know the end is near? */
1343 } else if (!strncmp ((gchar *) status, "NO_RECP", 7)) {
1344 g_set_error (
1345 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1346 _("Failed to encrypt: No valid recipients specified."));
1347 return -1;
1348 } else if (!strncmp ((gchar *) status, "INV_RECP ", 9)) {
1349 const gchar *addr;
1350
1351 addr = strchr ((gchar *) status, '<');
1352 if (!addr)
1353 addr = ((gchar *) status) + 10;
1354
1355 g_set_error (
1356 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1357 /* Translators: The first '%s' is replaced with the e-mail address, like '<user@example.com>';
1358 the second '%s' is replaced with the actual path and filename of the used gpg, like '/usr/bin/gpg2' */
1359 _("Failed to encrypt: Invalid recipient %s specified. A common issue is that the %s doesn’t have imported public key for this recipient."),
1360 addr, gpg_ctx_get_executable_name ());
1361 return -1;
1362 }
1363 break;
1364 }
1365 }
1366
1367 recycle:
1368
1369 /* recycle our statusbuf by moving inptr to the beginning of statusbuf */
1370 len = gpg->statusptr - inptr;
1371 memmove (gpg->statusbuf, inptr, len);
1372
1373 len = inptr - gpg->statusbuf;
1374 gpg->statusleft += len;
1375 gpg->statusptr -= len;
1376
1377 /* if we have more data, try parsing the next line? */
1378 if (gpg->statusptr > gpg->statusbuf)
1379 goto parse;
1380
1381 return 0;
1382 }
1383
1384 #endif
1385
1386 #define status_backup(gpg, start, len) G_STMT_START { \
1387 if (gpg->statusleft <= len) { \
1388 guint slen, soff; \
1389 \
1390 slen = soff = gpg->statusptr - gpg->statusbuf; \
1391 slen = slen ? slen : 1; \
1392 \
1393 while (slen < soff + len) \
1394 slen <<= 1; \
1395 \
1396 gpg->statusbuf = g_realloc (gpg->statusbuf, slen + 1); \
1397 gpg->statusptr = gpg->statusbuf + soff; \
1398 gpg->statusleft = slen - soff; \
1399 } \
1400 \
1401 memcpy (gpg->statusptr, start, len); \
1402 gpg->statusptr += len; \
1403 gpg->statusleft -= len; \
1404 } G_STMT_END
1405
1406 static void
gpg_ctx_op_cancel(struct _GpgCtx * gpg)1407 gpg_ctx_op_cancel (struct _GpgCtx *gpg)
1408 {
1409 #ifndef G_OS_WIN32
1410 pid_t retval;
1411 gint status;
1412
1413 if (gpg->exited)
1414 return;
1415
1416 kill (gpg->pid, SIGTERM);
1417 sleep (1);
1418 retval = waitpid (gpg->pid, &status, WNOHANG);
1419
1420 if (retval == (pid_t) 0) {
1421 /* no more mr nice guy... */
1422 kill (gpg->pid, SIGKILL);
1423 sleep (1);
1424 waitpid (gpg->pid, &status, WNOHANG);
1425 }
1426 #endif
1427 }
1428
1429 static gint
gpg_ctx_op_step(struct _GpgCtx * gpg,GCancellable * cancellable,GError ** error)1430 gpg_ctx_op_step (struct _GpgCtx *gpg,
1431 GCancellable *cancellable,
1432 GError **error)
1433 {
1434 #ifndef G_OS_WIN32
1435 GPollFD polls[6];
1436 gint status, i;
1437 gboolean read_data = FALSE, wrote_data = FALSE;
1438 gboolean was_in_decrypt_stage;
1439
1440 for (i = 0; i < 6; i++) {
1441 polls[i].fd = -1;
1442 polls[i].events = 0;
1443 }
1444
1445 if (!gpg->seen_eof1) {
1446 polls[0].fd = gpg->stdout_fd;
1447 polls[0].events = G_IO_IN;
1448 }
1449
1450 if (!gpg->seen_eof2) {
1451 polls[1].fd = gpg->stderr_fd;
1452 polls[1].events = G_IO_IN;
1453 }
1454
1455 if (!gpg->complete) {
1456 polls[2].fd = gpg->status_fd;
1457 polls[2].events = G_IO_IN;
1458 }
1459
1460 polls[3].fd = gpg->stdin_fd;
1461 polls[3].events = G_IO_OUT;
1462 polls[4].fd = gpg->passwd_fd;
1463 polls[4].events = G_IO_OUT;
1464 polls[5].fd = g_cancellable_get_fd (cancellable);
1465 polls[5].events = G_IO_IN;
1466
1467 do {
1468 for (i = 0; i < 6; i++)
1469 polls[i].revents = 0;
1470 status = g_poll (polls, 6, 30 * 1000);
1471 } while (status == -1 && errno == EINTR);
1472
1473 if (status == 0)
1474 return 0; /* timed out */
1475 else if (status == -1)
1476 goto exception;
1477
1478 if ((polls[5].revents & G_IO_IN) &&
1479 g_cancellable_set_error_if_cancelled (cancellable, error)) {
1480
1481 gpg_ctx_op_cancel (gpg);
1482 return -1;
1483 }
1484
1485 /* Test each and every file descriptor to see if it's 'ready',
1486 * and if so - do what we can with it and then drop through to
1487 * the next file descriptor and so on until we've done what we
1488 * can to all of them. If one fails along the way, return
1489 * -1. */
1490
1491 was_in_decrypt_stage = gpg->in_decrypt_stage;
1492
1493 if (polls[2].revents & (G_IO_IN | G_IO_HUP)) {
1494 /* read the status message and decide what to do... */
1495 gchar buffer[4096];
1496 gssize nread;
1497
1498 d (printf ("reading from gpg's status-fd...\n"));
1499
1500 do {
1501 nread = read (gpg->status_fd, buffer, sizeof (buffer));
1502 d (printf (" read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1503 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1504 if (nread == -1)
1505 goto exception;
1506
1507 if (nread > 0) {
1508 status_backup (gpg, buffer, nread);
1509
1510 if (gpg_ctx_parse_status (gpg, error) == -1)
1511 return -1;
1512 } else {
1513 gpg->complete = TRUE;
1514 }
1515 }
1516
1517 if ((polls[0].revents & (G_IO_IN | G_IO_HUP)) && gpg->ostream) {
1518 gchar buffer[4096];
1519 gssize nread;
1520
1521 d (printf ("reading gpg's stdout...\n"));
1522
1523 do {
1524 nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1525 d (printf (" read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1526 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1527 if (nread == -1)
1528 goto exception;
1529
1530 if (nread > 0) {
1531 if (gpg->mode != GPG_CTX_MODE_DECRYPT ||
1532 gpg->in_decrypt_stage || was_in_decrypt_stage) {
1533 gboolean done = FALSE;
1534
1535 while (!done) {
1536 gsize written = camel_stream_write (
1537 gpg->ostream, buffer, (gsize)
1538 nread, cancellable, error);
1539 if (written != nread)
1540 return -1;
1541
1542 done = TRUE;
1543
1544 /* Read everything cached */
1545 do {
1546 polls[0].revents = 0;
1547 status = g_poll (polls, 1, 5);
1548 } while (status == -1 && errno == EINTR);
1549
1550 if (status != -1 && status != 0 && (polls[0].revents & (G_IO_IN | G_IO_HUP))) {
1551 do {
1552 nread = read (gpg->stdout_fd, buffer, sizeof (buffer));
1553 d (printf (" cached read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1554 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1555
1556 if (nread == -1)
1557 goto exception;
1558
1559 done = !nread;
1560 }
1561 }
1562 } else {
1563 if (!gpg->decrypt_extra_text)
1564 gpg->decrypt_extra_text = g_string_new ("");
1565 g_string_append_len (gpg->decrypt_extra_text, buffer, nread);
1566 }
1567 } else {
1568 gpg->seen_eof1 = TRUE;
1569 }
1570
1571 read_data = TRUE;
1572 }
1573
1574 if (polls[1].revents & (G_IO_IN | G_IO_HUP)) {
1575 gchar buffer[4096];
1576 gssize nread;
1577
1578 d (printf ("reading gpg's stderr...\n"));
1579
1580 do {
1581 nread = read (gpg->stderr_fd, buffer, sizeof (buffer));
1582 d (printf (" read %d bytes (%.*s)\n", (gint) nread, (gint) nread, buffer));
1583 } while (nread == -1 && (errno == EINTR || errno == EAGAIN));
1584 if (nread == -1)
1585 goto exception;
1586
1587 if (nread > 0) {
1588 camel_stream_write (
1589 gpg->diagnostics, buffer,
1590 nread, cancellable, error);
1591 } else {
1592 gpg->seen_eof2 = TRUE;
1593 }
1594 }
1595
1596 if ((polls[4].revents & (G_IO_OUT | G_IO_HUP)) && gpg->need_passwd && gpg->send_passwd) {
1597 gssize w, nwritten = 0;
1598 gsize n;
1599
1600 d (printf ("sending gpg our passphrase...\n"));
1601
1602 /* send the passphrase to gpg */
1603 n = strlen (gpg->passwd);
1604 do {
1605 do {
1606 w = write (gpg->passwd_fd, gpg->passwd + nwritten, n - nwritten);
1607 } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1608
1609 if (w > 0)
1610 nwritten += w;
1611 } while (nwritten < n && w != -1);
1612
1613 /* zero and free our passwd buffer */
1614 memset (gpg->passwd, 0, n);
1615 g_free (gpg->passwd);
1616 gpg->passwd = NULL;
1617
1618 if (w == -1)
1619 goto exception;
1620
1621 gpg->send_passwd = FALSE;
1622 }
1623
1624 if ((polls[3].revents & (G_IO_OUT | G_IO_HUP)) && gpg->istream) {
1625 gchar buffer[4096];
1626 gssize nread;
1627
1628 d (printf ("writing to gpg's stdin...\n"));
1629
1630 /* write our stream to gpg's stdin */
1631 nread = camel_stream_read (
1632 gpg->istream, buffer,
1633 sizeof (buffer), cancellable, NULL);
1634 if (nread > 0) {
1635 gssize w, nwritten = 0;
1636
1637 do {
1638 do {
1639 w = write (gpg->stdin_fd, buffer + nwritten, nread - nwritten);
1640 } while (w == -1 && (errno == EINTR || errno == EAGAIN));
1641
1642 if (w > 0)
1643 nwritten += w;
1644 } while (nwritten < nread && w != -1);
1645
1646 if (w == -1)
1647 goto exception;
1648
1649 d (printf ("wrote %d (out of %d) bytes to gpg's stdin\n", (gint) nwritten, (gint) nread));
1650 wrote_data = TRUE;
1651 }
1652
1653 if (camel_stream_eos (gpg->istream)) {
1654 d (printf ("closing gpg's stdin\n"));
1655 close (gpg->stdin_fd);
1656 gpg->stdin_fd = -1;
1657 }
1658 }
1659
1660 if (gpg->need_id && !gpg->processing && !read_data && !wrote_data) {
1661 /* do not ask more than hundred times per second when looking for a pass phrase,
1662 * in case user has the use-agent set, it'll not use the all CPU when
1663 * agent is asking for a pass phrase, instead of us */
1664 g_usleep (G_USEC_PER_SEC / 100);
1665 }
1666
1667 return 0;
1668
1669 exception:
1670 /* always called on an i/o error */
1671 g_set_error (
1672 error, G_IO_ERROR,
1673 g_io_error_from_errno (errno),
1674 _("Failed to execute gpg: %s"), g_strerror (errno));
1675 gpg_ctx_op_cancel (gpg);
1676 #endif
1677 return -1;
1678 }
1679
1680 static gboolean
gpg_ctx_op_complete(struct _GpgCtx * gpg)1681 gpg_ctx_op_complete (struct _GpgCtx *gpg)
1682 {
1683 return gpg->complete && gpg->seen_eof1 && gpg->seen_eof2;}
1684
1685 #if 0
1686 static gboolean
1687 gpg_ctx_op_exited (struct _GpgCtx *gpg)
1688 {
1689 pid_t retval;
1690 gint status;
1691
1692 if (gpg->exited)
1693 return TRUE;
1694
1695 retval = waitpid (gpg->pid, &status, WNOHANG);
1696 if (retval == gpg->pid) {
1697 gpg->exit_status = status;
1698 gpg->exited = TRUE;
1699 return TRUE;
1700 }
1701
1702 return FALSE;
1703 }
1704 #endif
1705
1706 static gint
gpg_ctx_op_wait(struct _GpgCtx * gpg)1707 gpg_ctx_op_wait (struct _GpgCtx *gpg)
1708 {
1709 #ifndef G_OS_WIN32
1710 sigset_t mask, omask;
1711 pid_t retval;
1712 gint status;
1713
1714 if (!gpg->exited) {
1715 sigemptyset (&mask);
1716 sigaddset (&mask, SIGALRM);
1717 sigprocmask (SIG_BLOCK, &mask, &omask);
1718 alarm (1);
1719 retval = waitpid (gpg->pid, &status, 0);
1720 alarm (0);
1721 sigprocmask (SIG_SETMASK, &omask, NULL);
1722
1723 if (retval == (pid_t) -1 && errno == EINTR) {
1724 /* The child is hanging: send a friendly reminder. */
1725 kill (gpg->pid, SIGTERM);
1726 sleep (1);
1727 retval = waitpid (gpg->pid, &status, WNOHANG);
1728 if (retval == (pid_t) 0) {
1729 /* Still hanging; use brute force. */
1730 kill (gpg->pid, SIGKILL);
1731 sleep (1);
1732 retval = waitpid (gpg->pid, &status, WNOHANG);
1733 }
1734 }
1735 } else {
1736 status = gpg->exit_status;
1737 retval = gpg->pid;
1738 }
1739
1740 if (retval != (pid_t) -1 && WIFEXITED (status))
1741 return WEXITSTATUS (status);
1742 else
1743 return -1;
1744 #else
1745 return -1;
1746 #endif
1747 }
1748
1749 static gchar *
swrite(CamelMimePart * sigpart,GCancellable * cancellable,GError ** error)1750 swrite (CamelMimePart *sigpart,
1751 GCancellable *cancellable,
1752 GError **error)
1753 {
1754 GFile *file;
1755 GFileIOStream *base_stream = NULL;
1756 CamelStream *stream = NULL;
1757 CamelDataWrapper *wrapper;
1758 gchar *path = NULL;
1759 gint ret;
1760
1761 file = g_file_new_tmp ("evolution-pgp.XXXXXX", &base_stream, error);
1762
1763 /* Sanity check. */
1764 g_return_val_if_fail (
1765 ((file != NULL) && (base_stream != NULL)) ||
1766 ((file == NULL) && (base_stream == NULL)), NULL);
1767
1768 if (base_stream != NULL) {
1769 stream = camel_stream_new (G_IO_STREAM (base_stream));
1770 g_object_unref (base_stream);
1771 }
1772
1773 if (stream == NULL)
1774 return NULL;
1775
1776 wrapper = camel_medium_get_content (CAMEL_MEDIUM (sigpart));
1777 if (wrapper == NULL)
1778 wrapper = CAMEL_DATA_WRAPPER (sigpart);
1779
1780 ret = camel_data_wrapper_decode_to_stream_sync (
1781 wrapper, stream, cancellable, error);
1782 if (ret != -1) {
1783 ret = camel_stream_flush (stream, cancellable, error);
1784 if (ret != -1)
1785 ret = camel_stream_close (stream, cancellable, error);
1786 }
1787
1788 if (ret != -1)
1789 path = g_file_get_path (file);
1790
1791 g_object_unref (file);
1792 g_object_unref (stream);
1793
1794 return path;
1795 }
1796
1797 static const gchar *
gpg_context_find_photo(GHashTable * photos,GHashTable * signers_keyid,const gchar * name,const gchar * email)1798 gpg_context_find_photo (GHashTable *photos, /* keyid ~> filename in tmp */
1799 GHashTable *signers_keyid, /* signer ~> keyid */
1800 const gchar *name,
1801 const gchar *email)
1802 {
1803 GHashTableIter iter;
1804 gpointer key, value;
1805 const gchar *keyid = NULL;
1806
1807 if (!photos || !signers_keyid || ((!name || !*name) && (!email || !*email)))
1808 return NULL;
1809
1810 g_hash_table_iter_init (&iter, signers_keyid);
1811 while (g_hash_table_iter_next (&iter, &key, &value)) {
1812 if ((email && *email && strstr (key, email)) ||
1813 (name && *name && strstr (key, name))) {
1814 keyid = value;
1815 break;
1816 }
1817 }
1818
1819 if (keyid) {
1820 const gchar *filename;
1821
1822 filename = g_hash_table_lookup (photos, keyid);
1823 if (filename)
1824 return camel_pstring_strdup (filename);
1825 }
1826
1827 return NULL;
1828 }
1829
1830 static void
camel_gpg_context_free_photo_filename(gpointer ptr)1831 camel_gpg_context_free_photo_filename (gpointer ptr)
1832 {
1833 gchar *tmp_filename = g_strdup (ptr);
1834
1835 camel_pstring_free (ptr);
1836
1837 if (!camel_pstring_contains (tmp_filename) &&
1838 g_file_test (tmp_filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1839 g_unlink (tmp_filename);
1840 }
1841
1842 g_free (tmp_filename);
1843 }
1844
1845 static gpointer
camel_gpg_context_clone_photo_filename(gpointer ptr)1846 camel_gpg_context_clone_photo_filename (gpointer ptr)
1847 {
1848 return (gpointer) camel_pstring_strdup (ptr);
1849 }
1850
1851 static CamelInternetAddress *
gpg_ctx_extract_email_addresses_from_text(const gchar * text)1852 gpg_ctx_extract_email_addresses_from_text (const gchar *text)
1853 {
1854 CamelInternetAddress *signers_alt_emails = NULL;
1855 const gchar *lt = NULL, *gt = NULL, *bs = NULL, *be = NULL;
1856 gssize ii;
1857
1858 for (ii = 0; text && text[ii]; ii++) {
1859 switch (text[ii]) {
1860 case '<':
1861 lt = text + ii;
1862 gt = NULL;
1863 bs = NULL;
1864 be = NULL;
1865 break;
1866 case '>':
1867 if (lt) {
1868 gt = text + ii;
1869 bs = NULL;
1870 be = NULL;
1871 }
1872 break;
1873 case '[':
1874 if (lt && gt) {
1875 bs = text + ii;
1876 be = NULL;
1877 }
1878 break;
1879 case ']':
1880 if (lt && gt && bs) {
1881 be = text + ii;
1882 }
1883 break;
1884 case '\n':
1885 if (lt && lt < gt && gt < bs && bs < be) {
1886 gchar *email = g_strndup (lt + 1, gt - lt - 1);
1887
1888 if (!signers_alt_emails)
1889 signers_alt_emails = camel_internet_address_new ();
1890
1891 camel_internet_address_add (signers_alt_emails, NULL, email);
1892 g_free (email);
1893 }
1894 lt = NULL;
1895 gt = NULL;
1896 bs = NULL;
1897 be = NULL;
1898 break;
1899 default:
1900 break;
1901 }
1902 }
1903
1904 if (lt && lt < gt && gt < bs && bs < be) {
1905 gchar *email = g_strndup (lt + 1, gt - lt - 1);
1906
1907 if (!signers_alt_emails)
1908 signers_alt_emails = camel_internet_address_new ();
1909
1910 camel_internet_address_add (signers_alt_emails, NULL, email);
1911 g_free (email);
1912 }
1913
1914 return signers_alt_emails;
1915 }
1916
1917 static void
add_signers(CamelCipherValidity * validity,const gchar * diagnostics,const GString * signers,GHashTable * signers_keyid,const gchar * photos_filename)1918 add_signers (CamelCipherValidity *validity,
1919 const gchar *diagnostics,
1920 const GString *signers,
1921 GHashTable *signers_keyid,
1922 const gchar *photos_filename)
1923 {
1924 CamelInternetAddress *address;
1925 GHashTable *photos = NULL;
1926 gint i, count;
1927
1928 g_return_if_fail (validity != NULL);
1929
1930 if (!signers || !signers->str || !*signers->str)
1931 return;
1932
1933 address = camel_internet_address_new ();
1934 g_return_if_fail (address != NULL);
1935
1936 if (photos_filename) {
1937 /* A short file is expected */
1938 gchar *content = NULL;
1939 GError *error = NULL;
1940
1941 if (g_file_get_contents (photos_filename, &content, NULL, &error)) {
1942 gchar **lines;
1943 gint ii;
1944
1945 /* Each line is encoded as: KeyID\tPhotoFilename */
1946 lines = g_strsplit (content, "\n", -1);
1947
1948 for (ii = 0; lines && lines[ii]; ii++) {
1949 gchar *line, *filename;
1950
1951 line = lines[ii];
1952 filename = strchr (line, '\t');
1953
1954 if (filename) {
1955 *filename = '\0';
1956 filename++;
1957 }
1958
1959 if (filename && g_file_test (filename, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
1960 if (!photos)
1961 photos = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, camel_gpg_context_free_photo_filename);
1962
1963 g_hash_table_insert (photos, g_strdup (line), (gpointer) camel_pstring_strdup (filename));
1964 }
1965 }
1966
1967 g_strfreev (lines);
1968 } else {
1969 g_warning ("CamelGPGContext: Failed to open photos file '%s': %s", photos_filename, error ? error->message : "Unknown error");
1970 }
1971
1972 g_free (content);
1973 g_clear_error (&error);
1974 }
1975
1976 count = camel_address_decode (CAMEL_ADDRESS (address), signers->str);
1977 for (i = 0; i < count; i++) {
1978 const gchar *name = NULL, *email = NULL;
1979 const gchar *photo_filename; /* allocated on the string pool */
1980 gint index;
1981
1982 if (!camel_internet_address_get (address, i, &name, &email))
1983 break;
1984
1985 photo_filename = gpg_context_find_photo (photos, signers_keyid, name, email);
1986 index = camel_cipher_validity_add_certinfo (validity, CAMEL_CIPHER_VALIDITY_SIGN, name, email);
1987
1988 if (index != -1 && photo_filename) {
1989 camel_cipher_validity_set_certinfo_property (validity, CAMEL_CIPHER_VALIDITY_SIGN, index,
1990 CAMEL_CIPHER_CERT_INFO_PROPERTY_PHOTO_FILENAME, (gpointer) photo_filename,
1991 camel_gpg_context_free_photo_filename, camel_gpg_context_clone_photo_filename);
1992 } else if (photo_filename) {
1993 camel_gpg_context_free_photo_filename ((gpointer) photo_filename);
1994 }
1995 }
1996
1997 if (photos)
1998 g_hash_table_destroy (photos);
1999 g_object_unref (address);
2000
2001 if (diagnostics && !g_queue_is_empty (&validity->sign.signers)) {
2002 CamelInternetAddress *signers_alt_emails;
2003
2004 signers_alt_emails = gpg_ctx_extract_email_addresses_from_text (diagnostics);
2005 if (signers_alt_emails && camel_address_length (CAMEL_ADDRESS (signers_alt_emails)) > 1) {
2006 gchar *addresses = camel_address_format (CAMEL_ADDRESS (signers_alt_emails));
2007
2008 camel_cipher_validity_set_certinfo_property (validity, CAMEL_CIPHER_VALIDITY_SIGN, 0,
2009 CAMEL_CIPHER_CERT_INFO_PROPERTY_SIGNERS_ALT_EMAILS, addresses,
2010 g_free, (CamelCipherCloneFunc) g_strdup);
2011 }
2012
2013 g_clear_object (&signers_alt_emails);
2014 }
2015 }
2016
2017 /* ********************************************************************** */
2018
2019 static void
gpg_context_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)2020 gpg_context_set_property (GObject *object,
2021 guint property_id,
2022 const GValue *value,
2023 GParamSpec *pspec)
2024 {
2025 switch (property_id) {
2026 case PROP_ALWAYS_TRUST:
2027 camel_gpg_context_set_always_trust (
2028 CAMEL_GPG_CONTEXT (object),
2029 g_value_get_boolean (value));
2030 return;
2031
2032 case PROP_PREFER_INLINE:
2033 camel_gpg_context_set_prefer_inline (
2034 CAMEL_GPG_CONTEXT (object),
2035 g_value_get_boolean (value));
2036 return;
2037 }
2038
2039 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2040 }
2041
2042 static void
gpg_context_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)2043 gpg_context_get_property (GObject *object,
2044 guint property_id,
2045 GValue *value,
2046 GParamSpec *pspec)
2047 {
2048 switch (property_id) {
2049 case PROP_ALWAYS_TRUST:
2050 g_value_set_boolean (
2051 value,
2052 camel_gpg_context_get_always_trust (
2053 CAMEL_GPG_CONTEXT (object)));
2054 return;
2055
2056 case PROP_PREFER_INLINE:
2057 g_value_set_boolean (
2058 value,
2059 camel_gpg_context_get_prefer_inline (
2060 CAMEL_GPG_CONTEXT (object)));
2061 return;
2062 }
2063
2064 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
2065 }
2066
2067 static const gchar *
gpg_hash_to_id(CamelCipherContext * context,CamelCipherHash hash)2068 gpg_hash_to_id (CamelCipherContext *context,
2069 CamelCipherHash hash)
2070 {
2071 switch (hash) {
2072 case CAMEL_CIPHER_HASH_MD2:
2073 return "pgp-md2";
2074 case CAMEL_CIPHER_HASH_MD5:
2075 return "pgp-md5";
2076 case CAMEL_CIPHER_HASH_SHA1:
2077 return "pgp-sha1";
2078 case CAMEL_CIPHER_HASH_DEFAULT:
2079 case CAMEL_CIPHER_HASH_SHA256:
2080 return "pgp-sha256";
2081 case CAMEL_CIPHER_HASH_SHA384:
2082 return "pgp-sha384";
2083 case CAMEL_CIPHER_HASH_SHA512:
2084 return "pgp-sha512";
2085 case CAMEL_CIPHER_HASH_RIPEMD160:
2086 return "pgp-ripemd160";
2087 case CAMEL_CIPHER_HASH_TIGER192:
2088 return "pgp-tiger192";
2089 case CAMEL_CIPHER_HASH_HAVAL5160:
2090 return "pgp-haval-5-160";
2091 }
2092
2093 return NULL;
2094 }
2095
2096 static CamelCipherHash
gpg_id_to_hash(CamelCipherContext * context,const gchar * id)2097 gpg_id_to_hash (CamelCipherContext *context,
2098 const gchar *id)
2099 {
2100 if (id) {
2101 if (!strcmp (id, "pgp-md2"))
2102 return CAMEL_CIPHER_HASH_MD2;
2103 else if (!strcmp (id, "pgp-md5"))
2104 return CAMEL_CIPHER_HASH_MD5;
2105 else if (!strcmp (id, "pgp-sha1"))
2106 return CAMEL_CIPHER_HASH_SHA1;
2107 else if (!strcmp (id, "pgp-sha256"))
2108 return CAMEL_CIPHER_HASH_SHA256;
2109 else if (!strcmp (id, "pgp-sha384"))
2110 return CAMEL_CIPHER_HASH_SHA384;
2111 else if (!strcmp (id, "pgp-sha512"))
2112 return CAMEL_CIPHER_HASH_SHA512;
2113 else if (!strcmp (id, "pgp-ripemd160"))
2114 return CAMEL_CIPHER_HASH_RIPEMD160;
2115 else if (!strcmp (id, "tiger192"))
2116 return CAMEL_CIPHER_HASH_TIGER192;
2117 else if (!strcmp (id, "haval-5-160"))
2118 return CAMEL_CIPHER_HASH_HAVAL5160;
2119 }
2120
2121 return CAMEL_CIPHER_HASH_DEFAULT;
2122 }
2123
2124 static gboolean
gpg_context_decode_to_stream(CamelMimePart * part,CamelStream * stream,GCancellable * cancellable,GError ** error)2125 gpg_context_decode_to_stream (CamelMimePart *part,
2126 CamelStream *stream,
2127 GCancellable *cancellable,
2128 GError **error)
2129 {
2130 CamelDataWrapper *wrapper;
2131
2132 wrapper = camel_medium_get_content (CAMEL_MEDIUM (part));
2133
2134 /* This is when encrypting already signed part */
2135 if (CAMEL_IS_MIME_PART (wrapper))
2136 wrapper = camel_medium_get_content (CAMEL_MEDIUM (wrapper));
2137
2138 if (camel_data_wrapper_decode_to_stream_sync (wrapper, stream, cancellable, error) == -1 ||
2139 camel_stream_flush (stream, cancellable, error) == -1)
2140 return FALSE;
2141
2142 /* Reset stream position to beginning. */
2143 if (G_IS_SEEKABLE (stream))
2144 g_seekable_seek (G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
2145
2146 return TRUE;
2147 }
2148
2149 static gboolean
gpg_sign_sync(CamelCipherContext * context,const gchar * userid,CamelCipherHash hash,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2150 gpg_sign_sync (CamelCipherContext *context,
2151 const gchar *userid,
2152 CamelCipherHash hash,
2153 CamelMimePart *ipart,
2154 CamelMimePart *opart,
2155 GCancellable *cancellable,
2156 GError **error)
2157 {
2158 struct _GpgCtx *gpg = NULL;
2159 CamelCipherContextClass *class;
2160 CamelGpgContext *ctx = (CamelGpgContext *) context;
2161 CamelStream *ostream = camel_stream_mem_new (), *istream;
2162 CamelDataWrapper *dw;
2163 CamelContentType *ct;
2164 CamelMimePart *sigpart;
2165 CamelMultipartSigned *mps;
2166 gboolean success = FALSE;
2167 gboolean prefer_inline;
2168
2169 /* Note: see rfc2015 or rfc3156, section 5 */
2170
2171 class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2172
2173 prefer_inline = ctx->priv->prefer_inline &&
2174 camel_content_type_is (camel_mime_part_get_content_type (ipart), "text", "plain");
2175
2176 /* FIXME: stream this, we stream output at least */
2177 istream = camel_stream_mem_new ();
2178 if ((prefer_inline && !gpg_context_decode_to_stream (ipart, istream, cancellable, error)) ||
2179 (!prefer_inline && camel_cipher_canonical_to_stream (
2180 ipart, CAMEL_MIME_FILTER_CANON_STRIP |
2181 CAMEL_MIME_FILTER_CANON_CRLF |
2182 CAMEL_MIME_FILTER_CANON_FROM,
2183 istream, NULL, error) == -1)) {
2184 g_prefix_error (
2185 error, _("Could not generate signing data: "));
2186 goto fail;
2187 }
2188
2189 #ifdef GPG_LOG
2190 if (camel_debug_start ("gpg:sign")) {
2191 gchar *name;
2192 CamelStream *out;
2193
2194 name = g_strdup_printf ("camel-gpg.%d.sign-data", logid++);
2195 out = camel_stream_fs_new_with_name (
2196 name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2197 if (out) {
2198 printf ("Writing gpg signing data to '%s'\n", name);
2199 camel_stream_write_to_stream (istream, out, NULL, NULL);
2200 g_seekable_seek (
2201 G_SEEKABLE (istream), 0,
2202 G_SEEK_SET, NULL, NULL);
2203 g_object_unref (out);
2204 }
2205 g_free (name);
2206 camel_debug_end ();
2207 }
2208 #endif
2209
2210 gpg = gpg_ctx_new (context, cancellable);
2211 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_SIGN);
2212 gpg_ctx_set_hash (gpg, hash);
2213 gpg_ctx_set_armor (gpg, TRUE);
2214 gpg_ctx_set_userid (gpg, userid);
2215 gpg_ctx_set_istream (gpg, istream);
2216 gpg_ctx_set_ostream (gpg, ostream);
2217 gpg_ctx_set_prefer_inline (gpg, prefer_inline);
2218
2219 if (!gpg_ctx_op_start (gpg, error))
2220 goto fail;
2221
2222 while (!gpg_ctx_op_complete (gpg)) {
2223 if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2224 gpg_ctx_op_cancel (gpg);
2225 goto fail;
2226 }
2227 }
2228
2229 if (gpg_ctx_op_wait (gpg) != 0) {
2230 const gchar *diagnostics;
2231
2232 diagnostics = gpg_ctx_get_diagnostics (gpg);
2233 g_set_error (
2234 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2235 (diagnostics != NULL && *diagnostics != '\0') ?
2236 diagnostics : _("Failed to execute gpg."));
2237
2238 goto fail;
2239 }
2240
2241 success = TRUE;
2242
2243 dw = camel_data_wrapper_new ();
2244 g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2245 camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
2246
2247 if (gpg->prefer_inline) {
2248 CamelTransferEncoding encoding;
2249
2250 encoding = camel_mime_part_get_encoding (ipart);
2251
2252 if (encoding != CAMEL_TRANSFER_ENCODING_BASE64 &&
2253 encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
2254 encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
2255
2256 sigpart = camel_mime_part_new ();
2257 ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (ipart));
2258 camel_data_wrapper_set_mime_type_field (dw, ct);
2259
2260 camel_mime_part_set_encoding (sigpart, encoding);
2261 camel_medium_set_content ((CamelMedium *) sigpart, dw);
2262 g_object_unref (dw);
2263
2264 camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) sigpart);
2265
2266 g_object_unref (sigpart);
2267 } else {
2268 sigpart = camel_mime_part_new ();
2269 ct = camel_content_type_new ("application", "pgp-signature");
2270 camel_content_type_set_param (ct, "name", "signature.asc");
2271 camel_data_wrapper_set_mime_type_field (dw, ct);
2272 camel_content_type_unref (ct);
2273
2274 camel_medium_set_content ((CamelMedium *) sigpart, dw);
2275 g_object_unref (dw);
2276
2277 camel_mime_part_set_description (sigpart, "This is a digitally signed message part");
2278
2279 mps = camel_multipart_signed_new ();
2280 ct = camel_content_type_new ("multipart", "signed");
2281 camel_content_type_set_param (ct, "micalg", camel_cipher_context_hash_to_id (context, hash == CAMEL_CIPHER_HASH_DEFAULT ? gpg->hash : hash));
2282 camel_content_type_set_param (ct, "protocol", class->sign_protocol);
2283 camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) mps, ct);
2284 camel_content_type_unref (ct);
2285 camel_multipart_set_boundary ((CamelMultipart *) mps, NULL);
2286
2287 camel_multipart_signed_set_signature (mps, sigpart);
2288 camel_multipart_signed_set_content_stream (mps, istream);
2289
2290 g_object_unref (sigpart);
2291
2292 camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) mps);
2293
2294 g_object_unref (mps);
2295 }
2296
2297 g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2298
2299 fail:
2300 g_object_unref (ostream);
2301
2302 if (gpg)
2303 gpg_ctx_free (gpg);
2304
2305 return success;
2306 }
2307
2308 static CamelCipherValidity *
gpg_verify_sync(CamelCipherContext * context,CamelMimePart * ipart,GCancellable * cancellable,GError ** error)2309 gpg_verify_sync (CamelCipherContext *context,
2310 CamelMimePart *ipart,
2311 GCancellable *cancellable,
2312 GError **error)
2313 {
2314 CamelCipherContextClass *class;
2315 CamelCipherValidity *validity;
2316 const gchar *diagnostics = NULL;
2317 struct _GpgCtx *gpg = NULL;
2318 gchar *sigfile = NULL;
2319 CamelContentType *ct;
2320 CamelMimePart *sigpart;
2321 CamelStream *istream = NULL, *canon_stream;
2322 CamelMultipart *mps;
2323 CamelStream *filter;
2324 CamelMimeFilter *canon;
2325 gboolean is_retry = FALSE;
2326
2327 class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2328
2329 mps = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) ipart);
2330 ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mps));
2331
2332 /* Inline signature (using our fake mime type) or PGP/Mime signature */
2333 if (camel_content_type_is (ct, "multipart", "signed")) {
2334 /* PGP/Mime Signature */
2335 const gchar *tmp;
2336
2337 tmp = camel_content_type_param (ct, "protocol");
2338 if (!CAMEL_IS_MULTIPART_SIGNED (mps)
2339 || tmp == NULL
2340 || g_ascii_strcasecmp (tmp, class->sign_protocol) != 0) {
2341 g_set_error (
2342 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2343 _("Cannot verify message signature: "
2344 "Incorrect message format"));
2345 return NULL;
2346 }
2347
2348 if (!(istream = camel_multipart_signed_get_content_stream ((CamelMultipartSigned *) mps, NULL))) {
2349 g_set_error (
2350 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2351 _("Cannot verify message signature: "
2352 "Incorrect message format"));
2353 return NULL;
2354 }
2355
2356 if (!(sigpart = camel_multipart_get_part (mps, CAMEL_MULTIPART_SIGNED_SIGNATURE))) {
2357 g_set_error (
2358 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2359 _("Cannot verify message signature: "
2360 "Incorrect message format"));
2361 g_object_unref (istream);
2362 return NULL;
2363 }
2364 } else if (camel_content_type_is (ct, "application", "x-inlinepgp-signed")) {
2365 /* Inline Signed */
2366 CamelDataWrapper *content;
2367 content = camel_medium_get_content ((CamelMedium *) ipart);
2368 istream = camel_stream_mem_new ();
2369 if (!camel_data_wrapper_decode_to_stream_sync (
2370 content, istream, cancellable, error))
2371 goto exception;
2372 g_seekable_seek (
2373 G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2374 sigpart = NULL;
2375 } else {
2376 /* Invalid Mimetype */
2377 g_set_error (
2378 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2379 _("Cannot verify message signature: "
2380 "Incorrect message format"));
2381 return NULL;
2382 }
2383
2384 /* Now start the real work of verifying the message */
2385 #ifdef GPG_LOG
2386 if (camel_debug_start ("gpg:sign")) {
2387 gchar *name;
2388 CamelStream *out;
2389
2390 name = g_strdup_printf ("camel-gpg.%d.verify.data", logid);
2391 out = camel_stream_fs_new_with_name (
2392 name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2393 if (out) {
2394 printf ("Writing gpg verify data to '%s'\n", name);
2395 camel_stream_write_to_stream (istream, out, NULL, NULL);
2396 g_seekable_seek (
2397 G_SEEKABLE (istream),
2398 0, G_SEEK_SET, NULL, NULL);
2399 g_object_unref (out);
2400 }
2401
2402 g_free (name);
2403
2404 if (sigpart) {
2405 name = g_strdup_printf ("camel-gpg.%d.verify.signature", logid++);
2406 out = camel_stream_fs_new_with_name (
2407 name, O_CREAT | O_TRUNC | O_WRONLY, 0666, NULL);
2408 if (out) {
2409 printf ("Writing gpg verify signature to '%s'\n", name);
2410 camel_data_wrapper_write_to_stream_sync (
2411 CAMEL_DATA_WRAPPER (sigpart),
2412 out, NULL, NULL);
2413 g_object_unref (out);
2414 }
2415 g_free (name);
2416 }
2417 camel_debug_end ();
2418 }
2419 #endif
2420
2421 if (sigpart) {
2422 sigfile = swrite (sigpart, cancellable, error);
2423 if (sigfile == NULL) {
2424 g_prefix_error (
2425 error, _("Cannot verify message signature: "));
2426 goto exception;
2427 }
2428 }
2429
2430 retry:
2431 g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2432
2433 canon_stream = camel_stream_mem_new ();
2434
2435 /* strip trailing white-spaces */
2436 filter = camel_stream_filter_new (istream);
2437 canon = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_CRLF | (is_retry ? 0 : CAMEL_MIME_FILTER_CANON_STRIP));
2438 camel_stream_filter_add (CAMEL_STREAM_FILTER (filter), canon);
2439 g_object_unref (canon);
2440
2441 camel_stream_write_to_stream (filter, canon_stream, NULL, NULL);
2442
2443 g_object_unref (filter);
2444
2445 g_seekable_seek (G_SEEKABLE (canon_stream), 0, G_SEEK_SET, NULL, NULL);
2446
2447 gpg = gpg_ctx_new (context, cancellable);
2448 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_VERIFY);
2449 gpg_ctx_set_load_photos (gpg, camel_cipher_can_load_photos ());
2450 if (sigfile)
2451 gpg_ctx_set_sigfile (gpg, sigfile);
2452 gpg_ctx_set_istream (gpg, canon_stream);
2453
2454 if (!gpg_ctx_op_start (gpg, error)) {
2455 g_object_unref (canon_stream);
2456 goto exception;
2457 }
2458
2459 while (!gpg_ctx_op_complete (gpg)) {
2460 if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2461 gpg_ctx_op_cancel (gpg);
2462 g_object_unref (canon_stream);
2463 goto exception;
2464 }
2465 }
2466
2467 /* report error only when no data or didn't found signature */
2468 if (gpg_ctx_op_wait (gpg) != 0 && (gpg->nodata || !gpg->hadsig)) {
2469 const gchar *diagnostics;
2470
2471 diagnostics = gpg_ctx_get_diagnostics (gpg);
2472 g_set_error (
2473 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2474 (diagnostics != NULL && *diagnostics != '\0') ?
2475 diagnostics : _("Failed to execute gpg."));
2476
2477 g_object_unref (canon_stream);
2478
2479 goto exception;
2480 }
2481
2482 if (!is_retry && !gpg->validsig && !gpg->nopubkey) {
2483 /* Retry without stripping trailing spaces */
2484 is_retry = TRUE;
2485 gpg_ctx_free (gpg);
2486 g_object_unref (canon_stream);
2487 goto retry;
2488 }
2489
2490 validity = camel_cipher_validity_new ();
2491 diagnostics = gpg_ctx_get_diagnostics (gpg);
2492 camel_cipher_validity_set_description (validity, diagnostics);
2493 if (gpg->validsig) {
2494 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE || gpg->trust == GPG_TRUST_MARGINAL)
2495 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
2496 else if (gpg->trust != GPG_TRUST_NEVER)
2497 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
2498 else
2499 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2500 } else if (gpg->nopubkey) {
2501 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
2502 } else {
2503 validity->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2504 }
2505
2506 add_signers (validity, diagnostics, gpg->signers, gpg->signers_keyid, gpg->photos_filename);
2507
2508 gpg_ctx_free (gpg);
2509
2510 if (sigfile) {
2511 g_unlink (sigfile);
2512 g_free (sigfile);
2513 }
2514
2515 g_object_unref (canon_stream);
2516 g_clear_object (&istream);
2517
2518 return validity;
2519
2520 exception:
2521
2522 if (gpg != NULL)
2523 gpg_ctx_free (gpg);
2524
2525 if (istream)
2526 g_object_unref (istream);
2527
2528 if (sigfile) {
2529 g_unlink (sigfile);
2530 g_free (sigfile);
2531 }
2532
2533 return NULL;
2534 }
2535
2536 static gboolean
gpg_encrypt_sync(CamelCipherContext * context,const gchar * userid,GPtrArray * recipients,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2537 gpg_encrypt_sync (CamelCipherContext *context,
2538 const gchar *userid,
2539 GPtrArray *recipients,
2540 CamelMimePart *ipart,
2541 CamelMimePart *opart,
2542 GCancellable *cancellable,
2543 GError **error)
2544 {
2545 CamelCipherContextClass *class;
2546 CamelGpgContext *ctx = (CamelGpgContext *) context;
2547 struct _GpgCtx *gpg;
2548 CamelStream *istream, *ostream, *vstream;
2549 CamelMimePart *encpart, *verpart;
2550 CamelDataWrapper *dw;
2551 CamelContentType *ct;
2552 CamelMultipartEncrypted *mpe;
2553 gboolean success = FALSE;
2554 gboolean prefer_inline;
2555 GSList *gathered_keys = NULL, *link;
2556 gint i;
2557
2558 class = CAMEL_CIPHER_CONTEXT_GET_CLASS (context);
2559
2560 if (!camel_session_get_recipient_certificates_sync (camel_cipher_context_get_session (context),
2561 CAMEL_RECIPIENT_CERTIFICATE_PGP, recipients, &gathered_keys, cancellable, error))
2562 return FALSE;
2563
2564 prefer_inline = ctx->priv->prefer_inline &&
2565 camel_content_type_is (camel_mime_part_get_content_type (ipart), "text", "plain");
2566
2567 ostream = camel_stream_mem_new ();
2568 istream = camel_stream_mem_new ();
2569 if ((prefer_inline && !gpg_context_decode_to_stream (ipart, istream, cancellable, error)) ||
2570 (!prefer_inline && camel_cipher_canonical_to_stream (
2571 ipart, CAMEL_MIME_FILTER_CANON_CRLF, istream, NULL, error) == -1)) {
2572 g_prefix_error (
2573 error, _("Could not generate encrypting data: "));
2574 goto fail1;
2575 }
2576
2577 gpg = gpg_ctx_new (context, cancellable);
2578 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_ENCRYPT);
2579 gpg_ctx_set_armor (gpg, TRUE);
2580 gpg_ctx_set_userid (gpg, userid);
2581 gpg_ctx_set_istream (gpg, istream);
2582 gpg_ctx_set_ostream (gpg, ostream);
2583 gpg_ctx_set_always_trust (gpg, ctx->priv->always_trust);
2584 gpg_ctx_set_prefer_inline (gpg, prefer_inline);
2585
2586 if (gathered_keys && g_slist_length (gathered_keys) != recipients->len) {
2587 g_slist_free_full (gathered_keys, g_free);
2588 gathered_keys = NULL;
2589 }
2590
2591 for (link = gathered_keys, i = 0; i < recipients->len; i++) {
2592 gpg_ctx_add_recipient (gpg, recipients->pdata[i], link ? link->data : NULL);
2593 link = g_slist_next (link);
2594 }
2595
2596 if (!gpg_ctx_op_start (gpg, error))
2597 goto fail;
2598
2599 /* FIXME: move this to a common routine */
2600 while (!gpg_ctx_op_complete (gpg)) {
2601 if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2602 gpg_ctx_op_cancel (gpg);
2603 goto fail;
2604 }
2605 }
2606
2607 if (gpg_ctx_op_wait (gpg) != 0) {
2608 const gchar *diagnostics;
2609
2610 diagnostics = gpg_ctx_get_diagnostics (gpg);
2611 g_set_error (
2612 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2613 (diagnostics != NULL && *diagnostics != '\0') ?
2614 diagnostics : _("Failed to execute gpg."));
2615 goto fail;
2616 }
2617
2618 success = TRUE;
2619
2620 dw = camel_data_wrapper_new ();
2621 g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2622 camel_data_wrapper_construct_from_stream_sync (dw, ostream, NULL, NULL);
2623
2624 if (gpg->prefer_inline) {
2625 CamelTransferEncoding encoding;
2626
2627 encoding = camel_mime_part_get_encoding (ipart);
2628
2629 if (encoding != CAMEL_TRANSFER_ENCODING_BASE64 &&
2630 encoding != CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
2631 encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
2632
2633 encpart = camel_mime_part_new ();
2634 ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (ipart));
2635 camel_data_wrapper_set_mime_type_field (dw, ct);
2636
2637 camel_mime_part_set_encoding (encpart, encoding);
2638 camel_medium_set_content ((CamelMedium *) encpart, dw);
2639 g_object_unref (dw);
2640
2641 camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) encpart);
2642
2643 g_object_unref (encpart);
2644 } else {
2645 encpart = camel_mime_part_new ();
2646 ct = camel_content_type_new ("application", "octet-stream");
2647 camel_content_type_set_param (ct, "name", "encrypted.asc");
2648 camel_data_wrapper_set_mime_type_field (dw, ct);
2649 camel_content_type_unref (ct);
2650
2651 camel_medium_set_content ((CamelMedium *) encpart, dw);
2652 g_object_unref (dw);
2653
2654 camel_mime_part_set_description (encpart, _("This is a digitally encrypted message part"));
2655
2656 vstream = camel_stream_mem_new ();
2657 camel_stream_write_string (vstream, "Version: 1\n", NULL, NULL);
2658 g_seekable_seek (G_SEEKABLE (vstream), 0, G_SEEK_SET, NULL, NULL);
2659
2660 verpart = camel_mime_part_new ();
2661 dw = camel_data_wrapper_new ();
2662 camel_data_wrapper_set_mime_type (dw, class->encrypt_protocol);
2663 camel_data_wrapper_construct_from_stream_sync (
2664 dw, vstream, NULL, NULL);
2665 g_object_unref (vstream);
2666 camel_medium_set_content ((CamelMedium *) verpart, dw);
2667 g_object_unref (dw);
2668
2669 mpe = camel_multipart_encrypted_new ();
2670 ct = camel_content_type_new ("multipart", "encrypted");
2671 camel_content_type_set_param (ct, "protocol", class->encrypt_protocol);
2672 camel_data_wrapper_set_mime_type_field ((CamelDataWrapper *) mpe, ct);
2673 camel_content_type_unref (ct);
2674 camel_multipart_set_boundary ((CamelMultipart *) mpe, NULL);
2675
2676 camel_multipart_add_part ((CamelMultipart *) mpe, verpart);
2677 g_object_unref (verpart);
2678 camel_multipart_add_part ((CamelMultipart *) mpe, encpart);
2679 g_object_unref (encpart);
2680
2681 camel_medium_set_content ((CamelMedium *) opart, (CamelDataWrapper *) mpe);
2682
2683 g_object_unref (mpe);
2684 }
2685 fail:
2686 gpg_ctx_free (gpg);
2687 fail1:
2688 g_slist_free_full (gathered_keys, g_free);
2689 g_object_unref (istream);
2690 g_object_unref (ostream);
2691
2692 return success;
2693 }
2694
2695 static CamelCipherValidity *
gpg_decrypt_sync(CamelCipherContext * context,CamelMimePart * ipart,CamelMimePart * opart,GCancellable * cancellable,GError ** error)2696 gpg_decrypt_sync (CamelCipherContext *context,
2697 CamelMimePart *ipart,
2698 CamelMimePart *opart,
2699 GCancellable *cancellable,
2700 GError **error)
2701 {
2702 struct _GpgCtx *gpg = NULL;
2703 CamelCipherValidity *valid = NULL;
2704 CamelStream *ostream, *istream;
2705 CamelDataWrapper *content;
2706 CamelMimePart *encrypted;
2707 CamelMultipart *mp;
2708 CamelContentType *ct;
2709 gboolean success;
2710
2711 if (!ipart) {
2712 g_set_error (
2713 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2714 _("Cannot decrypt message: Incorrect message format"));
2715 return NULL;
2716 }
2717
2718 content = camel_medium_get_content ((CamelMedium *) ipart);
2719
2720 if (!content) {
2721 g_set_error (
2722 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2723 _("Cannot decrypt message: Incorrect message format"));
2724 return NULL;
2725 }
2726
2727 ct = camel_data_wrapper_get_mime_type_field (content);
2728 /* Encrypted part (using our fake mime type) or PGP/Mime multipart */
2729 if (camel_content_type_is (ct, "multipart", "encrypted")) {
2730 mp = (CamelMultipart *) camel_medium_get_content ((CamelMedium *) ipart);
2731 if (!(encrypted = camel_multipart_get_part (mp, CAMEL_MULTIPART_ENCRYPTED_CONTENT))) {
2732 g_set_error (
2733 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2734 _("Failed to decrypt MIME part: "
2735 "protocol error"));
2736 return NULL;
2737 }
2738
2739 content = camel_medium_get_content ((CamelMedium *) encrypted);
2740 } else if (camel_content_type_is (ct, "application", "x-inlinepgp-encrypted")) {
2741 content = camel_medium_get_content ((CamelMedium *) ipart);
2742 } else {
2743 /* Invalid Mimetype */
2744 g_set_error (
2745 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2746 _("Cannot decrypt message: Incorrect message format"));
2747 return NULL;
2748 }
2749
2750 istream = camel_stream_mem_new ();
2751 if (!camel_data_wrapper_decode_to_stream_sync (
2752 content, istream, cancellable, error)) {
2753 g_object_unref (istream);
2754 return NULL;
2755 }
2756
2757 g_seekable_seek (G_SEEKABLE (istream), 0, G_SEEK_SET, NULL, NULL);
2758
2759 ostream = camel_stream_mem_new ();
2760 camel_stream_mem_set_secure ((CamelStreamMem *) ostream);
2761
2762 gpg = gpg_ctx_new (context, cancellable);
2763 gpg_ctx_set_mode (gpg, GPG_CTX_MODE_DECRYPT);
2764 gpg_ctx_set_load_photos (gpg, camel_cipher_can_load_photos ());
2765 gpg_ctx_set_istream (gpg, istream);
2766 gpg_ctx_set_ostream (gpg, ostream);
2767
2768 gpg->bad_decrypt = TRUE;
2769
2770 if (!gpg_ctx_op_start (gpg, error))
2771 goto fail;
2772
2773 while (!gpg_ctx_op_complete (gpg)) {
2774 if (gpg_ctx_op_step (gpg, cancellable, error) == -1) {
2775 gpg_ctx_op_cancel (gpg);
2776 goto fail;
2777 }
2778 }
2779
2780 /* Report errors only if nothing was decrypted; missing sender's key used
2781 * for signature of a signed and encrypted messages causes GPG to return
2782 * failure, thus count with it.
2783 */
2784 if (gpg_ctx_op_wait (gpg) != 0 && (gpg->nodata || (gpg->bad_decrypt && !gpg->noseckey))) {
2785 const gchar *diagnostics;
2786
2787 diagnostics = gpg_ctx_get_diagnostics (gpg);
2788 g_set_error (
2789 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, "%s",
2790 (diagnostics != NULL && *diagnostics != '\0') ?
2791 diagnostics : _("Failed to execute gpg."));
2792 goto fail;
2793 }
2794
2795 /* Decrypted nothing, write at least CRLF */
2796 if (!g_seekable_tell (G_SEEKABLE (ostream))) {
2797 g_warn_if_fail (2 == camel_stream_write (ostream, "\r\n", 2, cancellable, NULL));
2798 }
2799
2800 g_seekable_seek (G_SEEKABLE (ostream), 0, G_SEEK_SET, NULL, NULL);
2801
2802 if (gpg->bad_decrypt && gpg->noseckey) {
2803 success = FALSE;
2804 g_set_error (
2805 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
2806 _("Failed to decrypt MIME part: Secret key not found"));
2807 } else if (camel_content_type_is (ct, "multipart", "encrypted")) {
2808 CamelDataWrapper *dw;
2809 CamelStream *null = camel_stream_null_new ();
2810
2811 /* Multipart encrypted - parse a full mime part */
2812 success = camel_data_wrapper_construct_from_stream_sync (
2813 CAMEL_DATA_WRAPPER (opart),
2814 ostream, NULL, error);
2815
2816 dw = camel_medium_get_content ((CamelMedium *) opart);
2817 if (!camel_data_wrapper_decode_to_stream_sync (
2818 dw, null, cancellable, NULL)) {
2819 /* nothing had been decoded from the stream, it doesn't
2820 * contain any header, like Content-Type or such, thus
2821 * write it as a message body */
2822 success = camel_data_wrapper_construct_from_stream_sync (
2823 dw, ostream, cancellable, error);
2824 }
2825
2826 g_object_unref (null);
2827 } else {
2828 /* Inline signed - raw data (may not be a mime part) */
2829 CamelDataWrapper *dw;
2830 dw = camel_data_wrapper_new ();
2831 success = camel_data_wrapper_construct_from_stream_sync (
2832 dw, ostream, NULL, error);
2833 camel_data_wrapper_set_mime_type (dw, "application/octet-stream");
2834 camel_medium_set_content ((CamelMedium *) opart, dw);
2835 g_object_unref (dw);
2836 /* Set mime/type of this new part to application/octet-stream to force type snooping */
2837 camel_mime_part_set_content_type (opart, "application/octet-stream");
2838 }
2839
2840 if (success) {
2841 valid = camel_cipher_validity_new ();
2842 if (gpg->decrypt_extra_text)
2843 valid->encrypt.description = g_strdup_printf (_("GPG blob contains unencrypted text: %s"), gpg->decrypt_extra_text->str);
2844 else
2845 valid->encrypt.description = g_strdup (_("Encrypted content"));
2846 valid->encrypt.status = CAMEL_CIPHER_VALIDITY_ENCRYPT_ENCRYPTED;
2847
2848 if (gpg->hadsig) {
2849 if (gpg->validsig) {
2850 if (gpg->trust == GPG_TRUST_UNDEFINED || gpg->trust == GPG_TRUST_NONE)
2851 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_UNKNOWN;
2852 else if (gpg->trust != GPG_TRUST_NEVER)
2853 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_GOOD;
2854 else
2855 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2856 } else if (gpg->nopubkey) {
2857 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_NEED_PUBLIC_KEY;
2858 } else {
2859 valid->sign.status = CAMEL_CIPHER_VALIDITY_SIGN_BAD;
2860 }
2861
2862 add_signers (valid, gpg_ctx_get_diagnostics (gpg), gpg->signers, gpg->signers_keyid, gpg->photos_filename);
2863 }
2864 }
2865
2866 fail:
2867 g_object_unref (ostream);
2868 g_object_unref (istream);
2869 gpg_ctx_free (gpg);
2870
2871 return valid;
2872 }
2873
2874 static void
camel_gpg_context_class_init(CamelGpgContextClass * class)2875 camel_gpg_context_class_init (CamelGpgContextClass *class)
2876 {
2877 GObjectClass *object_class;
2878 CamelCipherContextClass *cipher_context_class;
2879
2880 object_class = G_OBJECT_CLASS (class);
2881 object_class->set_property = gpg_context_set_property;
2882 object_class->get_property = gpg_context_get_property;
2883
2884 cipher_context_class = CAMEL_CIPHER_CONTEXT_CLASS (class);
2885 cipher_context_class->sign_protocol = "application/pgp-signature";
2886 cipher_context_class->encrypt_protocol = "application/pgp-encrypted";
2887 cipher_context_class->key_protocol = "application/pgp-keys";
2888 cipher_context_class->hash_to_id = gpg_hash_to_id;
2889 cipher_context_class->id_to_hash = gpg_id_to_hash;
2890 cipher_context_class->sign_sync = gpg_sign_sync;
2891 cipher_context_class->verify_sync = gpg_verify_sync;
2892 cipher_context_class->encrypt_sync = gpg_encrypt_sync;
2893 cipher_context_class->decrypt_sync = gpg_decrypt_sync;
2894
2895 g_object_class_install_property (
2896 object_class,
2897 PROP_ALWAYS_TRUST,
2898 g_param_spec_boolean (
2899 "always-trust",
2900 "Always Trust",
2901 NULL,
2902 FALSE,
2903 G_PARAM_READWRITE |
2904 G_PARAM_CONSTRUCT |
2905 G_PARAM_EXPLICIT_NOTIFY));
2906
2907 g_object_class_install_property (
2908 object_class,
2909 PROP_PREFER_INLINE,
2910 g_param_spec_boolean (
2911 "prefer-inline",
2912 "Prefer Inline",
2913 NULL,
2914 FALSE,
2915 G_PARAM_READWRITE |
2916 G_PARAM_CONSTRUCT |
2917 G_PARAM_EXPLICIT_NOTIFY));
2918 }
2919
2920 static void
camel_gpg_context_init(CamelGpgContext * context)2921 camel_gpg_context_init (CamelGpgContext *context)
2922 {
2923 context->priv = camel_gpg_context_get_instance_private (context);
2924 }
2925
2926 /**
2927 * camel_gpg_context_new:
2928 * @session: session
2929 *
2930 * Creates a new gpg cipher context object.
2931 *
2932 * Returns: a new gpg cipher context object.
2933 **/
2934 CamelCipherContext *
camel_gpg_context_new(CamelSession * session)2935 camel_gpg_context_new (CamelSession *session)
2936 {
2937 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
2938
2939 return g_object_new (
2940 CAMEL_TYPE_GPG_CONTEXT,
2941 "session", session, NULL);
2942 }
2943
2944 /**
2945 * camel_gpg_context_get_always_trust:
2946 * @context: a #CamelGpgContext
2947 *
2948 * Since: 2.32
2949 **/
2950 gboolean
camel_gpg_context_get_always_trust(CamelGpgContext * context)2951 camel_gpg_context_get_always_trust (CamelGpgContext *context)
2952 {
2953 g_return_val_if_fail (CAMEL_IS_GPG_CONTEXT (context), FALSE);
2954
2955 return context->priv->always_trust;
2956 }
2957
2958 /**
2959 * camel_gpg_context_set_always_trust:
2960 * @context: gpg context
2961 * @always_trust: always trust flag
2962 *
2963 * Sets the @always_trust flag on the gpg context which is used for
2964 * encryption.
2965 **/
2966 void
camel_gpg_context_set_always_trust(CamelGpgContext * context,gboolean always_trust)2967 camel_gpg_context_set_always_trust (CamelGpgContext *context,
2968 gboolean always_trust)
2969 {
2970 g_return_if_fail (CAMEL_IS_GPG_CONTEXT (context));
2971
2972 if (context->priv->always_trust == always_trust)
2973 return;
2974
2975 context->priv->always_trust = always_trust;
2976
2977 g_object_notify (G_OBJECT (context), "always-trust");
2978 }
2979
2980 /**
2981 * camel_gpg_context_get_prefer_inline:
2982 * @context: a #CamelGpgContext
2983 *
2984 * Returns: Whether prefer inline sign/encrypt (%TRUE), or as multiparts (%FALSE)
2985 *
2986 * Since: 3.20
2987 **/
2988 gboolean
camel_gpg_context_get_prefer_inline(CamelGpgContext * context)2989 camel_gpg_context_get_prefer_inline (CamelGpgContext *context)
2990 {
2991 g_return_val_if_fail (CAMEL_IS_GPG_CONTEXT (context), FALSE);
2992
2993 return context->priv->prefer_inline;
2994 }
2995
2996 /**
2997 * camel_gpg_context_set_prefer_inline:
2998 * @context: gpg context
2999 * @prefer_inline: whether prefer inline sign/encrypt
3000 *
3001 * Sets the @prefer_inline flag on the gpg context.
3002 *
3003 * Since: 3.20
3004 **/
3005 void
camel_gpg_context_set_prefer_inline(CamelGpgContext * context,gboolean prefer_inline)3006 camel_gpg_context_set_prefer_inline (CamelGpgContext *context,
3007 gboolean prefer_inline)
3008 {
3009 g_return_if_fail (CAMEL_IS_GPG_CONTEXT (context));
3010
3011 if (context->priv->prefer_inline == prefer_inline)
3012 return;
3013
3014 context->priv->prefer_inline = prefer_inline;
3015
3016 g_object_notify (G_OBJECT (context), "prefer-inline");
3017 }
3018