comphelper/source/misc/storagehelper.cxx                    |   38 ++++++++++--
 offapi/com/sun/star/security/XDocumentDigitalSignatures.idl |    6 +
 sfx2/source/dialog/filedlghelper.cxx                        |    2 
 xmlsecurity/inc/strings.hrc                                 |    1 
 xmlsecurity/source/component/documentdigitalsignatures.cxx  |   14 ++++
 5 files changed, 56 insertions(+), 5 deletions(-)

New commits:
commit 6a049e417b029f3733fcee05f99a3e8875aefdb8
Author:     Patrick Luby <guibmac...@gmail.com>
AuthorDate: Sun Mar 24 12:46:45 2024 -0400
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Tue Mar 26 10:01:31 2024 +0100

    tdf#160184 ask user if they want to trust an untrusted certificate
    
    gpgme contexts uses the "auto" trust model by default which only
    allows encrypting with keys that have their trust level set to
    "Ultimate". The gpg command, however, gives the user the option
    to encrypt with a certificate that has a lower trust level so
    emulate that bahavior by asking the user if they want to trust
    the certificate for just this operation only.
    
    Also, abort saving if no certificates are selected which is an
    indication that the user cancelled the Select Certificate dialog.
    
    Change-Id: I20951b1e31b2dcf8adb82243742f8c00fbaca8c2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165260
    Tested-by: Jenkins
    Reviewed-by: Patrick Luby <guibomac...@gmail.com>
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git a/comphelper/source/misc/storagehelper.cxx 
b/comphelper/source/misc/storagehelper.cxx
index 1d504cb91725..3812bf023915 100644
--- a/comphelper/source/misc/storagehelper.cxx
+++ b/comphelper/source/misc/storagehelper.cxx
@@ -476,13 +476,18 @@ uno::Sequence< beans::NamedValue > 
OStorageHelper::CreateGpgPackageEncryptionDat
     if (err)
         throw uno::RuntimeException("The GpgME library failed to initialize 
for the OpenPGP protocol.");
 
-    ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
-    if (ctx == nullptr)
-        throw uno::RuntimeException("The GpgME library failed to initialize 
for the OpenPGP protocol.");
-    ctx->setArmor(false);
-
+    bool bResetContext = true;
     for (const auto & cert : xSignCertificates)
     {
+        if (bResetContext)
+        {
+            bResetContext = false;
+            ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
+            if (ctx == nullptr)
+                throw uno::RuntimeException("The GpgME library failed to 
initialize for the OpenPGP protocol.");
+            ctx->setArmor(false);
+        }
+
         uno::Sequence < sal_Int8 > aKeyID;
         if (cert.is())
             aKeyID = cert->getSHA1Thumbprint();
@@ -504,6 +509,29 @@ uno::Sequence< beans::NamedValue > 
OStorageHelper::CreateGpgPackageEncryptionDat
             keys, plain,
             cipher, GpgME::Context::NoCompress);
 
+        // tdf#160184 ask user if they want to trust an untrusted certificate
+        // gpgme contexts uses the "auto" trust model by default which only
+        // allows encrypting with keys that have their trust level set to
+        // "Ultimate". The gpg command, however, gives the user the option
+        // to encrypt with a certificate that has a lower trust level so
+        // emulate that bahavior by asking the user if they want to trust
+        // the certificate for just this operation only.
+        if (crypt_res.error().code() == GPG_ERR_UNUSABLE_PUBKEY)
+        {
+            if (xSigner->trustUntrustedCertificate(cert))
+            {
+                // Reset the trust model back to "auto" before processing
+                // the next certificate
+                bResetContext = true;
+
+                ctx->setFlag("trust-model", "tofu+pgp");
+                ctx->setFlag("tofu-default-policy", "unknown");
+                crypt_res = ctx->encrypt(
+                    keys, plain,
+                    cipher, GpgME::Context::NoCompress);
+            }
+        }
+
         off_t result = cipher.seek(0,SEEK_SET);
         (void) result;
         assert(result == 0);
diff --git a/offapi/com/sun/star/security/XDocumentDigitalSignatures.idl 
b/offapi/com/sun/star/security/XDocumentDigitalSignatures.idl
index a90304313582..a32f77970996 100644
--- a/offapi/com/sun/star/security/XDocumentDigitalSignatures.idl
+++ b/offapi/com/sun/star/security/XDocumentDigitalSignatures.idl
@@ -212,6 +212,12 @@ interface XDocumentDigitalSignatures : 
com::sun::star::uno::XInterface
     boolean signPackageWithCertificate([in] 
::com::sun::star::security::XCertificate xCertificate,
                                                 [in] 
::com::sun::star::embed::XStorage xStorage,
                                                 [in] 
::com::sun::star::io::XStream xStream);
+
+    /** queries the user if the want to trust an untrusted certificate.
+
+        @since LibreOffice 24.2.3
+    */
+    boolean trustUntrustedCertificate([in] 
::com::sun::star::security::XCertificate xCertificate);
 };
 
 } ; } ; } ; } ;
diff --git a/sfx2/source/dialog/filedlghelper.cxx 
b/sfx2/source/dialog/filedlghelper.cxx
index 450eac25c047..2d352aef03d8 100644
--- a/sfx2/source/dialog/filedlghelper.cxx
+++ b/sfx2/source/dialog/filedlghelper.cxx
@@ -1571,6 +1571,8 @@ ErrCode FileDialogHelper_Impl::execute( 
std::vector<OUString>& rpURLList,
 
                     if ( aEncryptionData.hasElements() )
                         rpSet->Put( SfxUnoAnyItem( SID_ENCRYPTIONDATA, 
uno::Any( aEncryptionData) ) );
+                    else
+                        return ERRCODE_ABORT;
                 }
             }
             catch( const IllegalArgumentException& ){}
diff --git a/xmlsecurity/inc/strings.hrc b/xmlsecurity/inc/strings.hrc
index b450f885df6e..69b62a06dafe 100644
--- a/xmlsecurity/inc/strings.hrc
+++ b/xmlsecurity/inc/strings.hrc
@@ -65,5 +65,6 @@
 
 #define STR_BROKEN_MACRO_CERTIFICATE_DATA           
NC_("STR_BROKEN_MACRO_CERTIFICATE_DATA", "Macro security problem!

Broken certificate data: %{data}")
 #define STR_RELOAD_FILE_WARNING                     
NC_("STR_RELOAD_FILE_WARNING", "Reload the file to apply the new macro security 
level")
+#define STR_TRUST_UNTRUSTED_PUBKEY                   
NC_("STR_TRUST_UNTRUSTED_PUBKEY", "The following OpenPGP public key is not 
trusted:

%{data}

Do you want to use this untrusted OpenPGP public key?")
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/xmlsecurity/source/component/documentdigitalsignatures.cxx 
b/xmlsecurity/source/component/documentdigitalsignatures.cxx
index a06fcc81d128..f6ffd9a7566f 100644
--- a/xmlsecurity/source/component/documentdigitalsignatures.cxx
+++ b/xmlsecurity/source/component/documentdigitalsignatures.cxx
@@ -201,6 +201,9 @@ public:
                             css::uno::Reference<css::embed::XStorage> const& 
xStoragexStorage,
                             css::uno::Reference<css::io::XStream> const& 
xStream) override;
 
+    sal_Bool SAL_CALL trustUntrustedCertificate(
+                            css::uno::Reference<css::security::XCertificate> 
const& xCertificate) override;
+
     sal_Bool SAL_CALL signScriptingContentWithCertificate(
                             css::uno::Reference<css::security::XCertificate> 
const& xCertificate,
                             css::uno::Reference<css::embed::XStorage> const& 
xStoragexStorage,
@@ -840,6 +843,17 @@ sal_Bool 
DocumentDigitalSignatures::signPackageWithCertificate(
                                    DocumentSignatureMode::Package);
 }
 
+sal_Bool DocumentDigitalSignatures::trustUntrustedCertificate(
+    css::uno::Reference<css::security::XCertificate> const& xCertificate)
+{
+    OUString 
aSubjectName(comphelper::xmlsec::GetContentPart(xCertificate->getSubjectName(), 
xCertificate->getCertificateKind()));
+    OUString aMsg(XsResId(STR_TRUST_UNTRUSTED_PUBKEY));
+    aMsg = aMsg.replaceFirst("%{data}", aSubjectName);
+    std::unique_ptr<weld::MessageDialog> 
m_xQueryBox(Application::CreateMessageDialog(nullptr, VclMessageType::Error, 
VclButtonsType::YesNo, aMsg));
+    m_xQueryBox->set_default_response(RET_NO);
+    return m_xQueryBox->run() == RET_YES;
+}
+
 sal_Bool DocumentDigitalSignatures::signScriptingContentWithCertificate(
     css::uno::Reference<css::security::XCertificate> const& xCertificate,
     css::uno::Reference<css::embed::XStorage> const& xStorage,

Reply via email to