diff options
author | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-02-15 11:38:23 +0100 |
---|---|---|
committer | Miklos Vajna <vmiklos@collabora.co.uk> | 2016-02-15 17:24:47 +0100 |
commit | 565346a4fe0a074807381c2d8ea48242e9e69f4f (patch) | |
tree | d94b901ba04a794d5e97d76126e7061677f83c04 /xmlsecurity/source/helper | |
parent | 639aaa2fe5dfc2cb2739c6f833f3208ebf8dd710 (diff) |
xmlsecurity: factor out DocumentSignatureManager from DigitalSignaturesDialog
It's hard to unit test signing when the logic is implemented in the Add
and OK button handlers.
Change-Id: I5e07df69cd808cf170e21dfd55f2f44bc79c58a8
Diffstat (limited to 'xmlsecurity/source/helper')
-rw-r--r-- | xmlsecurity/source/helper/documentsignaturemanager.cxx | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/xmlsecurity/source/helper/documentsignaturemanager.cxx b/xmlsecurity/source/helper/documentsignaturemanager.cxx new file mode 100644 index 000000000000..bb904b80f9a6 --- /dev/null +++ b/xmlsecurity/source/helper/documentsignaturemanager.cxx @@ -0,0 +1,296 @@ +/* -*- 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 <documentsignaturemanager.hxx> + +#include <com/sun/star/embed/StorageFormats.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/io/TempFile.hpp> +#include <com/sun/star/io/XTruncate.hpp> +#include <com/sun/star/security/SerialNumberAdapter.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> + +#include <comphelper/storagehelper.hxx> +#include <rtl/ustrbuf.hxx> +#include <sax/tools/converter.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> + +using namespace com::sun::star; + +DocumentSignatureManager::DocumentSignatureManager(const uno::Reference<uno::XComponentContext>& xContext, DocumentSignatureMode eMode) + : mxContext(xContext), + maSignatureHelper(xContext), + meSignatureMode(eMode) +{ +} + +DocumentSignatureManager::~DocumentSignatureManager() +{ +} + +/* Using the zip storage, we cannot get the properties "MediaType" and "IsEncrypted" + We use the manifest to find out if a file is xml and if it is encrypted. + The parameter is an encoded uri. However, the manifest contains paths. Therefore + the path is encoded as uri, so they can be compared. +*/ +bool DocumentSignatureManager::isXML(const OUString& rURI) +{ + SAL_WARN_IF(!mxStore.is(), "xmlsecurity.helper", "empty storage reference"); + + // FIXME figure out why this is necessary. + static bool bTest = getenv("LO_TESTNAME"); + if (bTest) + return true; + + bool bIsXML = false; + bool bPropsAvailable = false; + const OUString sPropFullPath("FullPath"); + const OUString sPropMediaType("MediaType"); + const OUString sPropDigest("Digest"); + + for (int i = 0; i < m_manifest.getLength(); i++) + { + const uno::Sequence<css::beans::PropertyValue>& entry = m_manifest[i]; + OUString sPath, sMediaType; + bool bEncrypted = false; + for (int j = 0; j < entry.getLength(); j++) + { + const css::beans::PropertyValue& prop = entry[j]; + + if (prop.Name.equals(sPropFullPath)) + prop.Value >>= sPath; + else if (prop.Name.equals(sPropMediaType)) + prop.Value >>= sMediaType; + else if (prop.Name.equals(sPropDigest)) + bEncrypted = true; + } + if (DocumentSignatureHelper::equalsReferenceUriManifestPath(rURI, sPath)) + { + bIsXML = sMediaType == "text/xml" && ! bEncrypted; + bPropsAvailable = true; + break; + } + } + if (!bPropsAvailable) + { + //This would be the case for at least mimetype, META-INF/manifest.xml + //META-INF/macrosignatures.xml. + //Files can only be encrypted if they are in the manifest.xml. + //That is, the current file cannot be encrypted, otherwise bPropsAvailable + //would be true. + OUString aXMLExt("XML"); + sal_Int32 nSep = rURI.lastIndexOf('.'); + if (nSep != (-1)) + { + OUString aExt = rURI.copy(nSep+1); + if (aExt.equalsIgnoreAsciiCase(aXMLExt)) + bIsXML = true; + } + } + return bIsXML; +} + +//If bTempStream is true, then a temporary stream is return. If it is false then, the actual +//signature stream is used. +//Every time the user presses Add a new temporary stream is created. +//We keep the temporary stream as member because ImplGetSignatureInformations +//will later access the stream to create DocumentSignatureInformation objects +//which are stored in maCurrentSignatureInformations. +SignatureStreamHelper DocumentSignatureManager::ImplOpenSignatureStream(sal_Int32 nStreamOpenMode, bool bTempStream) +{ + SignatureStreamHelper aHelper; + if (mxStore.is()) + { + uno::Reference<container::XNameAccess> xNameAccess(mxStore, uno::UNO_QUERY); + if (xNameAccess.is() && xNameAccess->hasByName("[Content_Types].xml")) + aHelper.nStorageFormat = embed::StorageFormats::OFOPXML; + } + + if (bTempStream) + { + if (nStreamOpenMode & css::embed::ElementModes::TRUNCATE) + { + //We write always into a new temporary stream. + mxTempSignatureStream.set(css::io::TempFile::create(mxContext), uno::UNO_QUERY_THROW); + if (aHelper.nStorageFormat != embed::StorageFormats::OFOPXML) + aHelper.xSignatureStream = mxTempSignatureStream; + else + { + mxTempSignatureStorage = comphelper::OStorageHelper::GetStorageOfFormatFromStream(ZIP_STORAGE_FORMAT_STRING, mxTempSignatureStream); + aHelper.xSignatureStorage = mxTempSignatureStorage; + } + } + else + { + //When we read from the temp stream, then we must have previously + //created one. + SAL_WARN_IF(!mxTempSignatureStream.is(), "xmlsecurity.helper", "empty temp. signature stream reference"); + } + aHelper.xSignatureStream = mxTempSignatureStream; + if (aHelper.nStorageFormat == embed::StorageFormats::OFOPXML) + aHelper.xSignatureStorage = mxTempSignatureStorage; + } + else + { + //No temporary stream + if (!mxSignatureStream.is()) + { + //We may not have a dedicated stream for writing the signature + //So we take one directly from the storage + //Or DocumentDigitalSignatures::showDocumentContentSignatures was called, + //in which case Add/Remove is not allowed. This is done, for example, if the + //document is readonly + aHelper = DocumentSignatureHelper::OpenSignatureStream(mxStore, nStreamOpenMode, meSignatureMode); + } + else + { + aHelper.xSignatureStream = mxSignatureStream; + } + } + + if (nStreamOpenMode & css::embed::ElementModes::TRUNCATE) + { + if (aHelper.xSignatureStream.is() && aHelper.nStorageFormat != embed::StorageFormats::OFOPXML) + { + css::uno::Reference<css::io::XTruncate> xTruncate(aHelper.xSignatureStream, uno::UNO_QUERY_THROW); + xTruncate->truncate(); + } + } + else if (bTempStream || mxSignatureStream.is()) + { + //In case we read the signature stream from the storage directly, + //which is the case when DocumentDigitalSignatures::showDocumentContentSignatures + //then XSeakable is not supported + uno::Reference<io::XSeekable> xSeek(aHelper.xSignatureStream, uno::UNO_QUERY_THROW); + xSeek->seek(0); + } + + return aHelper; +} + +bool DocumentSignatureManager::add(const uno::Reference<security::XCertificate>& xCert, const OUString& rDescription, sal_Int32& nSecurityId) +{ + if (!xCert.is()) + { + SAL_WARN("xmlsecurity.helper", "no certificate selected"); + return false; + } + + uno::Reference<security::XSerialNumberAdapter> xSerialNumberAdapter = security::SerialNumberAdapter::create(mxContext); + OUString aCertSerial = xSerialNumberAdapter->toString(xCert->getSerialNumber()); + if (aCertSerial.isEmpty()) + { + SAL_WARN("xmlsecurity.helper", "Error in Certificate, problem with serial number!"); + return false; + } + + maSignatureHelper.StartMission(); + + nSecurityId = maSignatureHelper.GetNewSecurityId(); + + OUStringBuffer aStrBuffer; + sax::Converter::encodeBase64(aStrBuffer, xCert->getEncoded()); + + maSignatureHelper.SetX509Certificate(nSecurityId, xCert->getIssuerName(), aCertSerial, aStrBuffer.makeStringAndClear()); + + std::vector< OUString > aElements = DocumentSignatureHelper::CreateElementList(mxStore, meSignatureMode, OOo3_2Document); + DocumentSignatureHelper::AppendContentTypes(mxStore, aElements); + + sal_Int32 nElements = aElements.size(); + for (sal_Int32 n = 0; n < nElements; n++) + { + bool bBinaryMode = !isXML(aElements[n]); + maSignatureHelper.AddForSigning(nSecurityId, aElements[n], aElements[n], bBinaryMode); + } + + maSignatureHelper.SetDateTime(nSecurityId, Date(Date::SYSTEM), tools::Time(tools::Time::SYSTEM)); + maSignatureHelper.SetDescription(nSecurityId, rDescription); + + // We open a signature stream in which the existing and the new + //signature is written. ImplGetSignatureInformation (later in this function) will + //then read the stream an will fill maCurrentSignatureInformations. The final signature + //is written when the user presses OK. Then only maCurrentSignatureInformation and + //a sax writer are used to write the information. + SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE, true); + + if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML) + { + uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream, uno::UNO_QUERY_THROW); + uno::Reference<xml::sax::XWriter> xSaxWriter = maSignatureHelper.CreateDocumentHandlerWithHeader(xOutputStream); + + // Export old signatures... + uno::Reference<xml::sax::XDocumentHandler> xDocumentHandler(xSaxWriter, uno::UNO_QUERY_THROW); + size_t nInfos = maCurrentSignatureInformations.size(); + for (size_t n = 0; n < nInfos; n++) + XMLSignatureHelper::ExportSignature(xDocumentHandler, maCurrentSignatureInformations[n]); + + // Create a new one... + maSignatureHelper.CreateAndWriteSignature(xDocumentHandler); + + // That's it... + XMLSignatureHelper::CloseDocumentHandler(xDocumentHandler); + } + else + { + // OOXML + + // Handle relations. + maSignatureHelper.EnsureSignaturesRelation(mxStore); + // Old signatures + the new one. + int nSignatureCount = maCurrentSignatureInformations.size() + 1; + maSignatureHelper.ExportSignatureRelations(aStreamHelper.xSignatureStorage, nSignatureCount); + + // Create a new signature. + maSignatureHelper.CreateAndWriteOOXMLSignature(mxStore, aStreamHelper.xSignatureStorage, nSignatureCount); + + // Flush objects. + uno::Reference<embed::XTransactedObject> xTransact(aStreamHelper.xSignatureStorage, uno::UNO_QUERY); + xTransact->commit(); + uno::Reference<io::XOutputStream> xOutputStream(aStreamHelper.xSignatureStream, uno::UNO_QUERY); + xOutputStream->closeOutput(); + + uno::Reference<io::XTempFile> xTempFile(aStreamHelper.xSignatureStream, uno::UNO_QUERY); + SAL_INFO("xmlsecurity.dialogs", "AddButtonHdl: temporary storage is at " << xTempFile->getUri()); + } + + maSignatureHelper.EndMission(); + return true; +} + +void DocumentSignatureManager::read(bool bUseTempStream) +{ + maCurrentSignatureInformations.clear(); + + maSignatureHelper.StartMission(); + + SignatureStreamHelper aStreamHelper = ImplOpenSignatureStream(css::embed::ElementModes::READ, bUseTempStream); + if (aStreamHelper.nStorageFormat != embed::StorageFormats::OFOPXML && aStreamHelper.xSignatureStream.is()) + { + uno::Reference< io::XInputStream > xInputStream(aStreamHelper.xSignatureStream, uno::UNO_QUERY); + maSignatureHelper.ReadAndVerifySignature(xInputStream); + } + else if (aStreamHelper.nStorageFormat == embed::StorageFormats::OFOPXML && aStreamHelper.xSignatureStorage.is()) + maSignatureHelper.ReadAndVerifySignatureStorage(aStreamHelper.xSignatureStorage); + maSignatureHelper.EndMission(); + + maCurrentSignatureInformations = maSignatureHelper.GetSignatureInformations(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |