diff options
Diffstat (limited to 'xmlsecurity/source/dialogs')
-rw-r--r-- | xmlsecurity/source/dialogs/certificatechooser.cxx | 332 | ||||
-rw-r--r-- | xmlsecurity/source/dialogs/certificateviewer.cxx | 377 | ||||
-rw-r--r-- | xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx | 801 | ||||
-rw-r--r-- | xmlsecurity/source/dialogs/macrosecurity.cxx | 446 |
4 files changed, 1956 insertions, 0 deletions
diff --git a/xmlsecurity/source/dialogs/certificatechooser.cxx b/xmlsecurity/source/dialogs/certificatechooser.cxx new file mode 100644 index 000000000..3ac503521 --- /dev/null +++ b/xmlsecurity/source/dialogs/certificatechooser.cxx @@ -0,0 +1,332 @@ +/* -*- 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 <config_gpgme.h> +#include <certificatechooser.hxx> +#include <certificateviewer.hxx> +#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> +#include <com/sun/star/xml/crypto/XXMLSecurityContext.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/xmlsechelper.hxx> + +#include <com/sun/star/security/NoPasswordException.hpp> +#include <com/sun/star/security/CertificateCharacters.hpp> + +#include <o3tl/safeint.hxx> +#include <unotools/datetime.hxx> +#include <unotools/useroptions.hxx> + +#include <resourcemanager.hxx> +#include <strings.hrc> + +using namespace comphelper; +using namespace css; + +CertificateChooser::CertificateChooser(weld::Window* _pParent, + std::vector< css::uno::Reference< css::xml::crypto::XXMLSecurityContext > > && rxSecurityContexts, + UserAction eAction) + : GenericDialogController(_pParent, "xmlsec/ui/selectcertificatedialog.ui", "SelectCertificateDialog") + , meAction(eAction) + , m_xFTSign(m_xBuilder->weld_label("sign")) + , m_xFTEncrypt(m_xBuilder->weld_label("encrypt")) + , m_xCertLB(m_xBuilder->weld_tree_view("signatures")) + , m_xViewBtn(m_xBuilder->weld_button("viewcert")) + , m_xOKBtn(m_xBuilder->weld_button("ok")) + , m_xFTDescription(m_xBuilder->weld_label("description-label")) + , m_xDescriptionED(m_xBuilder->weld_entry("description")) +{ + auto nControlWidth = m_xCertLB->get_approximate_digit_width() * 105; + m_xCertLB->set_size_request(nControlWidth, m_xCertLB->get_height_rows(12)); + + std::vector<int> aWidths + { + o3tl::narrowing<int>(30*nControlWidth/100), + o3tl::narrowing<int>(30*nControlWidth/100), + o3tl::narrowing<int>(10*nControlWidth/100), + o3tl::narrowing<int>(20*nControlWidth/100) + }; + m_xCertLB->set_column_fixed_widths(aWidths); + m_xCertLB->connect_changed( LINK( this, CertificateChooser, CertificateHighlightHdl ) ); + m_xCertLB->connect_row_activated( LINK( this, CertificateChooser, CertificateSelectHdl ) ); + m_xViewBtn->connect_clicked( LINK( this, CertificateChooser, ViewButtonHdl ) ); + + mxSecurityContexts = std::move(rxSecurityContexts); + mbInitialized = false; + + // disable buttons + CertificateHighlightHdl(*m_xCertLB); +} + +CertificateChooser::~CertificateChooser() +{ +} + +short CertificateChooser::run() +{ + // #i48432# + // We can't check for personal certificates before raising this dialog, + // because the mozilla implementation throws a NoPassword exception, + // if the user pressed cancel, and also if the database does not exist! + // But in the later case, the is no password query, and the user is confused + // that nothing happens when pressing "Add..." in the SignatureDialog. + + // PostUserEvent( LINK( this, CertificateChooser, Initialize ) ); + + // PostUserLink behavior is too slow, so do it directly before Execute(). + // Problem: This Dialog should be visible right now, and the parent should not be accessible. + // Show, Update, DisableInput... + + m_xDialog->show(); + ImplInitialize(); + return GenericDialogController::run(); +} + +void CertificateChooser::HandleOneUsageBit(OUString& string, int& bits, int bit, TranslateId pResId) +{ + if (bits & bit) + { + if (!string.isEmpty()) + string += ", "; + string += XsResId(pResId); + bits &= ~bit; + } +} + +OUString CertificateChooser::UsageInClearText(int bits) +{ + OUString result; + + HandleOneUsageBit(result, bits, 0x80, STR_DIGITAL_SIGNATURE); + HandleOneUsageBit(result, bits, 0x40, STR_NON_REPUDIATION); + HandleOneUsageBit(result, bits, 0x20, STR_KEY_ENCIPHERMENT); + HandleOneUsageBit(result, bits, 0x10, STR_DATA_ENCIPHERMENT); + HandleOneUsageBit(result, bits, 0x08, STR_KEY_AGREEMENT); + HandleOneUsageBit(result, bits, 0x04, STR_KEY_CERT_SIGN); + HandleOneUsageBit(result, bits, 0x02, STR_CRL_SIGN); + HandleOneUsageBit(result, bits, 0x01, STR_ENCIPHER_ONLY); + + // Check for mystery leftover bits + if (bits != 0) + { + if (!result.isEmpty()) + result += ", "; + result += "0x" + OUString::number(bits, 16); + } + + return result; +} + +void CertificateChooser::ImplInitialize() +{ + if ( mbInitialized ) + return; + + SvtUserOptions aUserOpts; + + switch (meAction) + { + case UserAction::Sign: + m_xFTSign->show(); + m_xOKBtn->set_label(XsResId(STR_SIGN)); + msPreferredKey = aUserOpts.GetSigningKey(); + break; + + case UserAction::SelectSign: + m_xFTSign->show(); + m_xOKBtn->set_label(XsResId(STR_SELECTSIGN)); + msPreferredKey = aUserOpts.GetSigningKey(); + break; + + case UserAction::Encrypt: + m_xFTEncrypt->show(); + m_xFTDescription->hide(); + m_xDescriptionED->hide(); + m_xCertLB->set_selection_mode(SelectionMode::Multiple); + m_xOKBtn->set_label(XsResId(STR_ENCRYPT)); + msPreferredKey = aUserOpts.GetEncryptionKey(); + break; + + } + + for (auto &secContext : mxSecurityContexts) + { + if (!secContext.is()) + continue; + auto secEnvironment = secContext->getSecurityEnvironment(); + if (!secEnvironment.is()) + continue; + + uno::Sequence< uno::Reference< security::XCertificate > > xCerts; + try + { + if ( meAction == UserAction::Sign || meAction == UserAction::SelectSign) + xCerts = secEnvironment->getPersonalCertificates(); + else + xCerts = secEnvironment->getAllCertificates(); + } + catch (security::NoPasswordException&) + { + } + + for( sal_Int32 nCert = xCerts.getLength(); nCert; ) + { + uno::Reference< security::XCertificate > xCert = xCerts[ --nCert ]; + // Check if we have a private key for this... + tools::Long nCertificateCharacters = secEnvironment->getCertificateCharacters(xCert); + + if (!(nCertificateCharacters & security::CertificateCharacters::HAS_PRIVATE_KEY)) + { + ::comphelper::removeElementAt( xCerts, nCert ); + } + } + + + // fill list of certificates; the first entry will be selected + for ( const auto& xCert : std::as_const(xCerts) ) + { + std::shared_ptr<UserData> userData = std::make_shared<UserData>(); + userData->xCertificate = xCert; + userData->xSecurityContext = secContext; + userData->xSecurityEnvironment = secEnvironment; + mvUserData.push_back(userData); + + OUString sIssuer = xmlsec::GetContentPart( xCert->getIssuerName(), xCert->getCertificateKind()); + + m_xCertLB->append(); + int nRow = m_xCertLB->n_children() - 1; + m_xCertLB->set_text(nRow, xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind()), 0); + m_xCertLB->set_text(nRow, sIssuer, 1); + m_xCertLB->set_text(nRow, xmlsec::GetCertificateKind(xCert->getCertificateKind()), 2); + m_xCertLB->set_text(nRow, utl::GetDateString(xCert->getNotValidAfter()), 3); + m_xCertLB->set_text(nRow, UsageInClearText(xCert->getCertificateUsage()), 4); + OUString sId(weld::toId(userData.get())); + m_xCertLB->set_id(nRow, sId); + +#if HAVE_FEATURE_GPGME + // only GPG has preferred keys + if ( !sIssuer.isEmpty() && !msPreferredKey.isEmpty() ) { + if ( sIssuer == msPreferredKey ) + { + if ( meAction == UserAction::Sign || meAction == UserAction::SelectSign ) + m_xCertLB->select(nRow); + else if ( meAction == UserAction::Encrypt && + aUserOpts.GetEncryptToSelf() ) + mxEncryptToSelf = xCert; + } + } +#endif + } + } + + // enable/disable buttons + CertificateHighlightHdl(*m_xCertLB); + mbInitialized = true; +} + +uno::Sequence<uno::Reference< css::security::XCertificate > > CertificateChooser::GetSelectedCertificates() +{ + std::vector< uno::Reference< css::security::XCertificate > > aRet; + if (meAction == UserAction::Encrypt) + { + // for encryption, multiselection is enabled + m_xCertLB->selected_foreach([this, &aRet](weld::TreeIter& rEntry){ + UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(rEntry)); + aRet.push_back( userData->xCertificate ); + return false; + }); + } + else + { + uno::Reference< css::security::XCertificate > xCert; + int nSel = m_xCertLB->get_selected_index(); + if (nSel != -1) + { + UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel)); + xCert = userData->xCertificate; + } + aRet.push_back( xCert ); + } + +#if HAVE_FEATURE_GPGME + if ( mxEncryptToSelf.is()) + aRet.push_back( mxEncryptToSelf ); +#endif + + return comphelper::containerToSequence(aRet); +} + +uno::Reference<xml::crypto::XXMLSecurityContext> CertificateChooser::GetSelectedSecurityContext() const +{ + int nSel = m_xCertLB->get_selected_index(); + if (nSel == -1) + return uno::Reference<xml::crypto::XXMLSecurityContext>(); + + UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel)); + uno::Reference<xml::crypto::XXMLSecurityContext> xCert = userData->xSecurityContext; + return xCert; +} + +OUString CertificateChooser::GetDescription() const +{ + return m_xDescriptionED->get_text(); +} + +OUString CertificateChooser::GetUsageText() +{ + uno::Sequence< uno::Reference<css::security::XCertificate> > xCerts = + GetSelectedCertificates(); + return (xCerts.hasElements() && xCerts[0].is()) ? + UsageInClearText(xCerts[0]->getCertificateUsage()) : OUString(); +} + +IMPL_LINK_NOARG(CertificateChooser, CertificateHighlightHdl, weld::TreeView&, void) +{ + bool bEnable = m_xCertLB->get_selected_index() != -1; + m_xViewBtn->set_sensitive(bEnable); + m_xOKBtn->set_sensitive(bEnable); + m_xDescriptionED->set_sensitive(bEnable); +} + +IMPL_LINK_NOARG(CertificateChooser, CertificateSelectHdl, weld::TreeView&, bool) +{ + m_xDialog->response(RET_OK); + return true; +} + +IMPL_LINK_NOARG(CertificateChooser, ViewButtonHdl, weld::Button&, void) +{ + ImplShowCertificateDetails(); +} + +void CertificateChooser::ImplShowCertificateDetails() +{ + int nSel = m_xCertLB->get_selected_index(); + if (nSel == -1) + return; + + UserData* userData = weld::fromId<UserData*>(m_xCertLB->get_id(nSel)); + + if (!userData->xSecurityEnvironment.is() || !userData->xCertificate.is()) + return; + + CertificateViewer aViewer(m_xDialog.get(), userData->xSecurityEnvironment, userData->xCertificate, true, this); + aViewer.run(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/source/dialogs/certificateviewer.cxx b/xmlsecurity/source/dialogs/certificateviewer.cxx new file mode 100644 index 000000000..1801cdf53 --- /dev/null +++ b/xmlsecurity/source/dialogs/certificateviewer.cxx @@ -0,0 +1,377 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <certificatechooser.hxx> +#include <certificateviewer.hxx> +#include <com/sun/star/security/XCertificate.hpp> + +#include <com/sun/star/security/CertificateCharacters.hpp> +#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> +#include <com/sun/star/security/CertificateValidity.hpp> + +#include <unotools/localedatawrapper.hxx> +#include <unotools/datetime.hxx> + +#include <strings.hrc> +#include <resourcemanager.hxx> +#include <comphelper/xmlsechelper.hxx> +#include <tools/datetime.hxx> +#include <bitmaps.hlst> + +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> + +using namespace comphelper; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +CertificateViewer::CertificateViewer(weld::Window* _pParent, + const css::uno::Reference< css::xml::crypto::XSecurityEnvironment >& _rxSecurityEnvironment, + const css::uno::Reference< css::security::XCertificate >& _rXCert, bool bCheckForPrivateKey, + CertificateChooser* pParentChooser) + : GenericDialogController(_pParent, "xmlsec/ui/viewcertdialog.ui", "ViewCertDialog") + , mbCheckForPrivateKey(bCheckForPrivateKey) + , mpParentChooser(pParentChooser) + , mxTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) +{ + mxTabCtrl->connect_enter_page(LINK(this, CertificateViewer, ActivatePageHdl)); + + mxSecurityEnvironment = _rxSecurityEnvironment; + mxCert = _rXCert; + + mxGeneralPage.reset(new CertificateViewerGeneralTP(mxTabCtrl->get_page("general"), this)); + mxDetailsPage.reset(new CertificateViewerDetailsTP(mxTabCtrl->get_page("details"), this)); + if (!mxSecurityEnvironment->buildCertificatePath(mxCert).hasElements()) + mxTabCtrl->remove_page("path"); + else + mxPathId.reset(new CertificateViewerCertPathTP(mxTabCtrl->get_page("path"), this)); + mxTabCtrl->set_current_page("general"); +} + +IMPL_LINK(CertificateViewer, ActivatePageHdl, const OString&, rPage, void) +{ + if (rPage == "path") + mxPathId->ActivatePage(); +} + +CertificateViewerTP::CertificateViewerTP(weld::Container* pParent, const OUString& rUIXMLDescription, + const OString& rID, CertificateViewer* pDlg) + : mxBuilder(Application::CreateBuilder(pParent, rUIXMLDescription)) + , mxContainer(mxBuilder->weld_container(rID)) + , mpDlg(pDlg) +{ +} + +CertificateViewerGeneralTP::CertificateViewerGeneralTP(weld::Container* pParent, CertificateViewer* pDlg) + : CertificateViewerTP(pParent, "xmlsec/ui/certgeneral.ui", "CertGeneral", pDlg) + , m_xCertImg(mxBuilder->weld_image("certimage")) + , m_xHintNotTrustedFT(mxBuilder->weld_label("hintnotrust")) + , m_xIssuedToLabelFT(mxBuilder->weld_label("issued_to")) + , m_xIssuedToFT(mxBuilder->weld_label("issued_to_value")) + , m_xIssuedByLabelFT(mxBuilder->weld_label("issued_by")) + , m_xIssuedByFT(mxBuilder->weld_label("issued_by_value")) + , m_xValidFromDateFT(mxBuilder->weld_label("valid_from_value")) + , m_xValidToDateFT(mxBuilder->weld_label("valid_to_value")) + , m_xKeyImg(mxBuilder->weld_image("keyimage")) + , m_xHintCorrespPrivKeyFT(mxBuilder->weld_label("privatekey")) +{ + //Verify the certificate + sal_Int32 certStatus = mpDlg->mxSecurityEnvironment->verifyCertificate(mpDlg->mxCert, + Sequence<Reference<css::security::XCertificate> >()); + + bool bCertValid = certStatus == css::security::CertificateValidity::VALID; + + if ( !bCertValid ) + { + m_xCertImg->set_from_icon_name(BMP_STATE_NOT_VALIDATED); + m_xHintNotTrustedFT->set_label(XsResId(STR_CERTIFICATE_NOT_VALIDATED)); + } + + // insert data + css::uno::Reference< css::security::XCertificate > xCert = mpDlg->mxCert; + + OUString sSubjectName(xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind())); + if (!sSubjectName.isEmpty()) + m_xIssuedToFT->set_label(sSubjectName); + else + m_xIssuedToLabelFT->hide(); + OUString sIssuerName(xmlsec::GetContentPart(xCert->getIssuerName(), xCert->getCertificateKind())); + if (!sIssuerName.isEmpty()) + m_xIssuedByFT->set_label(sIssuerName); + else + m_xIssuedByLabelFT->hide(); + + DateTime aDateTimeStart( DateTime::EMPTY ); + DateTime aDateTimeEnd( DateTime::EMPTY ); + utl::typeConvert( xCert->getNotValidBefore(), aDateTimeStart ); + utl::typeConvert( xCert->getNotValidAfter(), aDateTimeEnd ); + + OUString sValidFromDate = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTimeStart.GetDate())); + OUString sValidToDate = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTimeEnd.GetDate())); + + m_xValidFromDateFT->set_label(sValidFromDate); + m_xValidToDateFT->set_label(sValidToDate); + + // Check if we have the private key... + bool bHasPrivateKey = false; + // #i41270# Check only if we have that certificate in our security environment + if (pDlg->mbCheckForPrivateKey) + { + tools::Long nCertificateCharacters = pDlg->mxSecurityEnvironment->getCertificateCharacters(xCert); + bHasPrivateKey = (nCertificateCharacters & security::CertificateCharacters::HAS_PRIVATE_KEY); + } + if (!bHasPrivateKey) + { + m_xKeyImg->hide(); + m_xHintCorrespPrivKeyFT->hide(); + } +} + +void CertificateViewerDetailsTP::InsertElement(const OUString& rField, const OUString& rValue, + const OUString& rDetails, bool bFixedWidthFont) +{ + m_aUserData.emplace_back(std::make_unique<Details_UserDatat>(rDetails, bFixedWidthFont)); + OUString sId(weld::toId(m_aUserData.back().get())); + m_xElementsLB->append(sId, rField); + m_xElementsLB->set_text(m_xElementsLB->n_children() -1, rValue, 1); +} + +CertificateViewerDetailsTP::CertificateViewerDetailsTP(weld::Container* pParent, CertificateViewer* pDlg) + : CertificateViewerTP(pParent, "xmlsec/ui/certdetails.ui", "CertDetails", pDlg) + , m_xElementsLB(mxBuilder->weld_tree_view("tablecontainer")) + , m_xValueDetails(mxBuilder->weld_text_view("valuedetails")) +{ + const int nWidth = m_xElementsLB->get_approximate_digit_width() * 60; + const int nHeight = m_xElementsLB->get_height_rows(8); + m_xElementsLB->set_size_request(nWidth, nHeight); + m_xValueDetails->set_size_request(nWidth, nHeight); + m_xElementsLB->set_column_fixed_widths( { nWidth / 2 } ); + + // fill list box + Reference< security::XCertificate > xCert = mpDlg->mxCert; + sal_uInt16 nLineBreak = 16; + const char* const pHexSep = " "; + OUString aLBEntry; + OUString aDetails; + // Certificate Versions are reported wrong (#i35107#) - 0 == "V1", 1 == "V2", ..., n = "V(n+1)" + aLBEntry = "V" + OUString::number( xCert->getVersion() + 1 ); + InsertElement( XsResId( STR_VERSION ), aLBEntry, aLBEntry ); + Sequence< sal_Int8 > aSeq = xCert->getSerialNumber(); + aLBEntry = xmlsec::GetHexString( aSeq, pHexSep ); + aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak ); + InsertElement( XsResId( STR_SERIALNUM ), aLBEntry, aDetails, true ); + + std::pair< OUString, OUString> pairIssuer = + xmlsec::GetDNForCertDetailsView(xCert->getIssuerName()); + aLBEntry = pairIssuer.first; + aDetails = pairIssuer.second; + InsertElement( XsResId( STR_ISSUER ), aLBEntry, aDetails ); + + DateTime aDateTime( DateTime::EMPTY ); + utl::typeConvert( xCert->getNotValidBefore(), aDateTime ); + aLBEntry = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTime.GetDate())) + " "; + aLBEntry += Application::GetSettings().GetUILocaleDataWrapper().getTime(tools::Time(aDateTime.GetTime())); + InsertElement( XsResId( STR_VALIDFROM ), aLBEntry, aLBEntry ); + utl::typeConvert( xCert->getNotValidAfter(), aDateTime ); + aLBEntry = Application::GetSettings().GetUILocaleDataWrapper().getDate(Date(aDateTime.GetDate()) ) + " "; + aLBEntry += Application::GetSettings().GetUILocaleDataWrapper().getTime(tools::Time(aDateTime.GetTime())); + InsertElement( XsResId( STR_VALIDTO ), aLBEntry, aLBEntry ); + + std::pair< OUString, OUString > pairSubject = + xmlsec::GetDNForCertDetailsView(xCert->getSubjectName()); + aLBEntry = pairSubject.first; + aDetails = pairSubject.second; + InsertElement( XsResId( STR_SUBJECT ), aLBEntry, aDetails ); + + aLBEntry = aDetails = xCert->getSubjectPublicKeyAlgorithm(); + InsertElement( XsResId( STR_SUBJECT_PUBKEY_ALGO ), aLBEntry, aDetails ); + aSeq = xCert->getSubjectPublicKeyValue(); + aLBEntry = xmlsec::GetHexString( aSeq, pHexSep ); + aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak ); + InsertElement( XsResId( STR_SUBJECT_PUBKEY_VAL ), aLBEntry, aDetails, true ); + + aLBEntry = aDetails = xCert->getSignatureAlgorithm(); + InsertElement( XsResId( STR_SIGNATURE_ALGO ), aLBEntry, aDetails ); + + CertificateChooser* pChooser = mpDlg->GetParentChooser(); + if (pChooser) + { + aLBEntry = CertificateChooser::UsageInClearText( mpDlg->mxCert->getCertificateUsage() ); + InsertElement( XsResId( STR_USE ), aLBEntry, aLBEntry ); + } + + aSeq = xCert->getSHA1Thumbprint(); + aLBEntry = xmlsec::GetHexString( aSeq, pHexSep ); + aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak ); + InsertElement( XsResId( STR_THUMBPRINT_SHA1 ), aLBEntry, aDetails, true ); + + aSeq = xCert->getMD5Thumbprint(); + aLBEntry = xmlsec::GetHexString( aSeq, pHexSep ); + aDetails = xmlsec::GetHexString( aSeq, pHexSep, nLineBreak ); + InsertElement( XsResId( STR_THUMBPRINT_MD5 ), aLBEntry, aDetails, true ); + + m_xElementsLB->connect_changed(LINK(this, CertificateViewerDetailsTP, ElementSelectHdl)); +} + +IMPL_LINK_NOARG(CertificateViewerDetailsTP, ElementSelectHdl, weld::TreeView&, void) +{ + int nEntry = m_xElementsLB->get_selected_index(); + OUString aElementText; + bool bFixedWidthFont; + if (nEntry != -1) + { + const Details_UserDatat* p = weld::fromId<Details_UserDatat*>(m_xElementsLB->get_id(nEntry)); + aElementText = p->maTxt; + bFixedWidthFont = p->mbFixedWidthFont; + } + else + bFixedWidthFont = false; + + m_xValueDetails->set_monospace(bFixedWidthFont); + m_xValueDetails->set_text(aElementText); +} + +CertificateViewerCertPathTP::CertificateViewerCertPathTP(weld::Container* pParent, CertificateViewer* pDlg) + : CertificateViewerTP(pParent, "xmlsec/ui/certpage.ui", "CertPage", pDlg) + , mpParent(pDlg) + , mbFirstActivateDone(false) + , mxCertPathLB(mxBuilder->weld_tree_view("signatures")) + , mxScratchIter(mxCertPathLB->make_iterator()) + , mxViewCertPB(mxBuilder->weld_button("viewcert")) + , mxCertStatusML(mxBuilder->weld_text_view("status")) + , mxCertOK(mxBuilder->weld_label("certok")) + , mxCertNotValidated(mxBuilder->weld_label("certnotok")) +{ + const int nWidth = mxCertPathLB->get_approximate_digit_width() * 60; + const int nHeight = mxCertPathLB->get_height_rows(6); + mxCertPathLB->set_size_request(nWidth, nHeight); + mxCertStatusML->set_size_request(nWidth, nHeight); + + mxCertPathLB->connect_changed( LINK( this, CertificateViewerCertPathTP, CertSelectHdl ) ); + mxViewCertPB->connect_clicked( LINK( this, CertificateViewerCertPathTP, ViewCertHdl ) ); +} + +CertificateViewerCertPathTP::~CertificateViewerCertPathTP() +{ + if (mxCertificateViewer) + mxCertificateViewer->response(RET_OK); +} + +void CertificateViewerCertPathTP::ActivatePage() +{ + if ( mbFirstActivateDone ) + return; + + mbFirstActivateDone = true; + Sequence< Reference< security::XCertificate > > aCertPath = + mpParent->mxSecurityEnvironment->buildCertificatePath( mpParent->mxCert ); + const Reference< security::XCertificate >* pCertPath = aCertPath.getConstArray(); + + sal_Int32 i, nCnt = aCertPath.getLength(); + std::unique_ptr<weld::TreeIter> xParent; + for (i = nCnt-1; i >= 0; i--) + { + const Reference< security::XCertificate > rCert = pCertPath[ i ]; + OUString sName = xmlsec::GetContentPart( rCert->getSubjectName(), rCert->getCertificateKind() ); + //Verify the certificate + sal_Int32 certStatus = mpDlg->mxSecurityEnvironment->verifyCertificate(rCert, + Sequence<Reference<css::security::XCertificate> >()); + bool bCertValid = certStatus == css::security::CertificateValidity::VALID; + InsertCert(xParent.get(), sName, rCert, bCertValid); + if (!xParent) + { + xParent = mxCertPathLB->make_iterator(); + (void)mxCertPathLB->get_iter_first(*xParent); + } + else + { + (void)mxCertPathLB->iter_children(*xParent); + } + } + + if (xParent) + mxCertPathLB->select(*xParent); + mxViewCertPB->set_sensitive(false); // Own certificate selected + + while (xParent) + { + mxCertPathLB->expand_row(*xParent); + if (!mxCertPathLB->iter_parent(*xParent)) + xParent.reset(); + } + + CertSelectHdl(*mxCertPathLB); +} + +IMPL_LINK_NOARG(CertificateViewerCertPathTP, ViewCertHdl, weld::Button&, void) +{ + std::unique_ptr<weld::TreeIter> xIter = mxCertPathLB->make_iterator(); + if (mxCertPathLB->get_selected(xIter.get())) + { + if (mxCertificateViewer) + mxCertificateViewer->response(RET_OK); + + CertPath_UserData* pData = weld::fromId<CertPath_UserData*>(mxCertPathLB->get_id(*xIter)); + mxCertificateViewer = std::make_shared<CertificateViewer>(mpDlg->getDialog(), mpDlg->mxSecurityEnvironment, + pData->mxCert, false, nullptr); + weld::DialogController::runAsync(mxCertificateViewer, [this] (sal_Int32) { mxCertificateViewer = nullptr; }); + } +} + +IMPL_LINK_NOARG(CertificateViewerCertPathTP, CertSelectHdl, weld::TreeView&, void) +{ + OUString sStatus; + + std::unique_ptr<weld::TreeIter> xIter = mxCertPathLB->make_iterator(); + bool bEntry = mxCertPathLB->get_selected(xIter.get()); + if (bEntry) + { + CertPath_UserData* pData = weld::fromId<CertPath_UserData*>(mxCertPathLB->get_id(*xIter)); + if (pData) + sStatus = pData->mbValid ? mxCertOK->get_label() : mxCertNotValidated->get_label(); + } + + mxCertStatusML->set_text(sStatus); + + bool bSensitive = false; + if (bEntry) + { + // if has children, so not the last one in the chain + if (mxCertPathLB->iter_children(*xIter)) + bSensitive = true; + } + mxViewCertPB->set_sensitive(bSensitive); +} + +void CertificateViewerCertPathTP::InsertCert(const weld::TreeIter* pParent, const OUString& rName, + const css::uno::Reference< css::security::XCertificate >& rxCert, + bool bValid) +{ + auto const sImage = bValid ? std::u16string_view(u"" BMP_CERT_OK) : std::u16string_view(u"" BMP_CERT_NOT_OK); + maUserData.emplace_back(std::make_unique<CertPath_UserData>(rxCert, bValid)); + OUString sId(weld::toId(maUserData.back().get())); + mxCertPathLB->insert(pParent, -1, &rName, &sId, nullptr, nullptr, false, mxScratchIter.get()); + mxCertPathLB->set_image(*mxScratchIter, OUString(sImage)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx new file mode 100644 index 000000000..1d40e7562 --- /dev/null +++ b/xmlsecurity/source/dialogs/digitalsignaturesdialog.cxx @@ -0,0 +1,801 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <digitalsignaturesdialog.hxx> +#include <certificatechooser.hxx> +#include <certificateviewer.hxx> +#include <biginteger.hxx> +#include <sax/tools/converter.hxx> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/embed/XStorage.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/security/NoPasswordException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/security/CertificateValidity.hpp> +#include <com/sun/star/packages/WrongPasswordException.hpp> +#include <com/sun/star/security/CertificateKind.hpp> +#include <com/sun/star/security/XDocumentDigitalSignatures.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/system/SystemShellExecuteException.hpp> + +#include <osl/file.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/uri.hxx> +#include <sal/log.hxx> + +#include <tools/date.hxx> +#include <tools/time.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 <vcl/weld.hxx> +#include <vcl/svapp.hxx> +#include <unotools/configitem.hxx> + +#ifdef _WIN32 +#include <o3tl/char16_t2wchar_t.hxx> +#include <prewin.h> +#include <Shlobj.h> +#endif + +using namespace comphelper; +using namespace css::security; +using namespace css::uno; +using namespace css; + +namespace +{ + class SaveODFItem: public utl::ConfigItem + { + private: + sal_Int16 m_nODF; + + virtual void ImplCommit() override; + + public: + virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override; + SaveODFItem(); + //See group ODF in Common.xcs + bool isLessODF1_2() const + { + return m_nODF < 3; + } + }; + + void SaveODFItem::ImplCommit() {} + void SaveODFItem::Notify( const css::uno::Sequence< OUString >& ) {} + + SaveODFItem::SaveODFItem(): utl::ConfigItem("Office.Common/Save"), m_nODF(0) + { + OUString sDef("ODF/DefaultVersion"); + Sequence< css::uno::Any > aValues = GetProperties( Sequence<OUString>(&sDef,1) ); + if ( aValues.getLength() != 1) + throw uno::RuntimeException( + "[xmlsecurity] Could not open property Office.Common/Save/ODF/DefaultVersion", + nullptr); + + sal_Int16 nTmp = 0; + if ( !(aValues[0] >>= nTmp) ) + throw uno::RuntimeException( + "[xmlsecurity]SaveODFItem::SaveODFItem(): Wrong Type!", + nullptr ); + + m_nODF = nTmp; + } +} + +DigitalSignaturesDialog::DigitalSignaturesDialog( + weld::Window* pParent, + const uno::Reference< uno::XComponentContext >& rxCtx, DocumentSignatureMode eMode, + bool bReadOnly, const OUString& sODFVersion, bool bHasDocumentSignature) + : GenericDialogController(pParent, "xmlsec/ui/digitalsignaturesdialog.ui", "DigitalSignaturesDialog") + , maSignatureManager(rxCtx, eMode) + , m_sODFVersion (sODFVersion) + , m_bHasDocumentSignature(bHasDocumentSignature) + , m_bWarningShowSignMacro(false) + , m_xHintDocFT(m_xBuilder->weld_label("dochint")) + , m_xHintBasicFT(m_xBuilder->weld_label("macrohint")) + , m_xHintPackageFT(m_xBuilder->weld_label("packagehint")) + , m_xSignaturesLB(m_xBuilder->weld_tree_view("signatures")) + , m_xSigsValidImg(m_xBuilder->weld_image("validimg")) + , m_xSigsValidFI(m_xBuilder->weld_label("validft")) + , m_xSigsInvalidImg(m_xBuilder->weld_image("invalidimg")) + , m_xSigsInvalidFI(m_xBuilder->weld_label("invalidft")) + , m_xSigsNotvalidatedImg(m_xBuilder->weld_image("notvalidatedimg")) + , m_xSigsNotvalidatedFI(m_xBuilder->weld_label("notvalidatedft")) + , m_xSigsOldSignatureImg(m_xBuilder->weld_image("oldsignatureimg")) + , m_xSigsOldSignatureFI(m_xBuilder->weld_label("oldsignatureft")) + , m_xAdESCompliantCB(m_xBuilder->weld_check_button("adescompliant")) + , m_xViewBtn(m_xBuilder->weld_button("view")) + , m_xAddBtn(m_xBuilder->weld_button("sign")) + , m_xRemoveBtn(m_xBuilder->weld_button("remove")) + , m_xStartCertMgrBtn(m_xBuilder->weld_button("start_certmanager")) + , m_xCloseBtn(m_xBuilder->weld_button("close")) +{ + m_bAdESCompliant = !DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion); + + 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_changed( LINK( this, DigitalSignaturesDialog, SignatureHighlightHdl ) ); + m_xSignaturesLB->connect_row_activated( LINK( this, DigitalSignaturesDialog, SignatureSelectHdl ) ); + + m_xAdESCompliantCB->connect_toggled( LINK( this, DigitalSignaturesDialog, AdESCompliantCheckBoxHdl ) ); + m_xAdESCompliantCB->set_active(m_bAdESCompliant); + + 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; + case DocumentSignatureMode::Package: + m_xHintPackageFT->show(); + break; + } + + if (comphelper::LibreOfficeKit::isActive()) + { + m_xAddBtn->hide(); + m_xRemoveBtn->hide(); + m_xStartCertMgrBtn->hide(); + } +} + +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; + m_xAdESCompliantCB->set_active(m_bAdESCompliant); + return; + } + + maSignatureManager.setStore(rxStore); + maSignatureManager.getSignatureHelper().SetStorage( maSignatureManager.getStore(), m_sODFVersion); +} + +void DigitalSignaturesDialog::SetSignatureStream( const css::uno::Reference < css::io::XStream >& rxStream ) +{ + maSignatureManager.setSignatureStream(rxStream); +} + +bool DigitalSignaturesDialog::canAddRemove() +{ + //FIXME: this func needs some cleanup, such as real split between + //'canAdd' and 'canRemove' case + bool ret = true; + + uno::Reference<container::XNameAccess> xNameAccess = maSignatureManager.getStore(); + if (xNameAccess.is() && xNameAccess->hasByName("[Content_Types].xml")) + // It's always possible to append an OOXML signature. + return ret; + + if (!maSignatureManager.getStore().is()) + // It's always possible to append a PDF signature. + return ret; + + OSL_ASSERT(maSignatureManager.getStore().is()); + bool bDoc1_1 = DocumentSignatureHelper::isODFPre_1_2(m_sODFVersion); + SaveODFItem item; + bool bSave1_1 = item.isLessODF1_2(); + + // 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 ( (!bSave1_1 && bDoc1_1) || (bSave1_1 && 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(); + ret = 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 + && ret) + { + 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) + ret = false; + else + m_bWarningShowSignMacro = true; + + } + } + return ret; +} + +bool DigitalSignaturesDialog::canAdd() +{ + return canAddRemove(); +} + +bool DigitalSignaturesDialog::canRemove() +{ + bool bRet = true; + + if ( maSignatureManager.getSignatureMode() == DocumentSignatureMode::Content ) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Question, VclButtonsType::YesNo, + XsResId(STR_XMLSECDLG_QUERY_REALLYREMOVE))); + short nDlgRet = xBox->run(); + bRet = ( nDlgRet == RET_YES ); + } + + return (bRet && canAddRemove()); +} + +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, AdESCompliantCheckBoxHdl, weld::Toggleable&, void) +{ + m_bAdESCompliant = m_xAdESCompliantCB->get_active(); +} + +IMPL_LINK_NOARG(DigitalSignaturesDialog, ViewButtonHdl, weld::Button&, void) +{ + ImplShowSignaturesDetails(); +} + +IMPL_LINK_NOARG(DigitalSignaturesDialog, AddButtonHdl, weld::Button&, void) +{ + if( ! canAdd()) + return; + try + { + 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()); + + CertificateChooser aChooser(m_xDialog.get(), std::move(xSecContexts), UserAction::Sign); + if (aChooser.run() == RET_OK) + { + sal_Int32 nSecurityId; + if (!maSignatureManager.add(aChooser.GetSelectedCertificates()[0], 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(); + } + } + } + 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) +{ + if (!canRemove()) + 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) +{ +#ifdef _WIN32 + // FIXME: call GpgME::dirInfo("bindir") somewhere in + // SecurityEnvironmentGpg or whatnot + // FIXME: perhaps poke GpgME for uiserver, and hope it returns something useful? + static const 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", + }; + static const OUString aPath = [] { + OUString sRet; + PWSTR sPath = nullptr; + HRESULT hr + = SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_DEFAULT, nullptr, &sPath); + if (SUCCEEDED(hr)) + { + sRet = o3tl::toU(sPath); + CoTaskMemFree(sPath); + } + return sRet; + }(); + if (aPath.isEmpty()) + return; +#else + static const std::u16string_view aGUIServers[] = { u"kleopatra", u"seahorse", u"gpa", u"kgpg" }; + const char* cPath = getenv("PATH"); + if (!cPath) + return; + OUString aPath(cPath, strlen(cPath), osl_getThreadTextEncoding()); +#endif + + OUString sFoundGUIServer, sExecutable; + + for ( auto const &rServer : aGUIServers ) + { + osl::FileBase::RC searchError = osl::File::searchFileURL(OUString(rServer), aPath, sFoundGUIServer ); + if (searchError == osl::FileBase::E_None) + { + osl::File::getSystemPathFromFileURL( sFoundGUIServer, sExecutable ); + break; + } + + } + + if ( !sExecutable.isEmpty() ) + { + uno::Reference< uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + uno::Reference< css::system::XSystemShellExecute > xSystemShell( + css::system::SystemShellExecute::create(xContext) ); + + xSystemShell->execute( sExecutable, OUString(), + css::system::SystemShellExecuteFlags::DEFAULTS ); + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + XsResId(STR_XMLSECDLG_NO_CERT_MANAGER))); + 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) +{ + if (xCert->getCertificateKind() == CertificateKind_OPENPGP) + return maSignatureManager.getGpgSecurityEnvironment(); + else if (xCert->getCertificateKind() == CertificateKind_X509) + return maSignatureManager.getSecurityEnvironment(); + + throw RuntimeException("Unknown certificate kind"); +} + +//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: */ diff --git a/xmlsecurity/source/dialogs/macrosecurity.cxx b/xmlsecurity/source/dialogs/macrosecurity.cxx new file mode 100644 index 000000000..bf82606f4 --- /dev/null +++ b/xmlsecurity/source/dialogs/macrosecurity.cxx @@ -0,0 +1,446 @@ +/* -*- 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 <macrosecurity.hxx> +#include <certificateviewer.hxx> +#include <biginteger.hxx> +#include <resourcemanager.hxx> +#include <strings.hrc> + +#include <o3tl/safeint.hxx> +#include <osl/file.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp> +#include <comphelper/sequence.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/xmlsechelper.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/ui/dialogs/FolderPicker.hpp> +#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp> +#include <sfx2/filedlghelper.hxx> +#include <tools/diagnose_ex.h> +#include <tools/urlobj.hxx> +#include <unotools/datetime.hxx> + +#include <vcl/svapp.hxx> + +using namespace comphelper; +using namespace ::com::sun::star; + + +IMPL_LINK_NOARG(MacroSecurity, OkBtnHdl, weld::Button&, void) +{ + m_xLevelTP->ClosePage(); + m_xTrustSrcTP->ClosePage(); + m_xDialog->response(RET_OK); +} + +MacroSecurity::MacroSecurity(weld::Window* pParent, + const css::uno::Reference<css::xml::crypto::XSecurityEnvironment>& rxSecurityEnvironment) + : GenericDialogController(pParent, "xmlsec/ui/macrosecuritydialog.ui", "MacroSecurityDialog") + , m_xSecurityEnvironment(rxSecurityEnvironment) + , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , m_xOkBtn(m_xBuilder->weld_button("ok")) + , m_xResetBtn(m_xBuilder->weld_button("reset")) +{ + m_xTabCtrl->connect_enter_page(LINK(this, MacroSecurity, ActivatePageHdl)); + + m_xLevelTP.reset(new MacroSecurityLevelTP(m_xTabCtrl->get_page("SecurityLevelPage"), this)); + m_xTrustSrcTP.reset(new MacroSecurityTrustedSourcesTP(m_xTabCtrl->get_page("SecurityTrustPage"), this)); + + m_xTabCtrl->set_current_page("SecurityLevelPage"); + m_xOkBtn->connect_clicked(LINK(this, MacroSecurity, OkBtnHdl)); +} + +IMPL_LINK(MacroSecurity, ActivatePageHdl, const OString&, rPage, void) +{ + if (rPage == "SecurityLevelPage") + m_xLevelTP->ActivatePage(); + else if (rPage == "SecurityTrustPage") + m_xTrustSrcTP->ActivatePage(); +} + +MacroSecurityTP::MacroSecurityTP(weld::Container* pParent, const OUString& rUIXMLDescription, + const OString& rID, MacroSecurity* pDlg) + : m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription)) + , m_xContainer(m_xBuilder->weld_container(rID)) + , m_pDlg(pDlg) +{ +} + +void MacroSecurityTP::ActivatePage() +{ +} + +MacroSecurityTP::~MacroSecurityTP() +{ +} + +MacroSecurityLevelTP::MacroSecurityLevelTP(weld::Container* pParent, MacroSecurity* pDlg) + : MacroSecurityTP(pParent, "xmlsec/ui/securitylevelpage.ui", "SecurityLevelPage", pDlg) + , m_xVeryHighRB(m_xBuilder->weld_radio_button("vhigh")) + , m_xHighRB(m_xBuilder->weld_radio_button("high")) + , m_xMediumRB(m_xBuilder->weld_radio_button("med")) + , m_xLowRB(m_xBuilder->weld_radio_button("low")) + , m_xVHighImg(m_xBuilder->weld_widget("vhighimg")) + , m_xHighImg(m_xBuilder->weld_widget("highimg")) + , m_xMedImg(m_xBuilder->weld_widget("medimg")) + , m_xLowImg(m_xBuilder->weld_widget("lowimg")) +{ + m_xLowRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) ); + m_xMediumRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) ); + m_xHighRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) ); + m_xVeryHighRB->connect_toggled( LINK( this, MacroSecurityLevelTP, RadioButtonHdl ) ); + + int nPrefWidth(std::max({m_xVeryHighRB->get_preferred_size().Width(), + m_xHighRB->get_preferred_size().Width(), + m_xMediumRB->get_preferred_size().Width(), + m_xLowRB->get_preferred_size().Width()})); + int nMaxWidth = m_xLowRB->get_approximate_digit_width() * 60; + if (nPrefWidth > nMaxWidth) + { + m_xLowRB->set_label_wrap(true); + m_xLowRB->set_size_request(nMaxWidth, -1); + m_xMediumRB->set_label_wrap(true); + m_xMediumRB->set_size_request(nMaxWidth, -1); + m_xHighRB->set_label_wrap(true); + m_xHighRB->set_size_request(nMaxWidth, -1); + m_xVeryHighRB->set_label_wrap(true); + m_xVeryHighRB->set_size_request(nMaxWidth, -1); + } + + mnCurLevel = static_cast<sal_uInt16>(SvtSecurityOptions::GetMacroSecurityLevel()); + bool bReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::MacroSecLevel ); + + weld::RadioButton* pCheck = nullptr; + weld::Widget* pImage = nullptr; + switch (mnCurLevel) + { + case 3: + pCheck = m_xVeryHighRB.get(); + pImage = m_xVHighImg.get(); + break; + case 2: + pCheck = m_xHighRB.get(); + pImage = m_xHighImg.get(); + break; + case 1: + pCheck = m_xMediumRB.get(); + pImage = m_xMedImg.get(); + break; + case 0: + pCheck = m_xLowRB.get(); + pImage = m_xLowImg.get(); + break; + } + if (pCheck) + pCheck->set_active(true); + else + { + OSL_FAIL("illegal macro security level"); + } + if (bReadonly && pImage) + { + pImage->show(); + m_xVeryHighRB->set_sensitive(false); + m_xHighRB->set_sensitive(false); + m_xMediumRB->set_sensitive(false); + m_xLowRB->set_sensitive(false); + } +} + +IMPL_LINK_NOARG(MacroSecurityLevelTP, RadioButtonHdl, weld::Toggleable&, void) +{ + sal_uInt16 nNewLevel = 0; + if( m_xVeryHighRB->get_active() ) + nNewLevel = 3; + else if( m_xHighRB->get_active() ) + nNewLevel = 2; + else if( m_xMediumRB->get_active() ) + nNewLevel = 1; + + if ( nNewLevel != mnCurLevel ) + { + mnCurLevel = nNewLevel; + m_pDlg->EnableReset(); + } +} + +void MacroSecurityLevelTP::ClosePage() +{ + SvtSecurityOptions::SetMacroSecurityLevel( mnCurLevel ); +} + +void MacroSecurityTrustedSourcesTP::ImplCheckButtons() +{ + bool bCertSelected = m_xTrustCertLB->get_selected_index() != -1; + m_xViewCertPB->set_sensitive( bCertSelected ); + m_xRemoveCertPB->set_sensitive( bCertSelected && !mbAuthorsReadonly); + + bool bLocationSelected = m_xTrustFileLocLB->get_selected_index() != -1; + m_xRemoveLocPB->set_sensitive( bLocationSelected && !mbURLsReadonly); +} + +void MacroSecurityTrustedSourcesTP::ShowBrokenCertificateError(std::u16string_view rData) +{ + OUString aMsg = XsResId(STR_BROKEN_MACRO_CERTIFICATE_DATA); + aMsg = aMsg.replaceFirst("%{data}", rData); + std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_pDlg->getDialog(), + VclMessageType::Error, VclButtonsType::Ok, aMsg)); + xErrorBox->run(); +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, ViewCertPBHdl, weld::Button&, void) +{ + int nEntry = m_xTrustCertLB->get_selected_index(); + if (nEntry == -1) + return; + + const sal_uInt16 nSelected = m_xTrustCertLB->get_id(nEntry).toUInt32(); + uno::Reference< css::security::XCertificate > xCert; + try + { + xCert = m_pDlg->m_xSecurityEnvironment->getCertificate(m_aTrustedAuthors[nSelected].SubjectName, + xmlsecurity::numericStringToBigInteger(m_aTrustedAuthors[nSelected].SerialNumber)); + } + catch (...) + { + TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "matching certificate not found for: " << m_aTrustedAuthors[nSelected].SubjectName); + } + + if (!xCert.is()) + { + try + { + xCert = m_pDlg->m_xSecurityEnvironment->createCertificateFromAscii(m_aTrustedAuthors[nSelected].RawData); + } + catch (...) + { + TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "certificate data couldn't be parsed: " << m_aTrustedAuthors[nSelected].RawData); + } + } + + if ( xCert.is() ) + { + CertificateViewer aViewer(m_pDlg->getDialog(), m_pDlg->m_xSecurityEnvironment, xCert, false, nullptr); + aViewer.run(); + } + else + // should never happen, as we parsed the certificate data when we added it! + ShowBrokenCertificateError(m_aTrustedAuthors[nSelected].RawData); +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, RemoveCertPBHdl, weld::Button&, void) +{ + int nEntry = m_xTrustCertLB->get_selected_index(); + if (nEntry != -1) + { + sal_uInt16 nAuthor = m_xTrustCertLB->get_id(nEntry).toUInt32(); + m_aTrustedAuthors.erase(m_aTrustedAuthors.begin() + nAuthor); + + FillCertLB(); + ImplCheckButtons(); + } +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, AddLocPBHdl, weld::Button&, void) +{ + try + { + uno::Reference < uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference < ui::dialogs::XFolderPicker2 > xFolderPicker = sfx2::createFolderPicker(xContext, m_pDlg->getDialog()); + + short nRet = xFolderPicker->execute(); + + if( ui::dialogs::ExecutableDialogResults::OK != nRet ) + return; + + OUString aPathStr = xFolderPicker->getDirectory(); + INetURLObject aNewObj( aPathStr ); + aNewObj.removeFinalSlash(); + + // then the new path also a URL else system path + OUString aSystemFileURL = ( aNewObj.GetProtocol() != INetProtocol::NotValid ) ? + aPathStr : aNewObj.getFSysPath( FSysStyle::Detect ); + + OUString aNewPathStr(aSystemFileURL); + + if ( osl::FileBase::getSystemPathFromFileURL( aSystemFileURL, aSystemFileURL ) == osl::FileBase::E_None ) + aNewPathStr = aSystemFileURL; + + if (m_xTrustFileLocLB->find_text(aNewPathStr) == -1) + m_xTrustFileLocLB->append_text(aNewPathStr); + + ImplCheckButtons(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "xmlsecurity.dialogs", "MacroSecurityTrustedSourcesTP::AddLocPBHdl(): exception from folder picker" ); + } +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, RemoveLocPBHdl, weld::Button&, void) +{ + sal_Int32 nSel = m_xTrustFileLocLB->get_selected_index(); + if (nSel == -1) + return; + + m_xTrustFileLocLB->remove(nSel); + // Trusted Path could not be removed (#i33584#) + // after remove an entry, select another one if exists + int nNewCount = m_xTrustFileLocLB->n_children(); + if (nNewCount > 0) + { + if (nSel >= nNewCount) + nSel = nNewCount - 1; + m_xTrustFileLocLB->select(nSel); + } + ImplCheckButtons(); +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, TrustCertLBSelectHdl, weld::TreeView&, void) +{ + ImplCheckButtons(); +} + +IMPL_LINK_NOARG(MacroSecurityTrustedSourcesTP, TrustFileLocLBSelectHdl, weld::TreeView&, void) +{ + ImplCheckButtons(); +} + +void MacroSecurityTrustedSourcesTP::FillCertLB(const bool bShowWarnings) +{ + m_xTrustCertLB->clear(); + + sal_uInt32 nEntries = m_aTrustedAuthors.size(); + + if ( !(nEntries && m_pDlg->m_xSecurityEnvironment.is()) ) + return; + + for( sal_uInt32 nEntry = 0 ; nEntry < nEntries ; ++nEntry ) + { + SvtSecurityOptions::Certificate& rEntry = m_aTrustedAuthors[ nEntry ]; + + try + { + // create from RawData + uno::Reference< css::security::XCertificate > xCert = m_pDlg->m_xSecurityEnvironment->createCertificateFromAscii(rEntry.RawData); + m_xTrustCertLB->append(OUString::number(nEntry), xmlsec::GetContentPart(xCert->getSubjectName(), xCert->getCertificateKind())); + m_xTrustCertLB->set_text(nEntry, xmlsec::GetContentPart(xCert->getIssuerName(), xCert->getCertificateKind()), 1); + m_xTrustCertLB->set_text(nEntry, utl::GetDateTimeString(xCert->getNotValidAfter()), 2); + } + catch (...) + { + if (bShowWarnings) + { + TOOLS_WARN_EXCEPTION("xmlsecurity.dialogs", "certificate data couldn't be parsed: " << rEntry.RawData); + OUString sData = rEntry.RawData; + css::uno::Any tools_warn_exception(DbgGetCaughtException()); + OUString sException = OStringToOUString(exceptionToString(tools_warn_exception), RTL_TEXTENCODING_UTF8); + if (!sException.isEmpty()) + sData += " / " + sException; + ShowBrokenCertificateError(sData); + } + } + } +} + +MacroSecurityTrustedSourcesTP::MacroSecurityTrustedSourcesTP(weld::Container* pParent, MacroSecurity* pDlg) + : MacroSecurityTP(pParent, "xmlsec/ui/securitytrustpage.ui", "SecurityTrustPage", pDlg) + , m_xTrustCertROFI(m_xBuilder->weld_image("lockcertimg")) + , m_xTrustCertLB(m_xBuilder->weld_tree_view("certificates")) + , m_xViewCertPB(m_xBuilder->weld_button("viewcert")) + , m_xRemoveCertPB(m_xBuilder->weld_button("removecert")) + , m_xTrustFileROFI(m_xBuilder->weld_image("lockfileimg")) + , m_xTrustFileLocLB(m_xBuilder->weld_tree_view("locations")) + , m_xAddLocPB(m_xBuilder->weld_button("addfile")) + , m_xRemoveLocPB(m_xBuilder->weld_button("removefile")) +{ + auto nColWidth = m_xTrustCertLB->get_approximate_digit_width() * 12; + std::vector<int> aWidths + { + o3tl::narrowing<int>(nColWidth * 2), + o3tl::narrowing<int>(nColWidth * 2) + }; + m_xTrustCertLB->set_column_fixed_widths(aWidths); + m_xTrustCertLB->set_size_request(nColWidth * 5.5, m_xTrustCertLB->get_height_rows(5)); + + m_xTrustCertLB->connect_changed( LINK( this, MacroSecurityTrustedSourcesTP, TrustCertLBSelectHdl ) ); + m_xViewCertPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, ViewCertPBHdl ) ); + m_xViewCertPB->set_sensitive(false); + m_xRemoveCertPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, RemoveCertPBHdl ) ); + m_xRemoveCertPB->set_sensitive(false); + + m_xTrustFileLocLB->connect_changed( LINK( this, MacroSecurityTrustedSourcesTP, TrustFileLocLBSelectHdl ) ); + m_xTrustFileLocLB->set_size_request(nColWidth * 5, m_xTrustFileLocLB->get_height_rows(5)); + m_xAddLocPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, AddLocPBHdl ) ); + m_xRemoveLocPB->connect_clicked( LINK( this, MacroSecurityTrustedSourcesTP, RemoveLocPBHdl ) ); + m_xRemoveLocPB->set_sensitive(false); + + m_aTrustedAuthors = SvtSecurityOptions::GetTrustedAuthors(); + mbAuthorsReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::MacroTrustedAuthors ); + m_xTrustCertROFI->set_visible(mbAuthorsReadonly); + + FillCertLB(true); + + std::vector< OUString > aSecureURLs = SvtSecurityOptions::GetSecureURLs(); + mbURLsReadonly = SvtSecurityOptions::IsReadOnly( SvtSecurityOptions::EOption::SecureUrls ); + m_xTrustFileROFI->set_visible(mbURLsReadonly); + m_xAddLocPB->set_sensitive(!mbURLsReadonly); + + for (const auto& rSecureURL : aSecureURLs) + { + OUString aSystemFileURL( rSecureURL ); + osl::FileBase::getSystemPathFromFileURL( aSystemFileURL, aSystemFileURL ); + m_xTrustFileLocLB->append_text(aSystemFileURL); + } +} + +void MacroSecurityTrustedSourcesTP::ActivatePage() +{ + m_pDlg->EnableReset( false ); + FillCertLB(); +} + +void MacroSecurityTrustedSourcesTP::ClosePage() +{ + sal_Int32 nEntryCnt = m_xTrustFileLocLB->n_children(); + if( nEntryCnt ) + { + std::vector< OUString > aSecureURLs; + for (sal_Int32 i = 0; i < nEntryCnt; ++i) + { + OUString aURL(m_xTrustFileLocLB->get_text(i)); + osl::FileBase::getFileURLFromSystemPath( aURL, aURL ); + aSecureURLs.push_back(aURL); + } + + SvtSecurityOptions::SetSecureURLs( std::move(aSecureURLs) ); + } + // Trusted Path could not be removed (#i33584#) + // don't forget to remove the old saved SecureURLs + else + SvtSecurityOptions::SetSecureURLs( std::vector< OUString >() ); + + SvtSecurityOptions::SetTrustedAuthors( m_aTrustedAuthors ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |