Kaydet (Commit) 11394fd7 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl Kaydeden (comit) Miklos Vajna

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.

Reviewed-on: https://gerrit.libreoffice.org/61780
Tested-by: Jenkins
Reviewed-by: 's avatarTomaž Vajngerl <quikee@gmail.com>
(cherry picked from commit 23a23123)

Conflicts:
	desktop/qa/desktop_lib/test_desktop_lib.cxx
	include/LibreOfficeKit/LibreOfficeKit.hxx
	include/sfx2/objsh.hxx
	sfx2/source/doc/docfile.cxx
	sfx2/source/doc/objserv.cxx

Change-Id: I2f0d6038fb1bcd00adcdf86e432f9df8858cc21c
üst 32b4b5af
......@@ -2300,10 +2300,12 @@ void DesktopLOKTest::testABI()
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, paintWindowDPI));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), offsetof(struct _LibreOfficeKitDocumentClass, insertCertificate));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(44), offsetof(struct _LibreOfficeKitDocumentClass, getSignatureState));
// Extending is fine, update this, and add new assert for the offsetof the
// new method
CPPUNIT_ASSERT_EQUAL(documentClassOffset(43), sizeof(struct _LibreOfficeKitDocumentClass));
CPPUNIT_ASSERT_EQUAL(documentClassOffset(45), sizeof(struct _LibreOfficeKitDocumentClass));
}
CPPUNIT_TEST_SUITE_REGISTRATION(DesktopLOKTest);
......
......@@ -72,6 +72,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>
......@@ -687,6 +694,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)
{
......@@ -745,6 +758,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();
......@@ -3667,6 +3683,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;
......
......@@ -318,6 +318,16 @@ struct _LibreOfficeKitDocumentClass
const int width, const int height,
const double dpiscale);
// 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
};
......
......@@ -557,6 +557,25 @@ public:
mpDoc->pClass->postWindowExtTextInputEvent(mpDoc, nWindowId, nType, pText);
}
/**
* 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
};
......
......@@ -269,6 +269,10 @@ public:
bool bScriptingContent, const OUString& aODFVersion,
bool bHasValidDocumentSignature);
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
......
......@@ -363,6 +363,9 @@ public:
bool HasValidSignatures();
SignatureState GetDocumentSignatureState();
void SignDocumentContent();
bool SignDocumentContentUsingCertificate(const css::uno::Reference<css::security::XCertificate>& xCertificate);
void SignSignatureLine(const OUString& aSignatureLineId,
const css::uno::Reference<css::security::XCertificate> xCert);
SignatureState GetScriptingSignatureState();
......
......@@ -3575,6 +3575,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(const Reference<XCertificate> xCert, const OUString& aSignatureLineId,
bool bScriptingContent, const OUString& aODFVersion,
bool bHasValidDocumentSignature)
......
......@@ -1509,6 +1509,85 @@ void SfxObjectShell::SignDocumentContent()
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(const OUString& aSignatureLineId,
const Reference<XCertificate> xCert)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment