Enable building tests for windows
[gpgol.git] / src / parsecontroller.cpp
1 /* @file parsecontroller.cpp
2  * @brief Parse a mail and decrypt / verify accordingly
3  *
4  * Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
5  * Software engineering by Intevation GmbH
6  *
7  * This file is part of GpgOL.
8  *
9  * GpgOL is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * GpgOL is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 #include "config.h"
23
24 #include "parsecontroller.h"
25 #include "attachment.h"
26 #include "mimedataprovider.h"
27
28 #include "keycache.h"
29
30 #include <gpgme++/context.h>
31 #include <gpgme++/decryptionresult.h>
32 #include <gpgme++/key.h>
33
34 #include <sstream>
35
36 #ifdef HAVE_W32_SYSTEM
37 #include "common.h"
38 /* We use UTF-8 internally. */
39 #undef _
40 # define _(a) utf8_gettext (a)
41 #else
42 # define _(a) a
43 #endif
44
45
46 const char decrypt_template_html[] = {
47 "<html><head></head><body>"
48 "<table border=\"0\" width=\"100%%\" cellspacing=\"1\" cellpadding=\"1\" bgcolor=\"#0069cc\">"
49 "<tr>"
50 "<td bgcolor=\"#0080ff\">"
51 "<p><span style=\"font-weight:600; background-color:#0080ff;\"><center>%s %s</center><span></p></td></tr>"
52 "<tr>"
53 "<td bgcolor=\"#e0f0ff\">"
54 "<center>"
55 "<br/>%s"
56 "</td></tr>"
57 "</table></body></html>"};
58
59 const char decrypt_template[] = {"%s %s\n\n%s"};
60
61 using namespace GpgME;
62
63 static bool
64 expect_no_headers (msgtype_t type)
65 {
66   TSTART;
67   TRETURN type != MSGTYPE_GPGOL_MULTIPART_SIGNED;
68 }
69
70 static bool
71 expect_no_mime (msgtype_t type)
72 {
73   TSTART;
74   TRETURN type == MSGTYPE_GPGOL_PGP_MESSAGE ||
75          type == MSGTYPE_GPGOL_CLEAR_SIGNED;
76 }
77
78 #ifdef BUILD_TESTS
79 static void
80 get_and_print_key_test (const char *fingerprint, GpgME::Protocol proto)
81 {
82   if (!fingerprint)
83     {
84       STRANGEPOINT;
85       return;
86     }
87   auto ctx = std::unique_ptr<GpgME::Context> (GpgME::Context::createForProtocol
88                                               (proto));
89
90   if (!ctx)
91     {
92       STRANGEPOINT;
93       return;
94     }
95   ctx->setKeyListMode (GpgME::KeyListMode::Local |
96                        GpgME::KeyListMode::Signatures |
97                        GpgME::KeyListMode::Validate |
98                        GpgME::KeyListMode::WithTofu);
99
100   GpgME::Error err;
101   const auto newKey = ctx->key (fingerprint, err, false);
102
103   std::stringstream ss;
104   ss << newKey;
105
106   log_debug ("Key: %s", ss.str().c_str());
107   return;
108 }
109 #endif
110
111 #ifdef HAVE_W32_SYSTEM
112 ParseController::ParseController(LPSTREAM instream, msgtype_t type):
113     m_inputprovider  (new MimeDataProvider(instream,
114                           expect_no_headers(type))),
115     m_outputprovider (new MimeDataProvider(expect_no_mime(type))),
116     m_type (type),
117     m_block_html (false)
118 {
119   TSTART;
120   memdbg_ctor ("ParseController");
121   log_data ("%s:%s: Creating parser for stream: %p of type %i"
122                    " expect no headers: %i expect no mime: %i",
123                    SRCNAME, __func__, instream, type,
124                    expect_no_headers (type), expect_no_mime (type));
125   TRETURN;
126 }
127 #endif
128
129 ParseController::ParseController(FILE *instream, msgtype_t type):
130     m_inputprovider  (new MimeDataProvider(instream,
131                           expect_no_headers(type))),
132     m_outputprovider (new MimeDataProvider(expect_no_mime(type))),
133     m_type (type),
134     m_block_html (false)
135 {
136   TSTART;
137   memdbg_ctor ("ParseController");
138   log_data ("%s:%s: Creating parser for stream: %p of type %i",
139                    SRCNAME, __func__, instream, type);
140   TRETURN;
141 }
142
143 ParseController::~ParseController()
144 {
145   TSTART;
146   log_debug ("%s:%s", SRCNAME, __func__);
147   memdbg_dtor ("ParseController");
148   delete m_inputprovider;
149   delete m_outputprovider;
150   TRETURN;
151 }
152
153 static void
154 operation_for_type(msgtype_t type, bool *decrypt,
155                    bool *verify)
156 {
157   *decrypt = false;
158   *verify = false;
159   switch (type)
160     {
161       case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
162       case MSGTYPE_GPGOL_PGP_MESSAGE:
163         *decrypt = true;
164         break;
165       case MSGTYPE_GPGOL_MULTIPART_SIGNED:
166       case MSGTYPE_GPGOL_CLEAR_SIGNED:
167         *verify = true;
168         break;
169       case MSGTYPE_GPGOL_OPAQUE_SIGNED:
170         *verify = true;
171         break;
172       case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
173         *decrypt = true;
174         break;
175       default:
176         log_error ("%s:%s: Unknown data type: %i",
177                    SRCNAME, __func__, type);
178     }
179 }
180
181 static bool
182 is_smime (Data &data)
183 {
184   TSTART;
185   data.seek (0, SEEK_SET);
186   auto id = data.type();
187   data.seek (0, SEEK_SET);
188   TRETURN id == Data::CMSSigned || id == Data::CMSEncrypted;
189 }
190
191 static std::string
192 format_recipients(GpgME::DecryptionResult result, Protocol protocol)
193 {
194   TSTART;
195   std::string msg;
196   for (const auto recipient: result.recipients())
197     {
198       auto ctx = Context::createForProtocol(protocol);
199       Error e;
200       if (!ctx) {
201           /* Can't happen */
202           TRACEPOINT;
203           continue;
204       }
205       const auto key = ctx->key(recipient.keyID(), e, false);
206       delete ctx;
207       if (!key.isNull() && key.numUserIDs() && !e) {
208         msg += std::string("<br/>") + key.userIDs()[0].id() + " (0x" + recipient.keyID() + ")";
209         continue;
210       }
211       msg += std::string("<br/>") + _("Unknown Key:") + " 0x" + recipient.keyID();
212     }
213   TRETURN msg;
214 }
215
216 static std::string
217 format_error(GpgME::DecryptionResult result, Protocol protocol)
218 {
219   TSTART;
220   char *buf;
221   bool no_sec = false;
222   std::string msg;
223
224   if (result.error ().isCanceled () ||
225       result.error ().code () == GPG_ERR_NO_SECKEY)
226     {
227        msg = _("Decryption canceled or timed out.");
228     }
229
230   if (result.error ().code () == GPG_ERR_DECRYPT_FAILED ||
231       result.error ().code () == GPG_ERR_NO_SECKEY)
232     {
233       no_sec = true;
234       for (const auto &recipient: result.recipients ()) {
235         no_sec &= (recipient.status ().code () == GPG_ERR_NO_SECKEY);
236       }
237     }
238
239   if (no_sec)
240     {
241       msg = _("No secret key found to decrypt the message. "
242               "It is encrypted to the following keys:");
243       msg += format_recipients (result, protocol);
244     }
245   else
246     {
247       msg = _("Could not decrypt the data: ");
248
249       if (result.isNull ())
250         {
251           msg += _("Failed to parse the mail.");
252         }
253       else if (result.isLegacyCipherNoMDC())
254         {
255           msg += _("Data is not integrity protected. "
256                    "Decrypting it could be a security problem. (no MDC)");
257         }
258       else
259         {
260           msg += result.error().asString();
261         }
262     }
263
264   if (gpgrt_asprintf (&buf, opt.prefer_html ? decrypt_template_html :
265                       decrypt_template,
266                       protocol == OpenPGP ? "OpenPGP" : "S/MIME",
267                       _("Encrypted message (decryption not possible)"),
268                       msg.c_str()) == -1)
269     {
270       log_error ("%s:%s:Failed to Format error.",
271                  SRCNAME, __func__);
272       TRETURN "Failed to Format error.";
273     }
274   msg = buf;
275   memdbg_alloc (buf);
276   xfree (buf);
277   TRETURN msg;
278 }
279
280 void
281 ParseController::setSender(const std::string &sender)
282 {
283   TSTART;
284   m_sender = sender;
285   TRETURN;
286 }
287
288 static bool
289 is_valid_chksum(const GpgME::Signature &sig)
290 {
291   TSTART;
292   const auto sum = sig.summary();
293   static unsigned int valid_mask = (unsigned int) (
294       GpgME::Signature::Valid |
295       GpgME::Signature::Green |
296       GpgME::Signature::KeyRevoked |
297       GpgME::Signature::KeyExpired |
298       GpgME::Signature::SigExpired |
299       GpgME::Signature::CrlMissing |
300       GpgME::Signature::CrlTooOld |
301       GpgME::Signature::TofuConflict );
302
303   TRETURN sum & valid_mask;
304 }
305
306
307 /* Note on stability:
308
309    Experiments have shown that we can have a crash if parse
310    Returns at time that is not good for the state of Outlook.
311
312    This happend in my test instance after a delay of > 1s < 3s
313    with a < 1% chance :-/
314
315    So if you have really really bad luck this might still crash
316    although it usually should be either much quicker or much slower
317    (slower e.g. when pinentry is requrired).
318 */
319 void
320 ParseController::parse()
321 {
322   TSTART;
323   // Wrap the input stream in an attachment / GpgME Data
324   Protocol protocol;
325   bool decrypt, verify;
326
327   Data input (m_inputprovider);
328
329   auto inputType = input.type ();
330
331   if (inputType == Data::Type::PGPSigned)
332     {
333       verify = true;
334       decrypt = false;
335     }
336   else
337     {
338       operation_for_type (m_type, &decrypt, &verify);
339     }
340
341   if ((m_inputprovider->signature() && is_smime (*m_inputprovider->signature())) ||
342       is_smime (input))
343     {
344       protocol = Protocol::CMS;
345     }
346   else
347     {
348       protocol = Protocol::OpenPGP;
349     }
350   auto ctx = std::unique_ptr<Context> (Context::createForProtocol (protocol));
351   if (!ctx)
352     {
353       log_error ("%s:%s:Failed to create context. Installation broken.",
354                  SRCNAME, __func__);
355       char *buf;
356       const char *proto = protocol == OpenPGP ? "OpenPGP" : "S/MIME";
357       if (gpgrt_asprintf (&buf, opt.prefer_html ? decrypt_template_html :
358                           decrypt_template,
359                           proto,
360                           _("Encrypted message (decryption not possible)"),
361                           _("Failed to find GnuPG please ensure that GnuPG or "
362                             "Gpg4win is properly installed.")) == -1)
363         {
364           log_error ("%s:%s:Failed format error.",
365                      SRCNAME, __func__);
366           /* Should never happen */
367           m_error = std::string("Bad installation");
368         }
369       memdbg_alloc (buf);
370       m_error = buf;
371       xfree (buf);
372       TRETURN;
373     }
374
375   /* Maybe a different option for this ? */
376   if (opt.autoretrieve)
377     {
378       ctx->setFlag("auto-key-retrieve", "1");
379     }
380
381   ctx->setArmor(true);
382
383   if (!m_sender.empty())
384     {
385       ctx->setSender(m_sender.c_str());
386     }
387
388   Data output (m_outputprovider);
389   log_debug ("%s:%s:%p decrypt: %i verify: %i with protocol: %s sender: %s type: %i",
390              SRCNAME, __func__, this,
391              decrypt, verify,
392              protocol == OpenPGP ? "OpenPGP" :
393              protocol == CMS ? "CMS" : "Unknown",
394              m_sender.empty() ? "none" : anonstr (m_sender.c_str()), inputType);
395   if (decrypt)
396     {
397       input.seek (0, SEEK_SET);
398       TRACEPOINT;
399       auto combined_result = ctx->decryptAndVerify(input, output);
400       log_debug ("%s:%s:%p decrypt / verify done.",
401                  SRCNAME, __func__, this);
402       m_decrypt_result = combined_result.first;
403       m_verify_result = combined_result.second;
404
405       if ((!m_decrypt_result.error () &&
406           m_verify_result.signatures ().empty() &&
407           m_outputprovider->signature ()) ||
408           is_smime (output) ||
409           output.type() == Data::Type::PGPSigned)
410         {
411           TRACEPOINT;
412           /* There is a signature in the output. So we have
413              to verify it now as an extra step. */
414           input = Data (m_outputprovider);
415           delete m_inputprovider;
416           m_inputprovider = m_outputprovider;
417           m_outputprovider = new MimeDataProvider();
418           output = Data(m_outputprovider);
419           verify = true;
420           TRACEPOINT;
421         }
422       else
423         {
424           verify = false;
425         }
426       TRACEPOINT;
427       if (m_decrypt_result.error () || m_decrypt_result.isNull () ||
428           m_decrypt_result.error ().isCanceled ())
429         {
430           m_error = format_error (m_decrypt_result, protocol);
431         }
432     }
433   if (verify)
434     {
435       TRACEPOINT;
436       GpgME::Data *sig = m_inputprovider->signature();
437       input.seek (0, SEEK_SET);
438       if (sig)
439         {
440           sig->seek (0, SEEK_SET);
441           TRACEPOINT;
442           m_verify_result = ctx->verifyDetachedSignature(*sig, input);
443           log_debug ("%s:%s:%p verify done.",
444                      SRCNAME, __func__, this);
445           /* Copy the input to output to do a mime parsing. */
446           char buf[4096];
447           input.seek (0, SEEK_SET);
448           output.seek (0, SEEK_SET);
449           size_t nread;
450           while ((nread = input.read (buf, 4096)) > 0)
451             {
452               output.write (buf, nread);
453             }
454         }
455       else
456         {
457           TRACEPOINT;
458           m_verify_result = ctx->verifyOpaqueSignature(input, output);
459           TRACEPOINT;
460
461           const auto sigs = m_verify_result.signatures();
462           bool allBad = sigs.size();
463           for (const auto s :sigs)
464             {
465               if (!(s.summary() & Signature::Red))
466                 {
467                   allBad = false;
468                   break;
469                 }
470             }
471 #ifdef HAVE_W32_SYSTEM
472           if (allBad)
473             {
474               log_debug ("%s:%s:%p inline verify error trying native to utf8.",
475                          SRCNAME, __func__, this);
476
477
478               /* The proper solution would be to take the encoding from
479                  the mail / headers. Then convert the wchar body to that
480                  encoding. Verify, and convert it after verifcation to
481                  UTF-8 which the rest of the code expects.
482
483                  Or native_body from native ACP to InternetCodepage, then
484                  verify and convert the output back to utf8 as the rest
485                  expects.
486
487                  But as this is clearsigned and we don't really want that.
488                  Meh.
489                  */
490               char *utf8 = native_to_utf8 (input.toString().c_str());
491               if (utf8)
492                 {
493                   // Try again after conversion.
494                   ctx = std::unique_ptr<Context> (Context::createForProtocol (protocol));
495                   ctx->setArmor (true);
496                   if (!m_sender.empty())
497                     {
498                       ctx->setSender(m_sender.c_str());
499                     }
500
501                   input = Data (utf8, strlen (utf8));
502                   xfree (utf8);
503
504                   // Use a fresh output
505                   auto provider = new MimeDataProvider (true);
506
507                   // Warning: The dtor of the Data object touches
508                   // the provider. So we have to delete it after
509                   // the assignment.
510                   output = Data (provider);
511                   delete m_outputprovider;
512                   m_outputprovider = provider;
513
514                   // Try again
515                   m_verify_result = ctx->verifyOpaqueSignature(input, output);
516                 }
517             }
518 #endif
519         }
520     }
521   log_debug ("%s:%s:%p: decrypt err: %i verify err: %i",
522              SRCNAME, __func__, this, m_decrypt_result.error().code(),
523              m_verify_result.error().code());
524
525   bool has_valid_encrypted_checksum = false;
526   /* Ensure that the Keys for the signatures are available
527      and if it has a valid encrypted checksum. */
528   for (const auto sig: m_verify_result.signatures())
529     {
530       TRACEPOINT;
531       has_valid_encrypted_checksum = is_valid_chksum (sig);
532
533 #ifndef BUILD_TESTS
534       KeyCache::instance ()->update (sig.fingerprint (), protocol);
535 #endif
536       TRACEPOINT;
537     }
538
539   if (protocol == Protocol::CMS && decrypt && !m_decrypt_result.error() &&
540       !has_valid_encrypted_checksum)
541     {
542       log_debug ("%s:%s:%p Encrypted S/MIME without checksum. Block HTML.",
543                  SRCNAME, __func__, this);
544       m_block_html = true;
545     }
546
547   if (opt.enable_debug & DBG_DATA)
548     {
549       std::stringstream ss;
550       TRACEPOINT;
551       ss << m_decrypt_result << '\n' << m_verify_result;
552       for (const auto sig: m_verify_result.signatures())
553         {
554           const auto key = sig.key();
555           if (key.isNull())
556             {
557 #ifndef BUILD_TESTS
558               ss << '\n' << "Cached key:\n" << KeyCache::instance()->getByFpr(
559                   sig.fingerprint(), false);
560 #else
561               get_and_print_key_test (sig.fingerprint (), protocol);
562 #endif
563             }
564           else
565             {
566               ss << '\n' << key;
567             }
568         }
569        log_debug ("Decrypt / Verify result: %s", ss.str().c_str());
570     }
571   else
572     {
573       log_debug ("%s:%s:%p Decrypt / verify done errs: %i / %i numsigs: %i.",
574                  SRCNAME, __func__, this, m_decrypt_result.error().code(),
575                  m_verify_result.error().code(), m_verify_result.numSignatures());
576     }
577   TRACEPOINT;
578
579   if (m_outputprovider)
580     {
581       m_outputprovider->finalize ();
582     }
583
584   TRETURN;
585 }
586
587 const std::string
588 ParseController::get_html_body () const
589 {
590   TSTART;
591   if (m_outputprovider)
592     {
593       TRETURN m_outputprovider->get_html_body ();
594     }
595   else
596     {
597       TRETURN std::string();
598     }
599 }
600
601 const std::string
602 ParseController::get_body () const
603 {
604   TSTART;
605   if (m_outputprovider)
606     {
607       TRETURN m_outputprovider->get_body ();
608     }
609   else
610     {
611       TRETURN std::string();
612     }
613 }
614
615 const std::string
616 ParseController::get_body_charset() const
617 {
618   TSTART;
619   if (m_outputprovider)
620     {
621       TRETURN m_outputprovider->get_body_charset();
622     }
623   else
624     {
625       TRETURN std::string();
626     }
627 }
628
629 const std::string
630 ParseController::get_html_charset() const
631 {
632   TSTART;
633   if (m_outputprovider)
634     {
635       TRETURN m_outputprovider->get_html_charset();
636     }
637   else
638     {
639       TRETURN std::string();
640     }
641 }
642
643 std::vector<std::shared_ptr<Attachment> >
644 ParseController::get_attachments() const
645 {
646   TSTART;
647   if (m_outputprovider)
648     {
649       TRETURN m_outputprovider->get_attachments();
650     }
651   else
652     {
653       TRETURN std::vector<std::shared_ptr<Attachment> >();
654     }
655 }