summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>2018-10-15 10:09:15 +0200
committerTomaž Vajngerl <quikee@gmail.com>2018-10-28 21:29:19 +0100
commit23a2312344ac961ead9ee14140c0b3e879bb7a41 (patch)
tree866e85ef63364028cff008f12ad9d520a1f48cc4
parent12b9952a9a04341bc41b0a5838bd25cfbb835e62 (diff)
lokit: add funct. to insert, sign and verify signature
A lot of signing code paths trigger a GUI dialog (to select the certificate for example) which aren't acceptable when triggering through the LOKit. This code paths needed to be duplicated and reworked to not trigger any GUI action. Change-Id: I2f0d6038fb1bcd00adcdf86e432f9df8858cc21c Reviewed-on: https://gerrit.libreoffice.org/61780 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
-rw-r--r--desktop/qa/desktop_lib/test_desktop_lib.cxx4
-rw-r--r--desktop/source/lib/init.cxx69
-rw-r--r--include/LibreOfficeKit/LibreOfficeKit.h10
-rw-r--r--include/LibreOfficeKit/LibreOfficeKit.hxx19
-rw-r--r--include/sfx2/docfile.hxx4
-rw-r--r--include/sfx2/objsh.hxx3
-rw-r--r--sfx2/source/doc/docfile.cxx117
-rw-r--r--sfx2/source/doc/objserv.cxx79
8 files changed, 304 insertions, 1 deletions
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index 69f2b3ac2bfb..9a8d7b88b0b7 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -2299,10 +2299,12 @@ void DesktopLOKTest::testABI()
CPPUNIT_ASSERT_EQUAL(documentClassOffset(39), offsetof(struct _LibreOfficeKitDocumentClass, setViewLanguage));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(40), offsetof(struct _LibreOfficeKitDocumentClass, postWindowExtTextInputEvent));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(41), offsetof(struct _LibreOfficeKitDocumentClass, getPartInfo));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), offsetof(struct _LibreOfficeKitDocumentClass, insertCertificate));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass, getSignatureState));
// Extending is fine, update this, and add new assert for the offsetof the
// new method
- CPPUNIT_ASSERT_EQUAL(documentClassOffset(42), sizeof(struct _LibreOfficeKitDocumentClass));
+ CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), sizeof(struct _LibreOfficeKitDocumentClass));
}
CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest);
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index a2182e9ea2a8..2a13775dcc64 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -73,6 +73,13 @@
#include <com/sun/star/document/XRedlinesSupplier.hpp>
#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
+#include <com/sun/star/xml/crypto/SEInitializer.hpp>
+#include <com/sun/star/xml/crypto/XSEInitializer.hpp>
+#include <com/sun/star/xml/crypto/XSecurityEnvironment.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/XDocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
#include <com/sun/star/linguistic2/XSpellChecker.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
@@ -682,6 +689,12 @@ static void doc_postWindow(LibreOfficeKitDocument* pThis, unsigned nLOKWindowId,
static char* doc_getPartInfo(LibreOfficeKitDocument* pThis, int nPart);
+static bool doc_insertCertificate(LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary,
+ const int pCertificateBinarySize);
+
+static int doc_getSignatureState(LibreOfficeKitDocument* pThis);
+
LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XComponent> &xComponent)
: mxComponent(xComponent)
{
@@ -742,6 +755,9 @@ LibLODocument_Impl::LibLODocument_Impl(const uno::Reference <css::lang::XCompone
m_pDocumentClass->getPartInfo = doc_getPartInfo;
+ m_pDocumentClass->insertCertificate = doc_insertCertificate;
+ m_pDocumentClass->getSignatureState = doc_getSignatureState;
+
gDocumentClass = m_pDocumentClass;
}
pClass = m_pDocumentClass.get();
@@ -3584,6 +3600,59 @@ static void doc_postWindow(LibreOfficeKitDocument* /*pThis*/, unsigned nLOKWindo
}
}
+// CERTIFICATE AND DOCUMENT SIGNING
+static bool doc_insertCertificate(LibreOfficeKitDocument* /*pThis*/, const unsigned char* pCertificateBinary, const int nCertificateBinarySize)
+{
+ if (!xContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(xContext);
+ uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext;
+ xSecurityContext = xSEInitializer->createSecurityContext(OUString());
+ if (!xSecurityContext.is())
+ return false;
+
+ uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment;
+ xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
+
+ uno::Sequence<sal_Int8> aCertificateSequence(nCertificateBinarySize);
+ std::copy(pCertificateBinary, pCertificateBinary + nCertificateBinarySize, aCertificateSequence.begin());
+
+ uno::Reference<security::XCertificate> xCertificate = xSecurityEnvironment->createCertificateFromRaw(aCertificateSequence);
+
+ if (!xCertificate.is())
+ return false;
+
+ printf("CERTIFICATE\n\tIssuerName: %s \n\tSubjectName: %s\n\tPK %s\n\n",
+ xCertificate->getIssuerName().toUtf8().getStr(),
+ xCertificate->getSubjectName().toUtf8().getStr(),
+ xCertificate->getSubjectPublicKeyAlgorithm().toUtf8().getStr());
+
+ SfxObjectShell* pDoc = SfxObjectShell::Current();
+ if (!pDoc)
+ return false;
+
+ return pDoc->SignDocumentContentUsingCertificate(xCertificate);
+}
+
+static int doc_getSignatureState(LibreOfficeKitDocument* pThis)
+{
+ LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis);
+
+ if (!pDocument->mxComponent.is())
+ return int(SignatureState::UNKNOWN);
+
+ SfxBaseModel* pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get());
+ if (!pBaseModel)
+ return int(SignatureState::UNKNOWN);
+
+ SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell();
+ if (!pObjectShell)
+ return int(SignatureState::UNKNOWN);
+
+ return int(pObjectShell->GetDocumentSignatureState());
+}
+
static char* lo_getError (LibreOfficeKit *pThis)
{
SolarMutexGuard aGuard;
diff --git a/include/LibreOfficeKit/LibreOfficeKit.h b/include/LibreOfficeKit/LibreOfficeKit.h
index 0ae5c6ff1332..24aa496214a8 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.h
+++ b/include/LibreOfficeKit/LibreOfficeKit.h
@@ -321,6 +321,16 @@ struct _LibreOfficeKitDocumentClass
const int nTileHeight);
#endif // IOS
+// CERTIFICATE AND SIGNING
+
+ /// @see lok::Document::insertCertificate().
+ bool (*insertCertificate) (LibreOfficeKitDocument* pThis,
+ const unsigned char* pCertificateBinary,
+ const int pCertificateBinarySize);
+
+ /// @see lok::Document::getSignatureState().
+ int (*getSignatureState) (LibreOfficeKitDocument* pThis);
+
#endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
};
diff --git a/include/LibreOfficeKit/LibreOfficeKit.hxx b/include/LibreOfficeKit/LibreOfficeKit.hxx
index 3568c861f083..d6e4bee6fa8e 100644
--- a/include/LibreOfficeKit/LibreOfficeKit.hxx
+++ b/include/LibreOfficeKit/LibreOfficeKit.hxx
@@ -578,6 +578,25 @@ public:
}
#endif // IOS
+ /**
+ * Insert certificate (in binary form) to the certificate store.
+ */
+ bool insertCertificate(const unsigned char* pCertificateBinary,
+ const int pCertificateBinarySize)
+ {
+ return mpDoc->pClass->insertCertificate(mpDoc, pCertificateBinary, pCertificateBinarySize);
+ }
+
+ /**
+ * Verify signature of the document.
+ *
+ * Check possible values in include/sfx2/signaturestate.hxx
+ */
+ int getSignatureState()
+ {
+ return mpDoc->pClass->getSignatureState(mpDoc);
+ }
+
#endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY
};
diff --git a/include/sfx2/docfile.hxx b/include/sfx2/docfile.hxx
index 0abd7ba23c3a..7022d51743d9 100644
--- a/include/sfx2/docfile.hxx
+++ b/include/sfx2/docfile.hxx
@@ -273,6 +273,10 @@ public:
= css::uno::Reference<css::graphic::XGraphic>(),
const OUString& aComment = OUString());
+ SAL_DLLPRIVATE bool
+ SignDocumentContentUsingCertificate(bool bHasValidDocumentSignature,
+ const css::uno::Reference<css::security::XCertificate>& xCertificate);
+
// the following two methods must be used and make sense only during saving currently
// TODO/LATER: in future the signature state should be controlled by the medium not by the document
// in this case the methods will be used generally, and might need to be renamed
diff --git a/include/sfx2/objsh.hxx b/include/sfx2/objsh.hxx
index 54d97faf3162..4e0ba7ae2a84 100644
--- a/include/sfx2/objsh.hxx
+++ b/include/sfx2/objsh.hxx
@@ -361,6 +361,9 @@ public:
bool HasValidSignatures();
SignatureState GetDocumentSignatureState();
void SignDocumentContent(weld::Window* pDialogParent);
+
+ bool SignDocumentContentUsingCertificate(const css::uno::Reference<css::security::XCertificate>& xCertificate);
+
void SignSignatureLine(weld::Window* pDialogParent, const OUString& aSignatureLineId,
const css::uno::Reference<css::security::XCertificate>& xCert,
const css::uno::Reference<css::graphic::XGraphic>& xValidGraphic,
diff --git a/sfx2/source/doc/docfile.cxx b/sfx2/source/doc/docfile.cxx
index 9e5fc078c635..9e55c128bb71 100644
--- a/sfx2/source/doc/docfile.cxx
+++ b/sfx2/source/doc/docfile.cxx
@@ -3644,6 +3644,123 @@ void SfxMedium::CreateTempFileNoCopy()
CloseStorage();
}
+bool SfxMedium::SignDocumentContentUsingCertificate(bool bHasValidDocumentSignature,
+ const Reference<XCertificate>& xCertificate)
+{
+ bool bChanges = false;
+
+ if (IsOpen() || GetError())
+ {
+ SAL_WARN("sfx.doc", "The medium must be closed by the signer!");
+ return bChanges;
+ }
+
+ // The component should know if there was a valid document signature, since
+ // it should show a warning in this case
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+ uno::Reference< security::XDocumentDigitalSignatures > xSigner(
+ security::DocumentDigitalSignatures::createWithVersionAndValidSignature(
+ comphelper::getProcessComponentContext(), aODFVersion, bHasValidDocumentSignature ) );
+
+ uno::Reference< embed::XStorage > xWriteableZipStor;
+
+ // we can reuse the temporary file if there is one already
+ CreateTempFile( false );
+ GetMedium_Impl();
+
+ try
+ {
+ if ( !pImpl->xStream.is() )
+ throw uno::RuntimeException();
+
+ bool bODF = GetFilter()->IsOwnFormat();
+ try
+ {
+ xWriteableZipStor = ::comphelper::OStorageHelper::GetStorageOfFormatFromStream( ZIP_STORAGE_FORMAT_STRING, pImpl->xStream );
+ }
+ catch (const io::IOException& rException)
+ {
+ if (bODF)
+ SAL_WARN("sfx.doc", "ODF stream is not a zip storage: " << rException);
+ }
+
+ if ( !xWriteableZipStor.is() && bODF )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XStorage > xMetaInf;
+ uno::Reference<container::XNameAccess> xNameAccess(xWriteableZipStor, uno::UNO_QUERY);
+ if (xNameAccess.is() && xNameAccess->hasByName("META-INF"))
+ {
+ xMetaInf = xWriteableZipStor->openStorageElement(
+ "META-INF",
+ embed::ElementModes::READWRITE );
+ if ( !xMetaInf.is() )
+ throw uno::RuntimeException();
+ }
+
+ {
+ if (xMetaInf.is())
+ {
+ // ODF.
+ uno::Reference< io::XStream > xStream;
+ if (GetFilter() && GetFilter()->IsOwnFormat())
+ xStream.set(xMetaInf->openStreamElement(xSigner->getDocumentContentSignatureDefaultStreamName(), embed::ElementModes::READWRITE), uno::UNO_SET_THROW);
+
+ bool bSuccess = xSigner->signDocumentWithCertificate(xCertificate, GetZipStorageToSign_Impl(), xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference< embed::XTransactedObject > xTransact( xMetaInf, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+ xTransact.set( xWriteableZipStor, uno::UNO_QUERY_THROW );
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else if (xWriteableZipStor.is())
+ {
+ // OOXML.
+ uno::Reference<io::XStream> xStream;
+
+ // We need read-write to be able to add the signature relation.
+ bool bSuccess =xSigner->signDocumentWithCertificate(
+ xCertificate, GetZipStorageToSign_Impl(/*bReadOnly=*/false), xStream);
+
+ if (bSuccess)
+ {
+ uno::Reference<embed::XTransactedObject> xTransact(xWriteableZipStor, uno::UNO_QUERY_THROW);
+ xTransact->commit();
+
+ // the temporary file has been written, commit it to the original file
+ Commit();
+ bChanges = true;
+ }
+ }
+ else
+ {
+ // Something not ZIP based: e.g. PDF.
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(GetName(), StreamMode::READ | StreamMode::WRITE));
+ uno::Reference<io::XStream> xStream(new utl::OStreamWrapper(*pStream));
+ if (xSigner->signDocumentWithCertificate(xCertificate, uno::Reference<embed::XStorage>(), xStream))
+ bChanges = true;
+ }
+ }
+ }
+ catch ( const uno::Exception& )
+ {
+ SAL_WARN( "sfx.doc", "Couldn't use signing functionality!" );
+ }
+
+ CloseAndRelease();
+
+ ResetError();
+
+ return bChanges;
+}
+
bool SfxMedium::SignContents_Impl(bool bSignScriptingContent, bool bHasValidDocumentSignature,
const OUString& aSignatureLineId,
const Reference<XCertificate>& xCert,
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index ce6b1cf0666d..c8bdda36a707 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -1585,6 +1585,85 @@ void SfxObjectShell::SignDocumentContent(weld::Window* pDialogParent)
AfterSigning(bSignSuccess, false);
}
+bool SfxObjectShell::SignDocumentContentUsingCertificate(const Reference<XCertificate>& xCertificate)
+{
+ // 1. PrepareForSigning
+
+ // check whether the document is signed
+ ImplGetSignatureState(false); // document signature
+ if (GetMedium() && GetMedium()->GetFilter() && GetMedium()->GetFilter()->IsOwnFormat())
+ ImplGetSignatureState( true ); // script signature
+ bool bHasSign = ( pImpl->nScriptingSignatureState != SignatureState::NOSIGNATURES || pImpl->nDocumentSignatureState != SignatureState::NOSIGNATURES );
+
+ // the target ODF version on saving (only valid when signing ODF of course)
+ SvtSaveOptions aSaveOpt;
+ SvtSaveOptions::ODFDefaultVersion nVersion = aSaveOpt.GetODFDefaultVersion();
+
+ // the document is not new and is not modified
+ OUString aODFVersion(comphelper::OStorageHelper::GetODFVersionFromStorage(GetStorage()));
+
+ if (IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty()
+ || (GetMedium()->GetFilter()->IsOwnFormat() && aODFVersion != ODFVER_012_TEXT && !bHasSign))
+ {
+ if ( nVersion >= SvtSaveOptions::ODFVER_012 )
+ {
+ sal_uInt16 nId = SID_SAVEDOC;
+ if ( !GetMedium() || GetMedium()->GetName().isEmpty() )
+ nId = SID_SAVEASDOC;
+ SfxRequest aSaveRequest( nId, SfxCallMode::SLOT, GetPool() );
+ //ToDo: Review. We needed to call SetModified, otherwise the document would not be saved.
+ SetModified();
+ ExecFile_Impl( aSaveRequest );
+
+ // Check if it is stored a format which supports signing
+ if (GetMedium() && GetMedium()->GetFilter() && !GetMedium()->GetName().isEmpty()
+ && ((!GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->GetFilter()->GetSupportsSigning())
+ || (GetMedium()->GetFilter()->IsOwnFormat()
+ && !GetMedium()->HasStorage_Impl())))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if ( IsModified() || !GetMedium() || GetMedium()->GetName().isEmpty() )
+ return false;
+ }
+
+ // the document is not modified currently, so it can not become modified after signing
+ pImpl->m_bAllowModifiedBackAfterSigning = false;
+ if ( IsEnableSetModified() )
+ {
+ EnableSetModified( false );
+ pImpl->m_bAllowModifiedBackAfterSigning = true;
+ }
+
+ // we have to store to the original document, the original medium should be closed for this time
+ bool bResult = ConnectTmpStorage_Impl( pMedium->GetStorage(), pMedium);
+ printf("ConnectTmpStorage_Impl %d\n", bResult);
+
+ if (!bResult)
+ return false;
+
+ GetMedium()->CloseAndRelease();
+
+ // 2. Check Read-Only
+ if (GetMedium()->IsOriginallyReadOnly())
+ return false;
+
+ // 3. Sign
+ bool bSignSuccess = GetMedium()->SignDocumentContentUsingCertificate(HasValidSignatures(), xCertificate);
+
+ // 4. AfterSigning
+ AfterSigning(bSignSuccess, false);
+
+ return true;
+}
+
void SfxObjectShell::SignSignatureLine(weld::Window* pDialogParent,
const OUString& aSignatureLineId,
const Reference<XCertificate>& xCert,