diff --git a/NEWS b/NEWS index 03ae566..5c55fd9 100644 --- a/NEWS +++ b/NEWS @@ -1,913 +1,917 @@ Noteworthy changes for version 2.5.2 (unreleased) ================================================= * Fixed re-encryption of drafts after modification if draft encryption is enabled. (T5812) + * Added setting "auto" for draftKey registry + value to autoselect a draft encryption key. + (T5564) + Noteworthy changes for version 2.5.1 (2021-12-01) ================================================= * Improved ReadAsPlain detection and plaintext handling. (T5681) Noteworthy changes for version 2.5.0 (2021-06-11) ================================================= * Changed encryption code to work even more on OOM. This avoids temporarily storing unencrypted data to MAPI when asynchronously encrypting. (T5022) * Added support to encrypt / sign Outlook internal data objects as attachments like mails, calendar entries and contacts. (T4184) Noteworthy changes for version 2.4.10 (2021-01-08) ================================================= * Fixed a logic error in GpgOL's recipient selection which caused one recipient to be ignored. Noteworthy changes for version 2.4.9 (2021-01-07) ================================================= * Fixed selection of "No Key" for a recipient. (T5223) * Fix preview of PGP mails when auto-key-retrieve is enabled. (T5164) Noteworthy changes for version 2.4.8 (2020-11-20) ================================================= * Fixed attachment realted isses because of preview. Noteworthy changes for version 2.4.7 (2020-09-04) ================================================= * Fixed an issue that unencrypted drafts were sent to the server even when draft encryption is on. (T5022) Noteworthy changes for version 2.4.6 (2020-07-22) ================================================= * Improved handling of protected headers mails. (T4796) * Experimental code for combined S/MIME and OpenPGP operations and protected headers. Options are: combinedOpsEnabled, encryptSubject, splitBCCMails * Fixed handling of WKS mails. (T4839) * Improved Addressbook integration. (T4874) * Automatically learn keys from smartcards. (T4877) * Fix signing key selection for group accounts. (T4940) * Show a preview when mail verification takes long. (T4944) * Allow changing the printer when printing crypto mails. (T4890) * Fix reply crypt selection when signing is selcted by default. (T4949) * Properly show level 2 validitity for Mails with keys from WKD. * Show a warning when both outlooks internal crypto and GpgOL is active für a mail. (T4953) * Fix cases where GpgOL would detect permanently decrypted mails as still encrypted. (T4718) * Disable automatic when displaying plaintext mails. (T4987) * Use a generic filename for attachments that with characters disallowed by Windows. (T4835) Noteworthy changes for version 2.4.5 (2019-12-20) ================================================= * Fix a crash when closing Outlook. (T4787) Noteworthy changes for version 2.4.4 (2019-12-13) ================================================= * Enable File -> Save As also for mails opened in their own window. Noteworthy changes for version 2.4.3 (2019-12-13) ================================================= * Improved compatibilty with OWA and other Mail clients working with the same S/MIME mails. (T4525) * Added user friendly error handling when attachments cannot be added. (T4731) * Fix a crash that could happen in rare cases when opening broken mails. * Crypto mails are no longer always classified as HTML. (T4639) Noteworthy changes for version 2.4.2 (2019-07-14) ================================================= * Fixed a possible plaintext leak. (T4662 T4661) * Fixed an issue when changing plain text options at runtime. (T4611) Noteworthy changes for version 2.4.1 (2019-05-15) ================================================= * Fixed printing of encrypted mails. * File -> Save As does work for encrypted mails now. Noteworthy changes for version 2.4.0 (2019-06-06) ================================================= * S/MIME Mails now use the same icons as Outlook * Message classes in GpgOL have been changed to improve compatibility with other clients. * Draft encryption was added as an experimental feature. * GpgOL autosecure no longer triggers for users without an S/MIME certificate. * Forwarding of crypto and non crypto mails has been very much improved. * Mails without headers are now handled better. * S/MIME Address book integration was added. Noteworthy changes for version 2.3.3 (2019-03-26) ================================================= * Fixed external API for sent mails. (T4241) * Fixed a crash in debug API. (T4262) * Fixed some cases where S/MIME was not detected correctly. (T4267, T4403) * Fixed tooltip for bad signatures. (T4299) * Fixed forwarding of sent mails. (T4321) * Improved generated attachment names. (T4258) * Added more, less secure automation options. * Added minimalistic protected headers support. * Added an option to decrypt mails permanently. * Improved error handling in case encryption failed. * No longer silently ignores unsupported attachments. (T4184) * Added external API to re-encrypt and decrypt. (T4241) Noteworthy changes for version 2.3.2 (2018-11-12) ================================================= * Reduced leakage of private information without DBG_DATA. (T4193) * Added handling for Junk folders. (T4188) * Added a fallback for encoding problems. (T4156) * Fixed system wide default configuration. * Improved S/MIME handling. * Populate keycache on startup. Noteworthy changes for version 2.3.1 (2018-10-16) ================================================= * Fixed attachement handling for office and pdf attachments. * Improved signature info display. * Added address book integration for OpenPGP. * Added auto import capabilities for S/MIME. * Added generic prefer S/MIME mode. * Various bugfixes and regression fixes. Noteworthy changes for version 2.3.0 (2018-08-31) ================================================= * Massive stability and performance improvements. * New configuration dialog. * New option to automatically encrypt if possible. * Moving mails is now possible. * Improvements to attachment handling with long filenames. * Support for contact Groups has been added. Noteworthy changes for version 2.2.0 (2018-06-15) ================================================= * Removed support for Outlook 2003 and 2007. * Fixed reply handling of PGP/Inline mails. (T3964) * Fixed a seemingly random crash. (T3946) * Added dutch and ukrainian translation. * Fixed encoding for some PGP/Inline mails. (T3986) Noteworthy changes for version 2.1.1 (2018-04-24) ================================================= * Fixed a regression in 3.1.0 that could lead to decryption errors. * Fixed internal keycache in de-vs mode. * Fixed a crash during recipient lookup. * Improved error handling. * Keys from WKD are automatically acceptable for auto encryption. * Added quick print context menu option. Noteworthy changes for version 2.1.0 (2018-04-12) ================================================= * Encryption and Signing has been reworked to, again, work without Kleopatra. * WKS Setup is supported in a basic way. * PGP/Inline is now fully supported. * Many Bugfixes and Parser improvements. Noteworthy changes for version 2.0.6 (2018-01-12) ================================================= * PGP/Inline sending is now compatible with Microsoft Exchange Online. (T3662) * A bug that caused encrypted mails not to be displayed has been fixed. (T3537) * A bug that caused drafted mails not to encrypt the correct content has been fixed. (T3419) * The recipient lookup for Exchange addresses has been slightly improved. * When Outlooks internal S/MIME handling code was activated mails might be sent out unencrypted (T3656) * Fixed signed only PGP Mails with attachments. (T3735) Noteworthy changes for version 2.0.5 (2017-12-08) ================================================= * A crash when receiving crypto mails with attachments without file extension has been fixed. (T3582). * Fixed a cause for potentially undefined behavior when closing. Noteworthy changes for version 2.0.4 (2017-12-05) ================================================= * Some possible "random" crashes in GpgOL have been fixed (T3484) * Fixed Outlook hang when selecting and deleting many mails (T3433) * G Suite Sync plugin accounts are now detected. Only no-mime PGP/Messages (without attachments) and encrypted only is supported. Reading is fully supported. * Basic support for No-MIME inline PGP Encryption (T3514) * Improved error handling for signed, unencrypted mails (T3538) * Performance improvements / Fix running out of resources (T3523) * Improved detection of large PGP/MIME messages and MS-TNEF Messages. (T3419 , T3542) Noteworthy changes for version 2.0.3 (2017-11-20) ================================================= * Additional saveguards have been added to prevent sending out unencrypted bodys when used with Exchange 2007. * Fixed a regression from 2.0.2 regarding message list display in Outlook 2010 and 2013. Noteworthy changes for version 2.0.2 (2017-11-16) ================================================= * A potential crash when pasting recpients was fixed. * A potential random crash in Outlook 2016 has been worked around. * Encoding problems when reading HTML mails have been fixed. * S/MIME Mails are reverted again if S/MIME is disabled. * S/MIME Mails through Exchange and sent mails are now handled correctly. Noteworthy changes for version 2.0.1 (2017-09-12) ================================================= * Support for some kinds of PGP Multipart / Signed mails has been fixed if S/MIME is disabled. Noteworthy changes for version 2.0.0 (2017-09-12) ================================================= * Decryption / verification is done in a second thread so outlook stays responsive while decrypting. * Opening a mail in a reader window no longer causes Outlook to resync the mail. * Inline editors (Reply and Forward in the messagelist) are now supported. * The HTML preferences from Outlook are now respected when viewing an encrypted multipart/alternative mail. * Two crashes that sometimes occured when sending mail have been fixed. * The "Do you want to save the changes" Messageboxes from outlook no longer show up. * Signature details are now shown in the Mail ribbon when reading messages. * Signature and encryption status is now shown in Outlook through categorisation. No more popups when reading encrypted mails. * There is now an Option to use inline-pgp when encrypting mails without attachments. * When opening a mail in a reader window closing it no longer causes the mail in the Messagelist not to be displayed anymore. * Decryption no longer requires an UI-Server (GPA or Kleopatra). * Various bugfixes. Noteworthy changes for version 1.4.0 (2016-03-30) ================================================= * (OL > 2007) An option dialog has been added to enable / disable S/MIME support and the new "simplified interface" * (OL > 2007) An option for a "simplified interface" has been Added. With this option encrypt / sign is now done while sending, including all attachments and using a standard format. (MIME Support) In this mode GpgOL automatically decrypts / verifies messages. And the only interface are the encrypt & sign buttons in the New Mail tab. This option is currently disabled by default but will eventually become the standard. * 64 Bit versions of Outlook are now supported. * (OL > 2007) Settings dialog added. (accessible over the new mail ribbon) * (OL > 2007) S/MIME Support is disabled by default. Enable it in the settings. * (OL > 2007) Reduced amount of syncing done by Outlook while looking at decrypted MIME mails. * (OL > 2007) If S/MIME is disabled GpgOL reverts the changes it made while reading an S/MIME mail so that Outlook can handle them again. * (OL > 2007) If GpgOL can't prevent syncing changes through IMAP or Exchange it tries to restore the original mail so that other clients can also read them. * (OL > 2007) Improved lookup of Exchange Sender address. * (OL > 2007) Fixed crash when Exchange Active Sync (Outlook.com) is used. Noteworthy changes for version 1.3.0 (2015-11-24) ================================================= * Outlook 2010 and later now handle recieved MIME mails. * A class of random crashes in Outlook 2010 and later has been fixed. Bug#1837 * Attachments of mime mails with non ASCII characters are now handled correctly. * Outlook 2016 is now supported. * Added translations for Chinese and French. Noteworthy changes for version 1.2.1 (2014-08-13) ================================================= * Fixed recipient/sender lookup problems when using Exchange or Active Directory. Noteworthy changes for version 1.2.0 (2013-08-19) ================================================= * Basic support for Outlook 2010 and later. Noteworthy changes for version 1.1.3 (2011-12-27) ================================================= * Fix data corruption bug for certain attachments. Bug#1352. * Fix crash on opening attachments with OL2007. Bug #1110. * Use the GIT commit ids instead of SVN revision numbers for version checks and to construct the Windows file version. Noteworthy changes for version 1.1.2 (2010-07-21) ================================================= * Add Portuguese translation * Fixed linking problems with latest libgpg-error. Noteworthy changes for version 1.1.1 (2010-01-13) ================================================= * Cleaned up some icons. Noteworthy changes for version 1.1.0 (2010-01-05) ================================================= * Replaced most ECE code by direct OOM code. This was required to support better icons; i.e. icons not limited to a 16 color palette. * New icons. * Removed protocol selection. The UI-server is now expected to select the protocol (i.e. the auto selection mode is now the only one). Noteworthy changes for version 1.0.1 (2009-09-28) ================================================= * No more event loop peeking to avoid problem with Office programs. * S/MIME support is now enabled by default. Noteworthy changes for version 1.0.0 (2009-06-18) ================================================= * Show a notice about potential problems. * After about 2 years of development, the 1.0 version is now due. Noteworthy changes for version 0.10.19 (2009-02-27) =================================================== * Save the crypto settings in a message draft. * Unnamed attachments are now shown with a suffix matching its MIME type. Noteworthy changes for version 0.10.18 (2009-01-28) =================================================== * Handle OL created S/MIME messages. Noteworthy changes for version 0.10.17 (2008-11-14) =================================================== * Minor cleanups. * All operations are now somewhat faster. Noteworthy changes for version 0.10.16 (2008-11-11) =================================================== * Fixed a regression in the last release with opaque signatures. * Fixed PGP cleartext signature verification. * Encryption of attachments is now much faster. Noteworthy changes for version 0.10.15 (2008-08-06) =================================================== * New option to present the body of a message as an attachment. This is useful to make sure that the body will never show up as plaintext in the message store. * New menu item to remove all GpgOL created flags and attachments from all messages in a folder. * Icons are now installed for messages processed by GpgOL. For now only for the German version of Outlook. Noteworthy changes for version 0.10.14 (2008-05-28) =================================================== * Minor fixes. Noteworthy changes for version 0.10.13 (2008-05-06) =================================================== * Properly handle the disposition of text attachments. Noteworthy changes for version 0.10.12 (2008-04-16) =================================================== * Added icons. * Minor usuability changes. Noteworthy changes for version 0.10.11 (2008-04-04) =================================================== * Fixed a performance problem with signed+encrypted. Noteworthy changes for version 0.10.10 (2008-04-02) =================================================== * Visual cleanups. * Changes to the I/O dispatcher. Noteworthy changes for version 0.10.9 (2008-03-19) ================================================== * Decrypt opaque signed and encrypted S/MIME mails. * Handle old-style PGP message with attachments. Note that the signature verification currently may indicate a bad signature. Noteworthy changes for version 0.10.8 (2008-03-18) ================================================== * Fixed a segv introduced with 0.10.6. Noteworthy changes for version 0.10.7 (2008-03-11) ================================================== * Changed the way sign+encrypt works to help the UI-server. Noteworthy changes for version 0.10.6 (2008-03-10) ================================================== * More tweaks to allow processing of opaque encrypted or signed S/MIME. * Shows an error message when trying to decrypt/verify messages not signed or encrypted. * Soft line breaks in QP encoded messages are now correctly processed. * The sender's address is send to the UI server to allow it to select an appropriate signing key. * Automatic protocol selection works now also with signing. * Processing large messages is faster. Noteworthy changes for version 0.10.5 (2008-02-18) ================================================== * PGP inline encrypted mails are not anymore deleted after the first decryption. Noteworthy changes for version 0.10.4 (2008-02-06) ================================================== * Sign and encrypt works now. * Texts with embedded attachments are now concatenated. * Encrypted message are now viewable in the sent messages folder. Noteworthy changes for version 0.10.3 (2007-12-10) ================================================== * Minor fixes. Noteworthy changes for version 0.10.2 (2007-11-12) ================================================== * New menu items to select the default protocol. * Code cleanups. Noteworthy changes for version 0.10.1 (2007-10-22) ================================================== * Auto start the server. * Code cleanups. * Made all dialogs language neutral. * The manual has some notes about the Registry usage and new MAPI properties. Noteworthy changes for version 0.10.0 (2007-10-11) ================================================== * Basically a complete rewrite. A lot of things are still missing but if might be useful to see the direction the development takes. Noteworthy changes for version 0.9.91 (2006-10-13) ================================================== * Fixed a crash in the recipients dialog. Noteworthy changes for version 0.9.90 (2006-08-28) ================================================== * Fix problem that message would be sent in clear text if the user cancelled the operation. * Cosmetic updates for some dialogs. * Do not show the 'select signer dialog' when only one secret key is available in the keyring. * Fixes for the automatic key selection algorithm used in the recipient key dialog. Noteworthy changes for version 0.9.10 (2006-04-25) ================================================== * Fixes for Umlaut problems. Noteworthy changes for version 0.9.9 (2006-04-24) ================================================= * Some cosmetic changes. * Encryption to the default key works again. Noteworthy changes for version 0.9.8 (2006-03-28) ================================================= * PGP/MIME signature verification may now work in some cases. * New option to prefer displaying of the HTML part. Noteworthy changes for version 0.9.7 (2006-03-21) ================================================= * Minor changes Noteworthy changes for version 0.9.6 (2006-01-26) ================================================= * Cosmetic fixes. Noteworthy changes for version 0.9.5 (2005-12-07) ================================================= * Fixed problems related to use on non-admin accounts. * Print a warning if used with OL prior to OL2003 SP2. Noteworthy changes for version 0.9.4 (2005-12-06) ================================================= * Added translation framework. Provided German translation. * New option to enable automatic decryption in the preview window. * Removed deprecated options to configure gpg path and homedir. * Default key from the option dialog works. * Support for HTML mails. Noteworthy changes for version 0.9.3 (2005-09-29) ================================================= * Fixed bugs introduced with the last release. * PGP/MIME decryption works now correctly with Latin-1 and utf-8. * No more pop-ups to ask whether to save changes after just decrypting a message. * Fixed a couple of bugs possibly leading to crashes. Noteworthy changes for version 0.9.2 (2005-09-22) ================================================= * Saving attachments from PGP/MIME encrypted messages works. Noteworthy changes for version 0.9.1 (2005-09-19) ================================================= * Bug fixes Noteworthy changes for version 0.9.0 (2005-09-04) ================================================= * Major rewrite. Renamed the package to GPGol. Note, that there used to be intermediate versions unter the name OutlGPG * The package as been renamed to GPGol and consist of only one DLL named "gpgol.dll". Installation of gpgme.dll and libgpg-error.dll is required. * It may by now only be build using the Mingw32 toolchain. * GPGol now uses the standard GPGME. Noteworthy changes for version 0.6.1 (unreleased) ================================================= * Fix the problem that the user can just reply with the encrypted text. * Fixes for a lot of minor problems with NT5 based systems and for Outlook version 2003. * Support for handling HTML mails. This includes the encryption of the contents and the proper decryption without losing the special (html) text attributes like colors. * Support for '%ENV%' strings for the log file. Noteworthy changes for version 0.5.5 (2005-07-12) ================================================= * Support to sign all outgoing attachments. * Support for logging. * Fixed some memory leaks. Noteworthy changes for version 0.5.4 (2005-07-03) ================================================= * Support for securing attachments. This means the all attachments will be encrypted if encryption has been selected for the message. * A new option to allow to save decrypted attachments to the disk. * Several bug fixes all over the place. Noteworthy changes for version 0.5.3 (2005-06-16) ================================================= * Allow to set a default key which is used automatically for encryption. * Handle old V3 keys in the signature verification dialog. * Issue and error if the encrypt process returned invalid recipients. Noteworthy changes for version 0.5.2 (2005-06-05) ================================================= * Differ between possible decryption failures. - General errors. - No secret key available. * Add a 'encrypt-to' listbox to the decryption dialog to know the recipients the message was encrypted for. * Add some checks to report problems with permissions related to the Registry. * Fixed a format string problem which was possible for crashes when the signature has been expired. Noteworthy changes for version 0.5.1 (2005-05-29) ================================================= * Issue a warning if the user cancels the sign or encryption procedure. * Support to read and write X- headers for messages. * Fixed a problem which crashes Outlook if the keyManager exe did not exist but was set in the registry. Noteworthy changes for version 0.4.0 (2005-05-10) ================================================= * Verify dialog is automatically shown whenever needed. Plus it contains a hint-label whenever the signature is special. For example the signature has expire or it was issued by a key which is not trustworthy. * Offer a GPG configuration dialog to set the path to GPG, the home directory and an optional field to specify a key manager. * Common dialogs for the following procedures: - verify a clearsign signature - decrypt a message (and verify a signature) - encrypt a message (and sign the plaintext) - clearsign a message * Provide a class to encapsulate MAPI messages and high-level functions for all crypto operations. diff --git a/src/cryptcontroller.cpp b/src/cryptcontroller.cpp index 38da2fe..1462aa5 100644 --- a/src/cryptcontroller.cpp +++ b/src/cryptcontroller.cpp @@ -1,1567 +1,1589 @@ /* @file cryptcontroller.cpp * @brief Helper to do crypto on a mail. * * Copyright (C) 2018 Intevation GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "config.h" #include "common.h" #include "cpphelp.h" #include "cryptcontroller.h" #include "mail.h" #include "mapihelp.h" #include "mimemaker.h" #include "wks-helper.h" #include "overlay.h" #include "keycache.h" #include "mymapitags.h" #include "recipient.h" #include "recipientmanager.h" #include "windowmessages.h" #include #include #include #include "common.h" #include static int sink_data_write (sink_t sink, const void *data, size_t datalen) { GpgME::Data *d = static_cast(sink->cb_data); d->write (data, datalen); return 0; } static int create_sign_attach (sink_t sink, protocol_t protocol, GpgME::Data &signature, GpgME::Data &signedData, const char *micalg); /** We have some C Style cruft in here as this was historically how GpgOL worked directly in the MAPI data objects. To reduce the regression risk the new object oriented way for crypto reused as much as possible from this. */ CryptController::CryptController (Mail *mail, bool encrypt, bool sign, GpgME::Protocol proto): m_mail (mail), m_encrypt (encrypt), m_sign (sign), m_crypto_success (false), m_proto (proto) { TSTART; memdbg_ctor ("CryptController"); log_debug ("%s:%s: CryptController ctor for %p encrypt %i sign %i inline %i.", SRCNAME, __func__, mail, encrypt, sign, mail->getDoPGPInline ()); m_recipients = mail->getCachedRecipients (); m_signer_keys = mail->getSigningKeys (); m_sender = mail->getSender (); TRETURN; } CryptController::~CryptController() { TSTART; memdbg_dtor ("CryptController"); log_debug ("%s:%s:%p", SRCNAME, __func__, m_mail); TRETURN; } int CryptController::collect_data () { TSTART; int count = m_mail->plainAttachments ().size (); /* Take the Body from the mail if possible. This is a fix for GnuPG-Bug-ID: T3614 because the body is not always properly updated in MAPI when sending. */ char *body = m_mail->takeCachedPlainBody (); if (body && !*body) { xfree (body); body = nullptr; } if (!count && !body) { if (!m_mail->isDraftEncrypt()) { gpgol_message_box (m_mail->getWindow (), utf8_gettext ("Can't encrypt / sign an empty message."), utf8_gettext ("GpgOL"), MB_OK); } xfree (body); TRETURN -1; } bool do_inline = m_mail->getDoPGPInline (); if (count && do_inline) { log_debug ("%s:%s: PGP Inline not supported for attachments." " Using PGP MIME", SRCNAME, __func__); do_inline = false; m_mail->setDoPGPInline (false); } else if (do_inline) { /* Inline. Use Body as input. We need to collect also our mime structure for S/MIME as we don't know yet if we are S/MIME or OpenPGP */ m_bodyInput.write (body, strlen (body)); log_debug ("%s:%s: Inline. Caching body.", SRCNAME, __func__); /* Set the input buffer to start. */ m_bodyInput.seek (0, SEEK_SET); } /* Set up the sink object to collect the mime structure */ struct sink_s sinkmem; sink_t sink = &sinkmem; memset (sink, 0, sizeof *sink); sink->cb_data = &m_input; sink->writefnc = sink_data_write; /* Collect the mime strucutre */ int err = add_body_and_attachments (sink, m_mail, body); xfree (body); if (err) { log_error ("%s:%s: Collecting body and attachments failed.", SRCNAME, __func__); TRETURN -1; } /* Set the input buffer to start. */ m_input.seek (0, SEEK_SET); TRETURN 0; } int CryptController::lookup_fingerprints (const std::vector &sigFprs, const std::vector > &recpFprs) { TSTART; auto ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto)); if (!ctx) { log_error ("%s:%s: failed to create context with protocol '%s'", SRCNAME, __func__, m_proto == GpgME::CMS ? "smime" : m_proto == GpgME::OpenPGP ? "openpgp" : "unknown"); TRETURN -1; } ctx->setKeyListMode (GpgME::Local); GpgME::Error err; if (sigFprs.size()) { char **cSigners = vector_to_cArray (sigFprs); err = ctx->startKeyListing (const_cast (cSigners), true); if (err) { log_error ("%s:%s: failed to start signer keylisting", SRCNAME, __func__); release_cArray (cSigners); TRETURN -1; } do { m_signer_keys.push_back(ctx->nextKey(err)); } while (!err); release_cArray (cSigners); m_signer_keys.pop_back(); if (m_signer_keys.empty()) { log_error ("%s:%s: failed to lookup key for '%s' with protocol '%s'", SRCNAME, __func__, anonstr (sigFprs[0].c_str ()), m_proto == GpgME::CMS ? "smime" : m_proto == GpgME::OpenPGP ? "openpgp" : "unknown"); TRETURN -1; } // reset context ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto)); ctx->setKeyListMode (GpgME::Local); } if (!recpFprs.size()) { TRETURN 0; } std::vector all_fingerprints; for (const auto &pair: recpFprs) { all_fingerprints.push_back (pair.second); } for (auto &recp: m_recipients) { std::vector fingerprintsToLookup; /* Find the matching fingerprints for the recpient. */ for (const auto &pair: recpFprs) { if (pair.first.empty()) { log_error ("%s:%s Recipient mbox is empty. Wrong Resolver Version?", SRCNAME, __func__); } /* We also accept the empty string here for backwards compatibility but we should still log an error to be clear. */ if (pair.first.empty() || pair.first == recp.mbox ()) { if (pair.second.empty ()) { log_err ("Would have added an empty string!"); continue; } fingerprintsToLookup.push_back (pair.second); all_fingerprints.erase(std::remove_if(all_fingerprints.begin(), all_fingerprints.end(), [&pair](const std::string &x){return x == pair.second;}), all_fingerprints.end()); } } if (!fingerprintsToLookup.size ()) { log_dbg ("No key selected for '%s'", anonstr (recp.mbox().c_str ())); continue; } // Convert recipient fingerprints char **cRecps = vector_to_cArray (fingerprintsToLookup); err = ctx->startKeyListing (const_cast (cRecps)); if (err) { log_error ("%s:%s: failed to start recipient keylisting", SRCNAME, __func__); release_cArray (cRecps); TRETURN -1; } std::vector keys; do { const auto key = ctx->nextKey (err); if (key.isNull () || err) { continue; } keys.push_back (key); log_dbg ("Adding '%s' as key for '%s", anonstr (key.primaryFingerprint ()), anonstr (recp.mbox ().c_str ())); } while (!err); release_cArray (cRecps); recp.setKeys (keys); // reset context ctx = std::shared_ptr (GpgME::Context::createForProtocol (m_proto)); ctx->setKeyListMode (GpgME::Local); } if (!all_fingerprints.empty()) { log_error ("%s:%s: BUG: Not all fingerprints could be matched " "to a recipient.", SRCNAME, __func__); for (const auto &fpr: all_fingerprints) { log_debug ("%s:%s Failed to find: %s", SRCNAME, __func__, anonstr (fpr.c_str ())); } clear_keys (); TRETURN -1; } resolving_done (); TRETURN 0; } int CryptController::parse_output (GpgME::Data &resolverOutput) { TSTART; std::istringstream ss(resolverOutput.toString()); std::string line; std::vector sigFprs; std::vector> recpFprs; while (std::getline (ss, line)) { rtrim (line); if (line == "cancel") { log_debug ("%s:%s: resolver canceled", SRCNAME, __func__); TRETURN -2; } if (line == "unencrypted") { log_debug ("%s:%s: FIXME resolver wants unencrypted", SRCNAME, __func__); TRETURN -1; } std::istringstream lss (line); // First is sig or enc std::string what; std::string how; std::string fingerprint; std::string mbox; std::getline (lss, what, ':'); std::getline (lss, how, ':'); std::getline (lss, fingerprint, ':'); std::getline (lss, mbox, ':'); if (m_proto == GpgME::UnknownProtocol) { /* TODO: Allow mixed */ m_proto = (how == "smime") ? GpgME::CMS : GpgME::OpenPGP; } if (what == "sig") { sigFprs.push_back (fingerprint); continue; } if (what == "enc") { recpFprs.push_back(std::make_pair(mbox, fingerprint)); } } if (m_sign && sigFprs.empty()) { log_error ("%s:%s: Sign requested but no signing fingerprint - sending unsigned", SRCNAME, __func__); m_sign = false; } if (m_encrypt && !recpFprs.size()) { log_error ("%s:%s: Encrypt requested but no recipient fingerprints", SRCNAME, __func__); gpgol_message_box (m_mail->getWindow (), utf8_gettext ("No recipients for encryption selected."), _("GpgOL"), MB_OK); TRETURN -2; } TRETURN lookup_fingerprints (sigFprs, recpFprs); } /* Combines all recipient keys for this operation into a list and copys the resolved recipients back. */ void CryptController::resolving_done () { TSTART; m_enc_keys.clear (); for (const auto &recp: m_recipients) { const auto &keys = recp.keys (); m_enc_keys.insert (m_enc_keys.end (), keys.begin (), keys.end ()); } /* Copy the resolved recipients back to the mail */ m_mail->setRecipients (m_recipients); m_mail->setSigningKeys (m_signer_keys); TRETURN; } bool CryptController::resolve_through_protocol (GpgME::Protocol proto) { TSTART; const auto cache = KeyCache::instance (); if (m_encrypt) { for (auto &recp: m_recipients) { recp.setKeys(cache->getEncryptionKeys(recp.mbox (), proto)); } } if (m_sign) { if (m_sender.empty()) { log_error ("%s:%s: Asked to sign but sender is empty.", SRCNAME, __func__); return false; } const auto key = cache->getSigningKey (m_sender.c_str (), proto); if (m_signer_keys.empty ()) { m_signer_keys.push_back (key); } for (const auto &k: m_signer_keys) { if (k.protocol () != proto) { m_signer_keys.push_back (key); break; } } } TRETURN is_resolved (); } /* Check if we can be resolved by a single protocol and return it. */ GpgME::Protocol CryptController::get_resolved_protocol () const { TSTART; GpgME::Protocol ret = GpgME::UnknownProtocol; bool hasOpenPGPSignKey = false; bool hasSMIMESignKey = false; if (m_sign) { for (const auto &sig_key: m_signer_keys) { hasOpenPGPSignKey |= (!sig_key.isNull() && sig_key.protocol () == GpgME::OpenPGP); hasSMIMESignKey |= (!sig_key.isNull() && sig_key.protocol () == GpgME::CMS); } } if (m_encrypt) { for (const auto &recp: m_recipients) { if (!recp.keys ().size () || recp.keys ()[0].isNull ()) { /* No keys or the first key in the list is null. */ TRETURN GpgME::UnknownProtocol; } for (const auto &key: recp.keys()) { if (key.protocol () == GpgME::OpenPGP && (!m_sign || hasOpenPGPSignKey) && (ret == GpgME::UnknownProtocol || ret == GpgME::OpenPGP)) { ret = GpgME::OpenPGP; continue; } if (key.protocol () == GpgME::CMS && (!m_sign || hasSMIMESignKey) && (ret == GpgME::UnknownProtocol || ret == GpgME::CMS)) { ret = GpgME::CMS; continue; } // Unresolvable TRETURN GpgME::UnknownProtocol; } } } TRETURN ret; } /* Check that the crypt operation is resolved. This supports combined S/MIME and OpenPGP operations. */ bool CryptController::is_resolved () const { /* Check that we have signing keys if necessary and at least one encryption key for each recipient. */ bool hasOpenPGPSignKey = false; bool hasSMIMESignKey = false; if (m_sign) { for (const auto &sig_key: m_signer_keys) { hasOpenPGPSignKey |= (!sig_key.isNull() && sig_key.protocol () == GpgME::OpenPGP); hasSMIMESignKey |= (!sig_key.isNull() && sig_key.protocol () == GpgME::CMS); } } log_dbg ("Has OpenPGP Sig Key: %i SMIME: %i", hasOpenPGPSignKey, hasSMIMESignKey); if (m_encrypt) { Recipient::dump (m_recipients); for (const auto &recp: m_recipients) { if (!recp.keys ().size () || recp.keys ()[0].isNull ()) { /* No keys or the first key in the list is null. */ return false; } /* If we don't sign we need no more checks. */ if (!m_sign) { continue; } for (const auto &key: recp.keys()) { if (key.protocol () == GpgME::OpenPGP && !hasOpenPGPSignKey) { return false; } if (key.protocol () == GpgME::CMS && !hasSMIMESignKey) { return false; } } } } return true; } int CryptController::resolve_keys_cached() { TSTART; // Prepare variables const auto recps = m_mail->getCachedRecipientAddresses (); bool resolved = false; if (opt.enable_smime && opt.prefer_smime) { resolved = resolve_through_protocol (GpgME::CMS); if (resolved) { log_debug ("%s:%s: Resolved with CMS due to preference.", SRCNAME, __func__); m_proto = GpgME::CMS; } } if (!resolved) { resolved = resolve_through_protocol (GpgME::OpenPGP); if (resolved) { log_debug ("%s:%s: Resolved with OpenPGP.", SRCNAME, __func__); m_proto = GpgME::OpenPGP; } } if (!resolved && (opt.enable_smime && !opt.prefer_smime)) { resolved = resolve_through_protocol (GpgME::CMS); if (resolved) { log_debug ("%s:%s: Resolved with CMS as fallback.", SRCNAME, __func__); m_proto = GpgME::CMS; } } for (const auto &recp: m_recipients) { log_debug ("Enc Key for: '%s' Type: %i", anonstr (recp.mbox ().c_str ()), recp.type ()); for (const auto &key: recp.keys ()) { log_debug ("%s:%s", to_cstr (key.protocol ()), anonstr (key.primaryFingerprint ())); } } for (const auto &sig_key: m_signer_keys) { log_debug ("%s:%s: Signing key: %s:%s", SRCNAME, __func__, to_cstr (sig_key.protocol()), anonstr (sig_key.primaryFingerprint ())); } if (!resolved) { log_debug ("%s:%s: Failed to resolve through cache", SRCNAME, __func__); m_enc_keys.clear (); m_signer_keys.clear (); m_proto = GpgME::UnknownProtocol; TRETURN 1; } if (m_encrypt) { log_debug ("%s:%s: Encrypting with protocol %s to:", SRCNAME, __func__, to_cstr (m_proto)); } TRETURN 0; } void CryptController::clear_keys () { for (auto &recp: m_recipients) { recp.setKeys (std::vector ()); } m_signer_keys.clear (); } int CryptController::resolve_keys () { TSTART; m_proto = get_resolved_protocol (); if (m_proto != GpgME::UnknownProtocol) { log_debug ("%s:%s: Already resolved by %s Not resolving again.", SRCNAME, __func__, to_cstr (m_proto)); start_crypto_overlay(); resolving_done (); TRETURN 0; } m_enc_keys.clear(); if (m_mail->isDraftEncrypt() && opt.draft_key) { - const auto key = KeyCache::instance()->getByFpr (opt.draft_key); + GpgME::Key key; + if (opt.draft_key && !strcmp (opt.draft_key, "auto")) + { + log_dbg ("Autoselecting draft key first ultimate key."); + for (const auto &k: KeyCache::instance()->getUltimateKeys ()) + { + if (k.hasSecret () && k.canEncrypt ()) + { + xfree (opt.draft_key); + gpgrt_asprintf (&opt.draft_key, "%s", k.primaryFingerprint()); + log_dbg ("Autoselecting %s as draft encryption key.", + opt.draft_key); + write_options (); + key = k; + break; + } + } + } + + if (key.isNull ()) + { + key = KeyCache::instance()->getByFpr (opt.draft_key); + } if (key.isNull()) { const char *buf = utf8_gettext ("Failed to encrypt draft.\n\n" "The configured encryption key for drafts " "could not be found.\n" "Please check your configuration or " "turn off draft encryption in the settings."); gpgol_message_box (get_active_hwnd (), buf, _("GpgOL"), MB_OK); TRETURN -1; } log_debug ("%s:%s: resolved draft encryption key protocol is: %s", SRCNAME, __func__, to_cstr (key.protocol())); m_proto = key.protocol (); m_enc_keys.push_back (key); TRETURN 0; } if (!m_recipients.size()) { /* Should not happen. But we add it for better bug reports. */ const char *bugmsg = utf8_gettext ("Operation failed.\n\n" "This is usually caused by a bug in GpgOL or an error in your setup.\n" "Please see https://www.gpg4win.org/reporting-bugs.html " "or ask your Administrator for support."); char *buf; gpgrt_asprintf (&buf, "Failed to resolve recipients.\n\n%s\n", bugmsg); memdbg_alloc (buf); gpgol_message_box (get_active_hwnd (), buf, _("GpgOL"), MB_OK); xfree(buf); TRETURN -1; } if (opt.autoresolve && !opt.alwaysShowApproval && !resolve_keys_cached ()) { log_debug ("%s:%s: resolved keys through the cache", SRCNAME, __func__); start_crypto_overlay(); resolving_done (); TRETURN 0; } std::vector args; // Collect the arguments char *gpg4win_dir = get_gpg4win_dir (); if (!gpg4win_dir) { TRACEPOINT; TRETURN -1; } const auto resolver = std::string (gpg4win_dir) + "\\bin\\resolver.exe"; args.push_back (resolver); log_debug ("%s:%s: resolving keys with '%s'", SRCNAME, __func__, resolver.c_str ()); // We want debug output as OutputDebugString args.push_back (std::string ("--debug")); // Yes passing it as int is ok. auto wnd = m_mail->getWindow (); if (wnd) { // Pass the handle of the active window for raise / overlay. args.push_back (std::string ("--hwnd")); args.push_back (std::to_string ((int) (intptr_t) wnd)); } // Set the overlay caption args.push_back (std::string ("--overlayText")); if (m_encrypt) { args.push_back (std::string (utf8_gettext ("Resolving recipients..."))); } else if (m_sign) { args.push_back (std::string (utf8_gettext ("Resolving signers..."))); } if (!opt.enable_smime) { args.push_back (std::string ("--protocol")); args.push_back (std::string ("pgp")); } if (m_sign) { args.push_back (std::string ("--sign")); } const auto cached_sender = m_mail->getSender (); if (cached_sender.empty()) { log_error ("%s:%s: resolve keys without sender.", SRCNAME, __func__); } else { args.push_back (std::string ("--sender")); args.push_back (cached_sender); } if (!opt.autoresolve || opt.alwaysShowApproval) { args.push_back (std::string ("--alwaysShow")); } if (opt.prefer_smime) { args.push_back (std::string ("--preferred-protocol")); args.push_back (std::string ("cms")); } args.push_back (std::string ("--lang")); args.push_back (std::string (gettext_localename ())); bool has_smime_override = false; if (m_encrypt) { args.push_back (std::string ("--encrypt")); // Get the recipients that are cached from OOM for (const auto &recp: m_recipients) { const auto mbox = recp.mbox (); auto overrides = KeyCache::instance ()->getOverrides (mbox, GpgME::OpenPGP); const auto cms_overrides = KeyCache::instance ()->getOverrides (mbox, GpgME::CMS); overrides.insert(overrides.end(), cms_overrides.begin(), cms_overrides.end()); if (overrides.size()) { std::string overrideStr = mbox + ":"; for (const auto &key: overrides) { if (key.isNull()) { TRACEPOINT; continue; } has_smime_override |= key.protocol() == GpgME::CMS; overrideStr += key.primaryFingerprint(); overrideStr += ","; } overrideStr.erase(overrideStr.size() - 1, 1); args.push_back (std::string ("-o")); args.push_back (overrideStr); } args.push_back (mbox); } } if (!opt.prefer_smime && has_smime_override) { /* Prefer S/MIME if there was an S/MIME override */ args.push_back (std::string ("--preferred-protocol")); args.push_back (std::string ("cms")); } // Args are prepared. Spawn the resolver. auto ctx = GpgME::Context::createForEngine (GpgME::SpawnEngine); if (!ctx) { // can't happen TRACEPOINT; TRETURN -1; } // Convert our collected vector to c strings // It's a bit overhead but should be quick for such small // data. char **cargs = vector_to_cArray (args); log_data ("%s:%s: Spawn args:", SRCNAME, __func__); for (size_t i = 0; cargs && cargs[i]; i++) { log_data (SIZE_T_FORMAT ": '%s'", i, cargs[i]); } GpgME::Data mystdin (GpgME::Data::null), mystdout, mystderr; GpgME::Error err = ctx->spawn (cargs[0], const_cast (cargs), mystdin, mystdout, mystderr, (GpgME::Context::SpawnFlags) ( GpgME::Context::SpawnAllowSetFg | GpgME::Context::SpawnShowWindow)); // Somehow Qt messes up which window to bring back to front. // So we do it manually. bring_to_front (wnd); // We need to create an overlay while encrypting as pinentry can take a while start_crypto_overlay(); log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ()); log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ()); release_cArray (cargs); if (err) { log_debug ("%s:%s: Resolver spawn finished Err code: %i asString: %s", SRCNAME, __func__, err.code(), err.asString()); } int ret = parse_output (mystdout); if (ret == -1) { log_debug ("%s:%s: Failed to parse / resolve keys.", SRCNAME, __func__); log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ()); log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ()); TRETURN -1; } TRETURN ret; } int CryptController::do_crypto (GpgME::Error &err, std::string &r_diag) { TSTART; log_debug ("%s:%s", SRCNAME, __func__); if (m_mail->isDraftEncrypt ()) { log_debug ("%s:%s Disabling sign because of draft encrypt", SRCNAME, __func__); m_sign = false; } /* Start a WKS check if necessary. */ WKSHelper::instance()->start_check (m_mail->getSender ()); int ret = 0; if (m_mail->copyParent ()) { /* Bypass resolving if we are working on a split mail */ m_recipients = m_mail->getCachedRecipients (); m_signer_keys = m_mail->getSigningKeys (); if (m_recipients.size () && m_recipients[0].keys ().size ()) { m_proto = m_recipients[0].keys ()[0].protocol (); } else if (m_signer_keys.size ()) { m_proto = m_signer_keys[0].protocol (); } m_enc_keys.clear (); for (const auto &recp: m_recipients) { const auto &keys = recp.keys (); m_enc_keys.insert (m_enc_keys.end (), keys.begin (), keys.end ()); } if ((opt.enable_debug & DBG_DATA)) { log_data ("Encrypting to: "); Recipient::dump (m_recipients); } } else { ret = resolve_keys (); } if (ret == -1) { //error log_debug ("%s:%s: Failure to resolve keys.", SRCNAME, __func__); TRETURN -1; } if (ret == -2) { // Cancel TRETURN -2; } if (!m_mail->copyParent ()) { RecipientManager mngr (m_recipients, m_signer_keys); if (mngr.getRequiredMails () > 1) { log_dbg ("More then one mail required for this recipient selection."); /* If we need to send multiple emails we jump back from here into the main event loop. Copy the mail object and send it out mutiple times. */ do_in_ui_thread_async (SEND_MULTIPLE_MAILS, m_mail); /* Cancel the crypto of this mail this continues in Mail::splitAndSend_o */ wm_unregister_pending_op (m_mail); TRETURN -3; } } bool do_inline = m_mail->getDoPGPInline (); if (m_proto == GpgME::CMS && do_inline) { log_debug ("%s:%s: Inline for S/MIME not supported. Switching to mime.", SRCNAME, __func__); do_inline = false; m_mail->setDoPGPInline (false); m_bodyInput = GpgME::Data(GpgME::Data::null); } auto ctx = GpgME::Context::create(m_proto); if (!ctx) { log_error ("%s:%s: Failure to create context.", SRCNAME, __func__); gpgol_message_box (m_mail->getWindow (), "Failure to create context.", utf8_gettext ("GpgOL"), MB_OK); TRETURN -1; } if (!m_signer_keys.empty ()) { for (const auto &key: m_signer_keys) { if (key.protocol () == m_proto) { ctx->addSigningKey (key); } } } ctx->setTextMode (m_proto == GpgME::OpenPGP); ctx->setArmor (m_proto == GpgME::OpenPGP); if (m_encrypt && m_sign && do_inline) { // Sign encrypt combined const auto result_pair = ctx->signAndEncrypt (m_enc_keys, do_inline ? m_bodyInput : m_input, m_output, GpgME::Context::AlwaysTrust); const auto err1 = result_pair.first.error(); const auto err2 = result_pair.second.error(); if (err1 || err2) { log_error ("%s:%s: Encrypt / Sign error %s %s.", SRCNAME, __func__, result_pair.first.error().asString(), result_pair.second.error().asString()); err = err1 ? err1 : err2; GpgME::Data log; const auto err3 = ctx->getAuditLog (log, GpgME::Context::DiagnosticAuditLog); if (!err3) { r_diag = log.toString(); } TRETURN -1; } if (err1.isCanceled() || err2.isCanceled()) { err = err1.isCanceled() ? err1 : err2; log_debug ("%s:%s: User cancled", SRCNAME, __func__); TRETURN -2; } } else if (m_encrypt && m_sign) { // First sign then encrypt const auto sigResult = ctx->sign (m_input, m_output, GpgME::Detached); err = sigResult.error(); if (err) { log_error ("%s:%s: Signing error %s.", SRCNAME, __func__, sigResult.error().asString()); GpgME::Data log; const auto err3 = ctx->getAuditLog (log, GpgME::Context::DiagnosticAuditLog); if (!err3) { r_diag = log.toString(); } TRETURN -1; } if (err.isCanceled()) { log_debug ("%s:%s: User cancled", SRCNAME, __func__); TRETURN -2; } parse_micalg (sigResult); // We now have plaintext in m_input // The detached signature in m_output // Set up the sink object to construct the multipart/signed GpgME::Data multipart; struct sink_s sinkmem; sink_t sink = &sinkmem; memset (sink, 0, sizeof *sink); sink->cb_data = &multipart; sink->writefnc = sink_data_write; if (create_sign_attach (sink, m_proto == GpgME::CMS ? PROTOCOL_SMIME : PROTOCOL_OPENPGP, m_output, m_input, m_micalg.c_str ())) { TRACEPOINT; TRETURN -1; } // Now we have the multipart throw away the rest. m_output = GpgME::Data (); m_input = GpgME::Data (); multipart.seek (0, SEEK_SET); const auto encResult = ctx->encrypt (m_enc_keys, multipart, m_output, GpgME::Context::AlwaysTrust); err = encResult.error(); if (err) { log_error ("%s:%s: Encryption error %s.", SRCNAME, __func__, err.asString()); GpgME::Data log; const auto err3 = ctx->getAuditLog (log, GpgME::Context::DiagnosticAuditLog); if (!err3) { r_diag = log.toString(); } TRETURN -1; } if (err.isCanceled()) { log_debug ("%s:%s: User cancled", SRCNAME, __func__); TRETURN -2; } // Now we have encrypted output just treat it like encrypted. } else if (m_encrypt) { const auto result = ctx->encrypt (m_enc_keys, do_inline ? m_bodyInput : m_input, m_output, GpgME::Context::AlwaysTrust); err = result.error(); if (err) { log_error ("%s:%s: Encryption error %s.", SRCNAME, __func__, err.asString()); GpgME::Data log; const auto err3 = ctx->getAuditLog (log, GpgME::Context::DiagnosticAuditLog); if (!err3) { r_diag = log.toString(); } TRETURN -1; } if (err.isCanceled()) { log_debug ("%s:%s: User cancled", SRCNAME, __func__); TRETURN -2; } } else if (m_sign) { const auto result = ctx->sign (do_inline ? m_bodyInput : m_input, m_output, do_inline ? GpgME::Clearsigned : GpgME::Detached); err = result.error(); if (err) { log_error ("%s:%s: Signing error %s.", SRCNAME, __func__, err.asString()); GpgME::Data log; const auto err3 = ctx->getAuditLog (log, GpgME::Context::DiagnosticAuditLog); if (!err3) { r_diag = log.toString(); } TRETURN -1; } if (err.isCanceled()) { log_debug ("%s:%s: User cancled", SRCNAME, __func__); TRETURN -2; } parse_micalg (result); } else { // ??? log_error ("%s:%s: unreachable code reached.", SRCNAME, __func__); } log_debug ("%s:%s: Crypto done sucessfuly.", SRCNAME, __func__); m_crypto_success = true; TRETURN 0; } static int write_data (sink_t sink, GpgME::Data &data) { TSTART; if (!sink || !sink->writefnc) { TRETURN -1; } char buf[4096]; size_t nread; data.seek (0, SEEK_SET); while ((nread = data.read (buf, 4096)) > 0) { sink->writefnc (sink, buf, nread); } TRETURN 0; } int create_sign_attach (sink_t sink, protocol_t protocol, GpgME::Data &signature, GpgME::Data &signedData, const char *micalg) { TSTART; char boundary[BOUNDARYSIZE+1]; char top_header[BOUNDARYSIZE+200]; int rc = 0; /* Write the top header. */ generate_boundary (boundary); create_top_signing_header (top_header, sizeof top_header, protocol, 1, boundary, micalg); if ((rc = write_string (sink, top_header))) { TRACEPOINT; TRETURN rc; } /* Write the boundary so that it is not included in the hashing. */ if ((rc = write_boundary (sink, boundary, 0))) { TRACEPOINT; TRETURN rc; } /* Write the signed mime structure */ if ((rc = write_data (sink, signedData))) { TRACEPOINT; TRETURN rc; } /* Write the signature attachment */ if ((rc = write_boundary (sink, boundary, 0))) { TRACEPOINT; TRETURN rc; } if (protocol == PROTOCOL_OPENPGP) { rc = write_string (sink, "Content-Type: application/pgp-signature;\r\n" "\tname=\"" OPENPGP_SIG_NAME "\"\r\n" "Content-Transfer-Encoding: 7Bit\r\n"); } else { rc = write_string (sink, "Content-Transfer-Encoding: base64\r\n" "Content-Type: application/pkcs7-signature\r\n" "Content-Disposition: inline;\r\n" "\tfilename=\"" SMIME_SIG_NAME "\"\r\n"); /* rc = write_string (sink, */ /* "Content-Type: application/x-pkcs7-signature\r\n" */ /* "\tname=\"smime.p7s\"\r\n" */ /* "Content-Transfer-Encoding: base64\r\n" */ /* "Content-Disposition: attachment;\r\n" */ /* "\tfilename=\"smime.p7s\"\r\n"); */ } if (rc) { TRACEPOINT; TRETURN rc; } if ((rc = write_string (sink, "\r\n"))) { TRACEPOINT; TRETURN rc; } // Write the signature data if (protocol == PROTOCOL_SMIME) { const std::string sigStr = signature.toString(); if ((rc = write_b64 (sink, (const void *) sigStr.c_str (), sigStr.size()))) { TRACEPOINT; TRETURN rc; } } else if ((rc = write_data (sink, signature))) { TRACEPOINT; TRETURN rc; } // Add an extra linefeed with should not harm. if ((rc = write_string (sink, "\r\n"))) { TRACEPOINT; TRETURN rc; } /* Write the final boundary. */ if ((rc = write_boundary (sink, boundary, 1))) { TRACEPOINT; TRETURN rc; } TRETURN rc; } static int create_encrypt_attach (sink_t sink, protocol_t protocol, GpgME::Data &encryptedData, int exchange_major_version) { TSTART; char boundary[BOUNDARYSIZE+1]; int rc = create_top_encryption_header (sink, protocol, boundary, false, exchange_major_version); // From here on use goto failure pattern. if (rc) { log_error ("%s:%s: Failed to create top header.", SRCNAME, __func__); TRETURN rc; } if (protocol == PROTOCOL_OPENPGP || exchange_major_version >= 15) { // With exchange 2016 we have to construct S/MIME // differently and write the raw data here. rc = write_data (sink, encryptedData); } else { const auto encStr = encryptedData.toString(); rc = write_b64 (sink, encStr.c_str(), encStr.size()); } if (rc) { log_error ("%s:%s: Failed to create top header.", SRCNAME, __func__); TRETURN rc; } /* Write the final boundary (for OpenPGP) and finish the attachment. */ if (*boundary && (rc = write_boundary (sink, boundary, 1))) { log_error ("%s:%s: Failed to write boundary.", SRCNAME, __func__); } TRETURN rc; } int CryptController::update_mail_mapi () { TSTART; log_debug ("%s:%s", SRCNAME, __func__); LPMESSAGE message = get_oom_base_message (m_mail->item()); if (!message) { log_error ("%s:%s: Failed to obtain message.", SRCNAME, __func__); TRETURN -1; } if (m_mail->getDoPGPInline ()) { // Nothing to do for inline. log_debug ("%s:%s: Inline mail. Setting encoding.", SRCNAME, __func__); SPropValue prop; prop.ulPropTag = PR_INTERNET_CPID; prop.Value.l = 65001; if (HrSetOneProp (message, &prop)) { log_error ("%s:%s: Failed to set CPID mapiprop.", SRCNAME, __func__); } TRETURN 0; } mapi_attach_item_t *att_table = mapi_create_attach_table (message, 0); /* When we forward e.g. a crypto mail we have sent the message has a MOSSTEMPL. We need to remove that. T4321 */ for (ULONG pos=0; att_table && !att_table[pos].end_of_table; pos++) { if (att_table[pos].attach_type == ATTACHTYPE_MOSSTEMPL) { log_debug ("%s:%s: Found existing moss attachment at " "pos %i removing it.", SRCNAME, __func__, att_table[pos].mapipos); if (message->DeleteAttach (att_table[pos].mapipos, 0, nullptr, 0) != S_OK) { log_error ("%s:%s: Failed to remove attachment.", SRCNAME, __func__); } } } // Set up the sink object for our MSOXSMIME attachment. struct sink_s sinkmem; sink_t sink = &sinkmem; memset (sink, 0, sizeof *sink); sink->cb_data = &m_input; sink->writefnc = sink_data_write; // For S/MIME encrypted mails we have to use the application/pkcs7-mime // content type. Otherwise newer (2016) exchange servers will throw // an M2MCVT.StorageError.Exeption (See GnuPG-Bug-Id: T3853 ) // This means that the conversion / build of the mime structure also // happens differently. int exchange_major_version = get_ex_major_version_for_addr ( m_mail->getSender ().c_str ()); std::string overrideMimeTag; if (m_proto == GpgME::CMS && m_encrypt && exchange_major_version >= 15) { log_debug ("%s:%s: CMS Encrypt with Exchange %i activating alternative.", SRCNAME, __func__, exchange_major_version); overrideMimeTag = "application/pkcs7-mime"; } LPATTACH attach = create_mapi_attachment (message, sink, overrideMimeTag.empty() ? nullptr : overrideMimeTag.c_str()); if (!attach) { log_error ("%s:%s: Failed to create moss attach.", SRCNAME, __func__); gpgol_release (message); TRETURN -1; } protocol_t protocol = m_proto == GpgME::CMS ? PROTOCOL_SMIME : PROTOCOL_OPENPGP; int rc = 0; /* Do we have override MIME ? */ const auto overrideMime = m_mail->get_override_mime_data (); if (!overrideMime.empty()) { rc = write_string (sink, overrideMime.c_str ()); } else if (m_sign && m_encrypt) { rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version); } else if (m_encrypt) { rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version); } else if (m_sign) { rc = create_sign_attach (sink, protocol, m_output, m_input, m_micalg.c_str ()); } // Close our attachment if (!rc) { rc = close_mapi_attachment (&attach, sink); } // Set message class etc. if (!rc) { rc = finalize_message (message, att_table, protocol, m_encrypt ? 1 : 0, false, m_mail->isDraftEncrypt (), exchange_major_version); } // only on error. if (rc) { cancel_mapi_attachment (&attach, sink); } // cleanup mapi_release_attach_table (att_table); gpgol_release (attach); gpgol_release (message); TRETURN rc; } std::string CryptController::get_inline_data () { TSTART; std::string ret; if (!m_mail->getDoPGPInline ()) { TRETURN ret; } m_output.seek (0, SEEK_SET); char buf[4096]; size_t nread; while ((nread = m_output.read (buf, 4096)) > 0) { ret += std::string (buf, nread); } TRETURN ret; } void CryptController::parse_micalg (const GpgME::SigningResult &result) { TSTART; if (result.isNull()) { TRACEPOINT; TRETURN; } const auto signature = result.createdSignature(0); if (signature.isNull()) { TRACEPOINT; TRETURN; } const char *hashAlg = signature.hashAlgorithmAsString (); if (!hashAlg) { TRACEPOINT; TRETURN; } if (m_proto == GpgME::OpenPGP) { m_micalg = std::string("pgp-") + hashAlg; } else { m_micalg = hashAlg; } std::transform(m_micalg.begin(), m_micalg.end(), m_micalg.begin(), ::tolower); log_debug ("%s:%s: micalg is: '%s'.", SRCNAME, __func__, m_micalg.c_str ()); TRETURN; } void CryptController::start_crypto_overlay () { TSTART; auto wid = m_mail->getWindow (); std::string text; if (m_encrypt) { text = utf8_gettext ("Encrypting..."); } else if (m_sign) { text = utf8_gettext ("Signing..."); } m_overlay = std::unique_ptr (new Overlay (wid, text)); TRETURN; } diff --git a/src/main.c b/src/main.c index 92119bf..5ce3ef1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,419 +1,420 @@ /* main.c - DLL entry point * Copyright (C) 2005, 2007, 2008 g10 Code GmbH * * This file is part of GpgOL. * * GpgOL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 * of the License, or (at your option) any later version. * * GpgOL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "mymapi.h" #include "mymapitags.h" #include "common.h" #include "mymapi.h" /* Local function prototypes. */ static char *get_locale_dir (void); static void drop_locale_dir (char *locale_dir); /* The major version of Outlook we are attached to */ int g_ol_version_major; void i18n_init (void) { char *locale_dir; #ifdef ENABLE_NLS # ifdef HAVE_LC_MESSAGES setlocale (LC_TIME, ""); setlocale (LC_MESSAGES, ""); # else setlocale (LC_ALL, "" ); # endif #endif locale_dir = get_locale_dir (); if (locale_dir) { bindtextdomain (PACKAGE_GT, locale_dir); drop_locale_dir (locale_dir); } textdomain (PACKAGE_GT); } static char * get_gpgme_w32_inst_dir (void) { char *gpg4win_dir = get_gpg4win_dir (); char *tmp; gpgrt_asprintf (&tmp, "%s\\bin\\gpgme-w32spawn.exe", gpg4win_dir); memdbg_alloc (tmp); if (!access(tmp, R_OK)) { xfree (tmp); gpgrt_asprintf (&tmp, "%s\\bin", gpg4win_dir); memdbg_alloc (tmp); xfree (gpg4win_dir); return tmp; } xfree (tmp); gpgrt_asprintf (&tmp, "%s\\gpgme-w32spawn.exe", gpg4win_dir); memdbg_alloc (tmp); if (!access(tmp, R_OK)) { xfree (tmp); return gpg4win_dir; } OutputDebugString("Failed to find gpgme-w32spawn.exe!"); return NULL; } /* Entry point called by DLL loader. */ int WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved) { (void)reserved; if (reason == DLL_PROCESS_ATTACH) { set_global_hinstance (hinst); gpg_err_init (); /* Set the installation directory for GpgME so that it can find tools like gpgme-w32-spawn correctly. */ char *instdir = get_gpgme_w32_inst_dir(); gpgme_set_global_flag ("w32-inst-dir", instdir); xfree (instdir); /* The next call initializes subsystems of gpgme and should be done as early as possible. The actual return value (the version string) is not used here. It may be called at any time later for this. */ gpgme_check_version (NULL); } else if (reason == DLL_PROCESS_DETACH) { gpg_err_deinit (0); } return TRUE; } static char * get_locale_dir (void) { char *instdir; char *p; char *dname; instdir = get_gpg4win_dir(); if (!instdir) return NULL; /* Build the key: "/share/locale". */ #define SLDIR "\\share\\locale" dname = xmalloc (strlen (instdir) + strlen (SLDIR) + 1); if (!dname) { xfree (instdir); return NULL; } p = dname; strcpy (p, instdir); p += strlen (instdir); strcpy (p, SLDIR); xfree (instdir); return dname; } static void drop_locale_dir (char *locale_dir) { xfree (locale_dir); } static int get_conf_bool (const char *name, int defaultVal) { char *val = NULL; int ret; load_extension_value (name, &val); ret = val == NULL ? defaultVal : *val != '1' ? 0 : 1; xfree (val); return ret; } static int dbg_compat (int oldval) { // We broke the debug levels at some point // This is cmpatibility code with the old // levels. #define DBG_MEMORY_OLD (1<<5) // 32 #define DBG_MIME_PARSER_OLD (1<<7) // 128 Unified as DBG_DATA #define DBG_MIME_DATA_OLD (1<<8) // 256 Unified in read_options #define DBG_OOM_OLD (1<<9) // 512 Unified as DBG_OOM #define DBG_OOM_EXTRA_OLD (1<<10)// 1024 Unified in read_options int new_dbg = oldval; if ((oldval & DBG_MEMORY_OLD)) { new_dbg |= DBG_MEMORY; new_dbg -= DBG_MEMORY_OLD; } if ((oldval & DBG_OOM_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_OLD; } if ((oldval & DBG_MIME_PARSER_OLD)) { new_dbg |= DBG_DATA; new_dbg -= DBG_MIME_PARSER_OLD; } if ((oldval & DBG_MIME_DATA_OLD)) { new_dbg |= DBG_DATA; new_dbg -= DBG_MIME_DATA_OLD; } if ((oldval & DBG_OOM_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_OLD; } if ((oldval & DBG_OOM_EXTRA_OLD)) { new_dbg |= DBG_OOM; new_dbg -= DBG_OOM_EXTRA_OLD; } #undef DBG_MEMORY_OLD #undef DBG_MIME_PARSER_OLD #undef DBG_MIME_DATA_OLD #undef DBG_OOM_OLD #undef DBG_OOM_EXTRA_OLD return new_dbg; } /* Read option settings from the Registry. */ void read_options (void) { char *val = NULL; /* Set the log file first so that output from this function is logged too. */ load_extension_value ("logFile", &val); set_log_file (val); xfree (val); val = NULL; /* Parse the debug flags. */ load_extension_value ("enableDebug", &val); opt.enable_debug = 0; if (val) { char *p, *pend; trim_spaces (val); for (p = val; p; p = pend) { pend = strpbrk (p, ", \t\n\r\f"); if (pend) { *pend++ = 0; pend += strspn (pend, ", \t\n\r\f"); } if (isascii (*p) && isdigit (*p)) { opt.enable_debug |= dbg_compat (strtoul (p, NULL, 0)); } else if (!strcmp (p, "memory")) opt.enable_debug |= DBG_MEMORY; else if (!strcmp (p, "mime-parser")) opt.enable_debug |= DBG_DATA; else if (!strcmp (p, "mime-data")) opt.enable_debug |= DBG_DATA; else if (!strcmp (p, "oom")) opt.enable_debug |= DBG_OOM; else if (!strcmp (p, "oom-extra")) opt.enable_debug |= DBG_OOM; else log_debug ("invalid debug flag `%s' ignored", p); } } else { /* To help the user enable debugging make sure that the registry key exists. Note that the other registry keys are stored after using the configuration dialog. */ store_extension_value ("enableDebug", "0"); } /* Yes we use free here because memtracing did not track the alloc as the option for debuging was not read before. */ free (val); val = NULL; if (opt.enable_debug) log_debug ("enabled debug flags:%s%s%s%s\n", (opt.enable_debug & DBG_MEMORY)? " memory":"", (opt.enable_debug & DBG_DATA)? " data":"", (opt.enable_debug & DBG_OOM)? " oom":"", (opt.enable_debug & DBG_TRACE)? " trace":"" ); opt.enable_smime = get_conf_bool ("enableSmime", 0); opt.encrypt_default = get_conf_bool ("encryptDefault", 0); opt.sign_default = get_conf_bool ("signDefault", 0); opt.inline_pgp = get_conf_bool ("inlinePGP", 0); opt.reply_crypt = get_conf_bool ("replyCrypt", 1); opt.prefer_smime = get_conf_bool ("preferSmime", 0); opt.autoresolve = get_conf_bool ("autoresolve", 1); opt.autoretrieve = get_conf_bool ("autoretrieve", 0); opt.automation = get_conf_bool ("automation", 1); opt.autosecure = get_conf_bool ("autosecure", 1); opt.autotrust = get_conf_bool ("autotrust", 0); opt.search_smime_servers = get_conf_bool ("searchSmimeServers", 0); opt.smime_html_warn_shown = get_conf_bool ("smimeHtmlWarnShown", 0); opt.smime_insecure_reply_fw_allowed = get_conf_bool ("smimeInsecureReplyAllowed", 0); opt.auto_unstrusted = get_conf_bool ("autoencryptUntrusted", 0); opt.autoimport = get_conf_bool ("autoimport", 0); opt.splitBCCMails = get_conf_bool ("splitBCCMails", 0); opt.combinedOpsEnabled = get_conf_bool ("combinedOpsEnabled", 0); opt.encryptSubject = get_conf_bool ("encryptSubject", 0); if (!opt.automation) { // Disabling automation is a shorthand to disable the // others, too. opt.autosecure = 0; opt.autoresolve = 0; opt.autotrust = 0; opt.autoretrieve = 0; opt.autoimport = 0; opt.auto_unstrusted = 0; } /* Draft encryption handling. */ if (get_conf_bool ("draftEnc", 0)) { load_extension_value ("draftKey", &val); if (val) { xfree (opt.draft_key); opt.draft_key = val; val = NULL; } } else { xfree (opt.draft_key); opt.draft_key = NULL; } opt.alwaysShowApproval = get_conf_bool ("alwaysShowApproval", 0); /* Hidden options */ opt.sync_enc = 1; //get_conf_bool ("syncEnc", 0); /* Due to an issue where async encryption would leave unencrypted mails in the recently deleted folder on the server we block it. */ opt.sync_dec = get_conf_bool ("syncDec", 0); } /* Write current options back to the Registry. */ int write_options (void) { struct { const char *name; int mode; int value; char *s_val; } table[] = { {"smimeHtmlWarnShown", 0, opt.smime_html_warn_shown, NULL}, + {"draftKey", 2, 0, opt.draft_key}, {NULL, 0, 0, NULL} }; char buf[32]; int rc, i; const char *string; for (i=0; table[i].name; i++) { switch (table[i].mode) { case 0: string = table[i].value? "1": "0"; log_debug ("storing option `%s' value=`%s'\n", table[i].name, string); rc = store_extension_value (table[i].name, string); break; case 1: sprintf (buf, "%d", table[i].value); log_debug ("storing option `%s' value=`%s'\n", table[i].name, buf); rc = store_extension_value (table[i].name, buf); break; case 2: string = table[i].s_val? table[i].s_val : ""; log_debug ("storing option `%s' value=`%s'\n", table[i].name, string); rc = store_extension_value (table[i].name, string); break; /* case 3: */ /* buf[0] = '0'; */ /* buf[1] = 0; */ /* switch (opt.default_protocol) */ /* { */ /* case PROTOCOL_UNKNOWN: buf[0] = '0'; /\* auto *\/ break; */ /* case PROTOCOL_OPENPGP: buf[0] = '1'; break; */ /* case PROTOCOL_SMIME: buf[0] = '2'; break; */ /* } */ /* log_debug ("storing option `%s' value=`%s'\n", */ /* table[i].name, buf); */ /* rc = store_extension_value (table[i].name, buf); */ /* break; */ case 4: sprintf (buf, "0x%x", table[i].value); log_debug ("storing option `%s' value=`%s'\n", table[i].name, buf); rc = store_extension_value (table[i].name, buf); break; default: rc = -1; break; } if (rc) log_error ("error storing option `%s': rc = %d\n", table[i].name, rc); } return 0; }