libreoffice/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx
Miklos Vajna 7d4222d008 cool#10630 doc electronic sign: fix insertion of the signature line
Have electronic signing configured in the LOK client, try to insert a
signature line, you'll get a certificate picker, while we don't have a
cert during esign.

What's in fact needed for creating the signature line is just a name
(previously extracted from the certificate), we can survive the lack of
actual certificate.

Fix the problem by adding a new External parameter to
.uno:InsertSignatureLine to hint that the certificate chooser should not
be opened, instead the editor name (used for comments already) should be
used. Add a new CertificateOrName in svl/ and use that in all places
where previously we wanted a certificate but in fact it's enough to have
a certificate or a name to create the signature line.

The name on the signature line is just visual feedback, the actual name
on the crypto signature is still not based on untrusted used input.

Change-Id: Ib7008112a8e28a9e7d9649745e6021dd6b6b9c39
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180193
Tested-by: Jenkins
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
2025-01-13 17:53:43 +01:00

904 lines
34 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <rtl/ustring.hxx>
#include <sal/config.h>
#include <string_view>
#include <digitalsignaturesdialog.hxx>
#include <certificatechooser.hxx>
#include <certificateviewer.hxx>
#include <biginteger.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/configuration.hxx>
#include <officecfg/Office/Common.hxx>
#include <com/sun/star/uno/SecurityException.hpp>
#include <com/sun/star/embed/XStorage.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/security/CertificateValidity.hpp>
#include <com/sun/star/security/CertificateKind.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <osl/file.hxx>
#include <sal/log.hxx>
#include <unotools/datetime.hxx>
#include <bitmaps.hlst>
#include <strings.hrc>
#include <resourcemanager.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/xmlsechelper.hxx>
#include <comphelper/processfactory.hxx>
#include <utility>
#include <vcl/weld.hxx>
#include <vcl/svapp.hxx>
#include <sfx2/viewsh.hxx>
#include <svl/cryptosign.hxx>
#ifdef _WIN32
#include <o3tl/char16_t2wchar_t.hxx>
#include <systools/win32/comtools.hxx>
#include <Shlobj.h>
#endif
#if defined MACOSX
#include <sys/stat.h>
#endif
using namespace comphelper;
using namespace css::security;
using namespace css::uno;
using namespace css;
namespace
{
#ifdef _WIN32
constexpr std::u16string_view aGUIServers[]
= { u"Gpg4win\\kleopatra.exe",
u"Gpg4win\\bin\\kleopatra.exe",
u"GNU\\GnuPG\\kleopatra.exe",
u"GNU\\GnuPG\\launch-gpa.exe",
u"GNU\\GnuPG\\gpa.exe",
u"GnuPG\\bin\\gpa.exe",
u"GNU\\GnuPG\\bin\\kleopatra.exe",
u"GNU\\GnuPG\\bin\\launch-gpa.exe",
u"GNU\\GnuPG\\bin\\gpa.exe"};
#elif defined MACOSX
constexpr std::u16string_view aGUIServers[]
= { u"/Applications/GPG Keychain.app",
u"/Applications/Trusted Key Manager.app", // tdf#147291
u"/Applications/SCinterface/scManager.app", // tdf#147291
u"/System/Applications/Utilities/Keychain Access.app"};
#else
constexpr std::u16string_view aGUIServers[]
= { u"kleopatra", u"seahorse", u"gpa", u"kgpg"};
#endif
bool GetPathAllOS(OUString& aPath)
{
#ifdef _WIN32
sal::systools::CoTaskMemAllocated<wchar_t> sPath;
HRESULT hr
= SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_DEFAULT, nullptr, &sPath);
if (FAILED(hr))
return false;
aPath = o3tl::toU(sPath);
#else
const char* cPath = getenv("PATH");
if (!cPath)
return false;
aPath = OUString(cPath, strlen(cPath), osl_getThreadTextEncoding());
#endif
return (!aPath.isEmpty());
}
void GetCertificateManager(OUString& sExecutable)
{
OUString aPath, aFoundGUIServer;
if (!GetPathAllOS(aPath))
return;
OUString aCetMgrConfig = officecfg::Office::Common::Security::Scripting::CertMgrPath::get();
if (!aCetMgrConfig.isEmpty())
{
if (aCetMgrConfig.indexOf('/') != -1
#ifdef _WIN32
|| aCetMgrConfig.indexOf('\\') != -1
#endif
)
{
sExecutable = aCetMgrConfig;
return;
}
osl::FileBase::RC searchError = osl::File::searchFileURL(
aCetMgrConfig, aPath,
aFoundGUIServer);
if (searchError == osl::FileBase::E_None)
{
osl::File::getSystemPathFromFileURL(aFoundGUIServer, sExecutable);
return;
}
}
for (const auto& rServer: aGUIServers)
{
bool bSetCertMgrPath = false;
#ifdef MACOSX
// On macOS, the list of default certificate manager applications
// includes absolute paths so check if the path exists and is a
// directory
if (rServer.starts_with('/'))
{
OString aSysPath = OUString(rServer).toUtf8();
if (struct stat st; stat(aSysPath.getStr(), &st) == 0 && S_ISDIR(st.st_mode))
{
bSetCertMgrPath = true;
sExecutable = rServer;
}
}
#endif
if (!bSetCertMgrPath)
{
osl::FileBase::RC searchError = osl::File::searchFileURL(
OUString(rServer), aPath,
aFoundGUIServer);
if (searchError == osl::FileBase::E_None && osl::File::getSystemPathFromFileURL(aFoundGUIServer, sExecutable) == osl::FileBase::E_None)
bSetCertMgrPath = true;
}
if (bSetCertMgrPath)
{
std::shared_ptr<comphelper::ConfigurationChanges> pBatch(
comphelper::ConfigurationChanges::create());
officecfg::Office::Common::Security::Scripting::CertMgrPath::set(sExecutable,
pBatch);
pBatch->commit();
return;
}
}
}
bool IsThereCertificateMgr()
{
OUString sExecutable;
GetCertificateManager(sExecutable);
return (!sExecutable.isEmpty());
}
}//anonymous namespace
DigitalSignaturesDialog::DigitalSignaturesDialog(
weld::Window* pParent,
const uno::Reference< uno::XComponentContext >& rxCtx, DocumentSignatureMode eMode,
bool bReadOnly, OUString sODFVersion, bool bHasDocumentSignature,
SfxViewShell* pViewShell)
: GenericDialogController(pParent, u"xmlsec/ui/digitalsignaturesdialog.ui"_ustr, u"DigitalSignaturesDialog"_ustr)
, maSignatureManager(rxCtx, eMode)
, m_sODFVersion (std::move(sODFVersion))
, m_bHasDocumentSignature(bHasDocumentSignature)
, m_bWarningShowSignMacro(false)
, m_pViewShell(pViewShell)
, m_xHintDocFT(m_xBuilder->weld_label(u"dochint"_ustr))
, m_xHintBasicFT(m_xBuilder->weld_label(u"macrohint"_ustr))
, m_xSignaturesLB(m_xBuilder->weld_tree_view(u"signatures"_ustr))
, m_xSigsValidImg(m_xBuilder->weld_image(u"validimg"_ustr))
, m_xSigsValidFI(m_xBuilder->weld_label(u"validft"_ustr))
, m_xSigsInvalidImg(m_xBuilder->weld_image(u"invalidimg"_ustr))
, m_xSigsInvalidFI(m_xBuilder->weld_label(u"invalidft"_ustr))
, m_xSigsNotvalidatedImg(m_xBuilder->weld_image(u"notvalidatedimg"_ustr))
, m_xSigsNotvalidatedFI(m_xBuilder->weld_label(u"notvalidatedft"_ustr))
, m_xSigsOldSignatureImg(m_xBuilder->weld_image(u"oldsignatureimg"_ustr))
, m_xSigsOldSignatureFI(m_xBuilder->weld_label(u"oldsignatureft"_ustr))
, m_xViewBtn(m_xBuilder->weld_button(u"view"_ustr))
, m_xAddBtn(m_xBuilder->weld_button(u"sign"_ustr))
, m_xRemoveBtn(m_xBuilder->weld_button(u"remove"_ustr))
, m_xStartCertMgrBtn(m_xBuilder->weld_button(u"start_certmanager"_ustr))
, m_xCloseBtn(m_xBuilder->weld_button(u"close"_ustr))
{
auto nControlWidth = m_xSignaturesLB->get_approximate_digit_width() * 105;
m_xSignaturesLB->set_size_request(nControlWidth, m_xSignaturesLB->get_height_rows(10));
// Give the first column 6 percent, try to distribute the rest equally.
std::vector<int> aWidths;
aWidths.push_back(6*nControlWidth/100);
auto nColWidth = (nControlWidth - aWidths[0]) / 4;
aWidths.push_back(nColWidth);
aWidths.push_back(nColWidth);
aWidths.push_back(nColWidth);
m_xSignaturesLB->set_column_fixed_widths(aWidths);
mbVerifySignatures = true;
mbSignaturesChanged = false;
m_xSignaturesLB->connect_selection_changed(
LINK(this, DigitalSignaturesDialog, SignatureHighlightHdl));
m_xSignaturesLB->connect_row_activated( LINK( this, DigitalSignaturesDialog, SignatureSelectHdl ) );
m_xViewBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, ViewButtonHdl ) );
m_xViewBtn->set_sensitive(false);
m_xAddBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, AddButtonHdl ) );
if ( bReadOnly )
m_xAddBtn->set_sensitive(false);
m_xRemoveBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, RemoveButtonHdl ) );
m_xRemoveBtn->set_sensitive(false);
m_xStartCertMgrBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, CertMgrButtonHdl ) );
m_xCloseBtn->connect_clicked( LINK( this, DigitalSignaturesDialog, OKButtonHdl) );
switch( maSignatureManager.getSignatureMode() )
{
case DocumentSignatureMode::Content:
m_xHintDocFT->show();
break;
case DocumentSignatureMode::Macros:
m_xHintBasicFT->show();
break;
}
if (comphelper::LibreOfficeKit::isActive())
{
// If the view has a signing certificate, then allow adding a signature.
if (!pViewShell || !pViewShell->GetSigningCertificate().m_xCertificate.is())
{
m_xAddBtn->hide();
}
m_xStartCertMgrBtn->hide();
}
if (!IsThereCertificateMgr())
{
m_xStartCertMgrBtn->set_sensitive(false);
}
}
DigitalSignaturesDialog::~DigitalSignaturesDialog()
{
if (m_xViewer)
m_xViewer->response(RET_OK);
if (m_xInfoBox)
m_xInfoBox->response(RET_OK);
}
bool DigitalSignaturesDialog::Init()
{
bool bInit = maSignatureManager.init();
SAL_WARN_IF( !bInit, "xmlsecurity.dialogs", "Error initializing security context!" );
if ( bInit )
{
maSignatureManager.getSignatureHelper().SetStartVerifySignatureHdl( LINK( this, DigitalSignaturesDialog, StartVerifySignatureHdl ) );
}
return bInit;
}
void DigitalSignaturesDialog::SetStorage( const css::uno::Reference < css::embed::XStorage >& rxStore )
{
if (!rxStore.is())
{
// PDF supports AdES.
m_bAdESCompliant = true;
return;
}
// only ODF 1.1 wants to be non-XAdES (m_sODFVersion="1.0" for OOXML somehow?)
m_bAdESCompliant = !rxStore->hasByName(u"META-INF"_ustr) // it's a Zip storage
|| !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
maSignatureManager.setStore(rxStore);
maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion, {});
}
void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream )
{
maSignatureManager.setSignatureStream(rxStream);
}
void DigitalSignaturesDialog::SetScriptingSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream )
{
if (!rxStream.is())
{
return;
}
uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
moScriptSignatureManager.emplace(xContext, DocumentSignatureMode::Macros);
if (!moScriptSignatureManager->init())
{
return;
}
moScriptSignatureManager->setStore(maSignatureManager.getStore());
// This is the storage used by UriBindingHelper::getUriBinding().
moScriptSignatureManager->getSignatureHelper().SetStorage(maSignatureManager.getStore(), m_sODFVersion);
maSignatureManager.getSignatureHelper().SetStorage(maSignatureManager.getStore(), m_sODFVersion, rxStream);
moScriptSignatureManager->setSignatureStream(rxStream);
}
bool DigitalSignaturesDialog::canAddRemove()
{
//FIXME: this func needs some cleanup, such as real split between
//'canAdd' and 'canRemove' case
uno::Reference<container::XNameAccess> xNameAccess = maSignatureManager.getStore();
if (xNameAccess.is() && xNameAccess->hasByName(u"[Content_Types].xml"_ustr))
// It's always possible to append an OOXML signature.
return true;
if (!maSignatureManager.getStore().is())
// It's always possible to append a PDF signature.
return true;
OSL_ASSERT(maSignatureManager.getStore().is());
bool bDoc1_1 = DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion);
// see specification
//cvs: specs/www/appwide/security/Electronic_Signatures_and_Security.sxw
//Paragraph 'Behavior with regard to ODF 1.2'
//For both, macro and document
if ( bDoc1_1 )
{
//#4
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Warning, VclButtonsType::Ok,
XsResId(STR_XMLSECDLG_OLD_ODF_FORMAT)));
xBox->run();
return false;
}
//As of OOo 3.2 the document signature includes in macrosignatures.xml. That is
//adding a macro signature will break an existing document signature.
//The sfx2 will remove the documentsignature when the user adds a macro signature
if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros)
{
if (m_bHasDocumentSignature && !m_bWarningShowSignMacro)
{
//The warning says that the document signatures will be removed if the user
//continues. He can then either press 'OK' or 'NO'
//It the user presses 'Add' or 'Remove' several times then, then the warning
//is shown every time until the user presses 'OK'. From then on, the warning
//is not displayed anymore as long as the signatures dialog is alive.
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(
m_xDialog.get(), VclMessageType::Question, VclButtonsType::YesNo,
XsResId(STR_XMLSECDLG_QUERY_REMOVEDOCSIGNBEFORESIGN)));
if (xBox->run() == RET_NO)
return false;
m_bWarningShowSignMacro = true;
}
}
return true;
}
bool DigitalSignaturesDialog::canAdd() { return canAddRemove(); }
void DigitalSignaturesDialog::canRemove(const std::function<void(bool)>& rCallback)
{
auto onFinished = [this, rCallback](bool bRet) {
rCallback(bRet && canAddRemove());
};
bool bRet = true;
if ( maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content )
{
std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Question, VclButtonsType::YesNo,
XsResId(STR_XMLSECDLG_QUERY_REALLYREMOVE)));
xBox->runAsync(xBox, [onFinished](sal_Int32 nDlgRet) {
onFinished(nDlgRet == RET_YES);
});
return;
}
onFinished(bRet);
}
void DigitalSignaturesDialog::beforeRun()
{
// Verify Signatures and add certificates to ListBox...
mbVerifySignatures = true;
ImplGetSignatureInformations(/*bUseTempStream=*/false, /*bCacheLastSignature=*/true);
ImplFillSignaturesBox();
// FIXME: Disable the "Use XAdES compliant signatures" checkbox if it is irrelevant. If it is
// enabled, set its initial state based on existing signatures, if any.
// If it is OOXML, the checkbox is irrelevant.
// How to find out here whether it is OOXML? I don't want to create a SignatureStreamHelper and
// check its nStorageFormat as that seems overly complicated and seems to have weird indirect
// consequences, as I noticed when I tried to use DocumentSignatureManager::IsXAdESRelevant()
// (which now is in #if 0).
if (!maSignatureManager.getCurrentSignatureInformations().empty())
{
// If the document has only SHA-1 signatures we probably want it to stay that way?
}
// Only verify once, content will not change.
// But for refreshing signature information, StartVerifySignatureHdl will be called after each add/remove
mbVerifySignatures = false;
}
short DigitalSignaturesDialog::run()
{
beforeRun();
return GenericDialogController::run();
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureHighlightHdl, weld::TreeView&, void)
{
bool bSel = m_xSignaturesLB->get_selected_index() != -1;
m_xViewBtn->set_sensitive( bSel );
if ( m_xAddBtn->get_sensitive() ) // not read only
m_xRemoveBtn->set_sensitive( bSel );
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, OKButtonHdl, weld::Button&, void)
{
if (mbSignaturesChanged)
maSignatureManager.write(m_bAdESCompliant);
m_xDialog->response(RET_OK);
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, SignatureSelectHdl, weld::TreeView&, bool)
{
ImplShowSignaturesDetails();
return true;
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, ViewButtonHdl, weld::Button&, void)
{
ImplShowSignaturesDetails();
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, weld::Button&, void)
{
if( ! canAdd())
return;
// Separate function, so the function can call itself when the certificate choosing was
// successful, but the actual signing was not.
AddButtonHdlImpl();
}
void DigitalSignaturesDialog::AddButtonHdlImpl()
{
std::vector<uno::Reference<xml::crypto::XXMLSecurityContext>> xSecContexts
{
maSignatureManager.getSecurityContext()
};
// Gpg signing is only possible with ODF >= 1.2 documents
if (DocumentSignatureHelper::CanSignWithGPG(maSignatureManager.getStore(), m_sODFVersion))
xSecContexts.push_back(maSignatureManager.getGpgSecurityContext());
std::shared_ptr<CertificateChooser> aChooser = CertificateChooser::getInstance(m_xDialog.get(), m_pViewShell, std::move(xSecContexts), CertificateChooserUserAction::Sign);
aChooser->BeforeRun();
weld::DialogController::runAsync(aChooser, [this, aChooser](sal_Int32 nRet) {
if (nRet != RET_OK)
{
return;
}
try
{
sal_Int32 nSecurityId;
svl::crypto::SigningContext aSigningContext;
aSigningContext.m_xCertificate = aChooser->GetSelectedCertificates()[0];
if (moScriptSignatureManager)
{
if (!moScriptSignatureManager->add(aSigningContext,
aChooser->GetSelectedSecurityContext(),
aChooser->GetDescription(), nSecurityId,
m_bAdESCompliant))
{
return;
}
moScriptSignatureManager->read(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
moScriptSignatureManager->write(m_bAdESCompliant);
maSignatureManager.setScriptingSignatureStream(moScriptSignatureManager->getSignatureStream());
}
if (!maSignatureManager.add(aSigningContext, aChooser->GetSelectedSecurityContext(),
aChooser->GetDescription(), nSecurityId, m_bAdESCompliant))
return;
mbSignaturesChanged = true;
xml::crypto::SecurityOperationStatus nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
if (maSignatureManager.getStore().is())
// In the PDF case the signature information is only available after parsing.
nStatus = maSignatureManager.getSignatureHelper().GetSignatureInformation( nSecurityId ).nStatus;
if ( nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED )
{
mbSignaturesChanged = true;
// Can't simply remember current information, need parsing for getting full information :(
// We need to verify the signatures again, otherwise the status in the signature information
// will not contain
// SecurityOperationStatus_OPERATION_SUCCEEDED
mbVerifySignatures = true;
ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
ImplFillSignaturesBox();
}
else
{
AddButtonHdlImpl();
}
}
catch ( uno::Exception& )
{
TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "adding a signature!" );
std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Error, VclButtonsType::Ok,
XsResId(STR_XMLSECDLG_SIGNING_FAILED)));
xBox->run();
// Don't keep invalid entries...
ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/false);
ImplFillSignaturesBox();
}
});
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, RemoveButtonHdl, weld::Button&, void)
{
canRemove([this](bool bRet) {
if (!bRet)
return;
int nEntry = m_xSignaturesLB->get_selected_index();
if (nEntry == -1)
return;
try
{
sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
maSignatureManager.remove(nSelected);
mbSignaturesChanged = true;
ImplFillSignaturesBox();
}
catch ( uno::Exception& )
{
TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "Exception while removing a signature!" );
// Don't keep invalid entries...
ImplGetSignatureInformations(/*bUseTempStream=*/true, /*bCacheLastSignature=*/true);
ImplFillSignaturesBox();
}
});
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, CertMgrButtonHdl, weld::Button&, void)
{
OUString sExecutable;
GetCertificateManager(sExecutable);
if (!sExecutable.isEmpty())
{
const uno::Reference<uno::XComponentContext>& xContext
= ::comphelper::getProcessComponentContext();
uno::Reference<css::system::XSystemShellExecute> xSystemShell(
css::system::SystemShellExecute::create(xContext));
try
{
xSystemShell->execute(sExecutable, OUString(),
css::system::SystemShellExecuteFlags::DEFAULTS);
}
catch (...)
{
// Related tdf#159307 fix uncloseable windows due to uncaught exception
// XSystemShellExecute::execute() throws an exception for a variety
// of common error conditions such as files or directories that
// are non-existent or non-executable. Failure to catch such
// exceptions would cause the document window to be uncloseable
// and the application to be unquittable.
TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "executable failed!" );
sExecutable = OUString();
}
}
OUString sDialogText = (sExecutable.isEmpty() ?
XsResId(STR_XMLSECDLG_NO_CERT_MANAGER) : XsResId(STR_XMLSECDLG_OPENED_CRTMGR) + sExecutable);
std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
m_xDialog.get(), VclMessageType::Info, VclButtonsType::Ok,
sDialogText));
xInfoBox->run();
}
IMPL_LINK_NOARG(DigitalSignaturesDialog, StartVerifySignatureHdl, LinkParamNone*, bool)
{
return mbVerifySignatures;
}
void DigitalSignaturesDialog::ImplFillSignaturesBox()
{
m_xSignaturesLB->clear();
size_t nInfos = maSignatureManager.getCurrentSignatureInformations().size();
size_t nValidSigs = 0, nValidCerts = 0;
bool bAllNewSignatures = true;
bool bSomePartial = false;
if( nInfos )
{
for( size_t n = 0; n < nInfos; ++n )
{
DocumentSignatureAlgorithm mode = DocumentSignatureHelper::getDocumentAlgorithm(
m_sODFVersion, maSignatureManager.getCurrentSignatureInformations()[n]);
std::vector< OUString > aElementsToBeVerified;
if (maSignatureManager.getStore().is())
aElementsToBeVerified = DocumentSignatureHelper::CreateElementList(maSignatureManager.getStore(), maSignatureManager.getSignatureMode(), mode);
const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[n];
uno::Reference< css::security::XCertificate > xCert = getCertificate(rInfo);
OUString aSubject;
OUString aIssuer;
OUString aDateTimeStr;
OUString aDescription;
OUString aType;
bool bCertValid = false;
if( xCert.is() )
{
//check the validity of the cert
try {
sal_Int32 certResult = getSecurityEnvironmentForCertificate(xCert)->verifyCertificate(xCert,
Sequence<uno::Reference<security::XCertificate> >());
bCertValid = certResult == css::security::CertificateValidity::VALID;
if ( bCertValid )
nValidCerts++;
} catch (css::uno::SecurityException& ) {
OSL_FAIL("Verification of certificate failed");
bCertValid = false;
}
aSubject = xmlsec::GetContentPart( xCert->getSubjectName(), xCert->getCertificateKind() );
aIssuer = xmlsec::GetContentPart( xCert->getIssuerName(), xCert->getCertificateKind() );
}
else if (!rInfo.ouGpgCertificate.isEmpty())
{
// In case we don't have the gpg key locally, get some data from the document
aIssuer = rInfo.ouGpgOwner;
}
aDateTimeStr = utl::GetDateTimeString( rInfo.stDateTime );
aDescription = rInfo.ouDescription;
// Decide type string.
if (maSignatureManager.getStore().is())
{
// OpenPGP
if (!rInfo.ouGpgCertificate.isEmpty())
aType = "OpenPGP";
// XML based: XAdES or not.
else if (rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->CertDigest.isEmpty())
aType = "XAdES";
else
aType = "XML-DSig";
}
else
{
// Assume PDF: PAdES or not.
if (rInfo.bHasSigningCertificate)
aType = "PAdES";
else
aType = "PDF";
}
bool bSigValid = rInfo.nStatus == css::xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
if ( bSigValid )
{
if (maSignatureManager.getStore().is())
{
// ZIP based.
bSigValid = DocumentSignatureHelper::checkIfAllFilesAreSigned(
aElementsToBeVerified, rInfo, mode);
}
else
{
// Assume PDF.
bSigValid = !rInfo.bPartialDocumentSignature;
}
if( bSigValid )
nValidSigs++;
else
{
bSomePartial = true;
}
}
OUString sImage;
if (!bSigValid)
{
sImage = BMP_SIG_INVALID;
}
else if (!bCertValid)
{
sImage = BMP_SIG_NOT_VALIDATED;
}
//Check if the signature is a "old" document signature, that is, which was created
//by a version of OOo previous to 3.2
// If there is no storage, then it's pointless to check storage
// stream references.
else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
&& (maSignatureManager.getStore().is() && !DocumentSignatureHelper::isOOo3_2_Signature(
maSignatureManager.getCurrentSignatureInformations()[n])))
{
sImage = BMP_SIG_NOT_VALIDATED;
bAllNewSignatures = false;
}
else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content
&& DocumentSignatureHelper::isOOo3_2_Signature(
maSignatureManager.getCurrentSignatureInformations()[n]))
{
sImage = BMP_SIG_VALID;
}
else if (maSignatureManager.getSignatureMode() == DocumentSignatureMode::Macros)
{
sImage = BMP_SIG_VALID;
}
m_xSignaturesLB->insert(nullptr, n, nullptr, nullptr,
&sImage, nullptr, false, nullptr);
m_xSignaturesLB->set_text(n, aSubject, 1);
m_xSignaturesLB->set_text(n, aIssuer, 2);
m_xSignaturesLB->set_text(n, aDateTimeStr, 3);
m_xSignaturesLB->set_text(n, aDescription, 4);
m_xSignaturesLB->set_text(n, aType, 5);
m_xSignaturesLB->set_id(n, OUString::number(n)); // misuse user data as index
}
}
bool bAllSigsValid = (nValidSigs == nInfos);
bool bAllCertsValid = (nValidCerts == nInfos);
bool bShowValidState = nInfos && (bAllSigsValid && bAllCertsValid && bAllNewSignatures);
m_xSigsValidImg->set_visible( bShowValidState);
m_xSigsValidFI->set_visible( bShowValidState );
bool bShowInvalidState = nInfos && !bAllSigsValid;
m_xSigsInvalidImg->set_visible( bShowInvalidState && !bSomePartial);
m_xSigsInvalidFI->set_visible( bShowInvalidState && !bSomePartial);
bool bShowNotValidatedState = nInfos && bAllSigsValid && !bAllCertsValid;
m_xSigsNotvalidatedImg->set_visible(bShowNotValidatedState);
m_xSigsNotvalidatedFI->set_visible(bShowNotValidatedState);
//bAllNewSignatures is always true if we are not in document mode
bool bShowOldSignature = nInfos && bAllSigsValid && bAllCertsValid && !bAllNewSignatures;
m_xSigsOldSignatureImg->set_visible(bShowOldSignature || bSomePartial);
m_xSigsOldSignatureFI->set_visible(bShowOldSignature || bSomePartial);
SignatureHighlightHdl(*m_xSignaturesLB);
}
uno::Reference<security::XCertificate> DigitalSignaturesDialog::getCertificate(const SignatureInformation& rInfo)
{
uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = maSignatureManager.getSecurityEnvironment();
uno::Reference<xml::crypto::XSecurityEnvironment> xGpgSecEnv = maSignatureManager.getGpgSecurityEnvironment();
uno::Reference<security::XCertificate> xCert;
//First we try to get the certificate which is embedded in the XML Signature
if (xSecEnv.is() && rInfo.GetSigningCertificate() && !rInfo.GetSigningCertificate()->X509Certificate.isEmpty())
xCert = xSecEnv->createCertificateFromAscii(rInfo.GetSigningCertificate()->X509Certificate);
else {
//There must be an embedded certificate because we use it to get the
//issuer name. We cannot use /Signature/KeyInfo/X509Data/X509IssuerName
//because it could be modified by an attacker. The issuer is displayed
//in the digital signature dialog.
//Comparing the X509IssuerName with the one from the X509Certificate in order
//to find out if the X509IssuerName was modified does not work. See #i62684
SAL_WARN( "xmlsecurity.dialogs", "Could not find embedded certificate!");
}
//In case there is no embedded certificate we try to get it from a local store
if (!xCert.is() && xSecEnv.is() && rInfo.GetSigningCertificate())
{
xCert = xSecEnv->getCertificate(rInfo.GetSigningCertificate()->X509IssuerName,
xmlsecurity::numericStringToBigInteger(rInfo.GetSigningCertificate()->X509SerialNumber));
}
if (!xCert.is() && xGpgSecEnv.is() && !rInfo.ouGpgKeyID.isEmpty())
xCert = xGpgSecEnv->getCertificate( rInfo.ouGpgKeyID, xmlsecurity::numericStringToBigInteger(u"") );
SAL_WARN_IF( !xCert.is(), "xmlsecurity.dialogs", "Certificate not found and can't be created!" );
return xCert;
}
uno::Reference<xml::crypto::XSecurityEnvironment> DigitalSignaturesDialog::getSecurityEnvironmentForCertificate(const uno::Reference<security::XCertificate>& xCert)
{
switch(xCert->getCertificateKind())
{
case CertificateKind_OPENPGP:
return maSignatureManager.getGpgSecurityEnvironment();
case CertificateKind_X509:
return maSignatureManager.getSecurityEnvironment();
default:
throw RuntimeException(u"Unknown certificate kind"_ustr);
}
}
//If bUseTempStream is true then the temporary signature stream is used.
//Otherwise the real signature stream is used.
void DigitalSignaturesDialog::ImplGetSignatureInformations(bool bUseTempStream, bool bCacheLastSignature)
{
maSignatureManager.read(bUseTempStream, bCacheLastSignature);
mbVerifySignatures = false;
}
void DigitalSignaturesDialog::ImplShowSignaturesDetails()
{
int nEntry = m_xSignaturesLB->get_selected_index();
if (nEntry == -1)
return;
sal_uInt16 nSelected = m_xSignaturesLB->get_id(nEntry).toUInt32();
const SignatureInformation& rInfo = maSignatureManager.getCurrentSignatureInformations()[ nSelected ];
uno::Reference<security::XCertificate> xCert = getCertificate(rInfo);
if ( xCert.is() )
{
if (m_xViewer)
m_xViewer->response(RET_OK);
uno::Reference<xml::crypto::XSecurityEnvironment> xSecEnv = getSecurityEnvironmentForCertificate(xCert);
m_xViewer = std::make_shared<CertificateViewer>(m_xDialog.get(), xSecEnv, xCert, false, nullptr);
weld::DialogController::runAsync(m_xViewer, [this] (sal_Int32) { m_xViewer = nullptr; });
}
else
{
if (m_xInfoBox)
m_xInfoBox->response(RET_OK);
m_xInfoBox = std::shared_ptr<weld::MessageDialog>(Application::CreateMessageDialog(m_xDialog.get(),
VclMessageType::Info, VclButtonsType::Ok,
XsResId(STR_XMLSECDLG_NO_CERT_FOUND)));
m_xInfoBox->runAsync(m_xInfoBox, [this] (sal_Int32) { m_xInfoBox = nullptr; });
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */