LibreOffice Module oox (master) 1
DocumentDecryption.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 */
10
12
14
15#include <com/sun/star/beans/NamedValue.hpp>
16#include <com/sun/star/io/XSeekable.hpp>
17#include <com/sun/star/io/XStream.hpp>
18#include <com/sun/star/io/IOException.hpp>
19#include <com/sun/star/uno/XComponentContext.hpp>
20#include <com/sun/star/packages/XPackageEncryption.hpp>
23
24#include <sal/log.hxx>
25#include <utility>
26
27namespace
28{
29void lcl_getListOfStreams(oox::StorageBase* pStorage, std::vector<OUString>& rElementNames)
30{
31 std::vector<OUString> oElementNames;
32 pStorage->getElementNames(oElementNames);
33 for (const auto& sName : oElementNames)
34 {
35 oox::StorageRef rSubStorage = pStorage->openSubStorage(sName, false);
36 if (rSubStorage && rSubStorage->isStorage())
37 {
38 lcl_getListOfStreams(rSubStorage.get(), rElementNames);
39 }
40 else
41 {
42 if (pStorage->isRootStorage())
43 rElementNames.push_back(sName);
44 else
45 rElementNames.push_back(pStorage->getPath() + "/" + sName);
46 }
47 }
48}
49}
50
51namespace oox::crypto
52{
53using namespace css;
54
55DocumentDecryption::DocumentDecryption(css::uno::Reference<css::uno::XComponentContext> xContext,
56 oox::ole::OleStorage& rOleStorage)
57 : mxContext(std::move(xContext))
58 , mrOleStorage(rOleStorage)
59{
60 // Get OLE streams into sequences for later use in CryptoEngine
61 std::vector<OUString> aStreamNames;
62 lcl_getListOfStreams(&mrOleStorage, aStreamNames);
63
65 for (const auto& sStreamName : aStreamNames)
66 {
67 uno::Reference<io::XInputStream> xStream = mrOleStorage.openInputStream(sStreamName);
68 if (!xStream.is())
69 throw io::IOException("Cannot open OLE input stream for " + sStreamName + "!");
70
71 BinaryXInputStream aBinaryInputStream(xStream, true);
72
73 css::uno::Sequence<sal_Int8> oData;
74 sal_Int32 nStreamSize = aBinaryInputStream.size();
75 sal_Int32 nReadBytes = aBinaryInputStream.readData(oData, nStreamSize);
76
77 if (nStreamSize != nReadBytes)
78 {
79 SAL_WARN("oox", "OLE stream invalid content");
80 throw io::IOException("OLE stream invalid content for " + sStreamName + "!");
81 }
82
83 aStreamsData[sStreamName] <<= oData;
84 }
86}
87
88bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
89{
90 if (mxPackageEncryption.is())
91 return mxPackageEncryption->generateEncryptionKey(rPassword);
92 return false;
93}
94
96{
98 return false;
99
100 // Read 0x6DataSpaces/DataSpaceMap
101 uno::Reference<io::XInputStream> xDataSpaceMap
102 = mrOleStorage.openInputStream("\006DataSpaces/DataSpaceMap");
103 OUString sDataSpaceName;
104
105 if (xDataSpaceMap.is())
106 {
107 bool bBroken = false;
108
109 BinaryXInputStream aDataSpaceStream(xDataSpaceMap, true);
110 sal_uInt32 aHeaderLength = aDataSpaceStream.readuInt32();
111 SAL_WARN_IF(aHeaderLength != 8, "oox",
112 "DataSpaceMap length != 8 is not supported. Some content may be skipped");
113 sal_uInt32 aEntryCount = aDataSpaceStream.readuInt32();
114 SAL_WARN_IF(aEntryCount != 1, "oox",
115 "DataSpaceMap contains more than one entry. Some content may be skipped");
116
117 // Read each DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1)
118 for (sal_uInt32 i = 0; i < aEntryCount && !bBroken; i++)
119 {
120 // entryLen unused for the moment
121 aDataSpaceStream.skip(sizeof(sal_uInt32));
122
123 // Read each DataSpaceReferenceComponent (MS-OFFCRYPTO 2.1.6.2)
124 sal_uInt32 aReferenceComponentCount = aDataSpaceStream.readuInt32();
125 for (sal_uInt32 j = 0; j < aReferenceComponentCount && !bBroken; j++)
126 {
127 // Read next reference component
128 // refComponentType unused for the moment
129 aDataSpaceStream.skip(sizeof(sal_uInt32));
130 sal_uInt32 aReferenceComponentNameLength = aDataSpaceStream.readuInt32();
131 // sReferenceComponentName unused for the moment
132 if (aDataSpaceStream.getRemaining() < aReferenceComponentNameLength)
133 {
134 bBroken = true;
135 break;
136 }
137 aDataSpaceStream.readUnicodeArray(aReferenceComponentNameLength / 2);
138 aDataSpaceStream.skip((4 - (aReferenceComponentNameLength & 3))
139 & 3); // Skip padding
140
141 bBroken |= aDataSpaceStream.isEof();
142 }
143
144 sal_uInt32 aDataSpaceNameLength = aDataSpaceStream.readuInt32();
145 if (aDataSpaceStream.getRemaining() < aDataSpaceNameLength)
146 {
147 bBroken = true;
148 break;
149 }
150 sDataSpaceName = aDataSpaceStream.readUnicodeArray(aDataSpaceNameLength / 2);
151 aDataSpaceStream.skip((4 - (aDataSpaceNameLength & 3)) & 3); // Skip padding
152
153 bBroken |= aDataSpaceStream.isEof();
154 }
155
156 if (bBroken)
157 {
158 SAL_WARN("oox", "EOF on parsing DataSpaceMapEntry table");
159 return false;
160 }
161 }
162 else
163 {
164 // Fallback for documents generated by LO: they sometimes do not have all
165 // required by MS-OFFCRYPTO specification streams (0x6DataSpaces/DataSpaceMap and others)
166 SAL_WARN("oox", "Encrypted package does not contain DataSpaceMap");
167 sDataSpaceName = "StrongEncryptionDataSpace";
168 }
169
170 uno::Sequence<uno::Any> aArguments;
172 mxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
173 "com.sun.star.comp.oox.crypto." + sDataSpaceName, aArguments, mxContext),
174 css::uno::UNO_QUERY);
175
176 if (!mxPackageEncryption.is())
177 {
178 // we do not know how to decrypt this document
179 return false;
180 }
181
182 return mxPackageEncryption->readEncryptionInfo(maStreamsSequence);
183}
184
185uno::Sequence<beans::NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword)
186{
187 if (!mxPackageEncryption.is())
188 return uno::Sequence<beans::NamedValue>();
189
190 return mxPackageEncryption->createEncryptionData(rPassword);
191}
192
193bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStream)
194{
195 bool bResult = false;
196
197 if (!mrOleStorage.isStorage())
198 return false;
199
200 if (!mxPackageEncryption.is())
201 return false;
202
203 // open the required input streams in the encrypted package
204 uno::Reference<io::XInputStream> xEncryptedPackage
205 = mrOleStorage.openInputStream("EncryptedPackage");
206
207 // create temporary file for unencrypted package
208 uno::Reference<io::XOutputStream> xDecryptedPackage = xDocumentStream->getOutputStream();
209
210 bResult = mxPackageEncryption->decrypt(xEncryptedPackage, xDecryptedPackage);
211
212 css::uno::Reference<io::XSeekable> xSeekable(xDecryptedPackage, css::uno::UNO_QUERY);
213 xSeekable->seek(0);
214
215 if (bResult)
216 return mxPackageEncryption->checkDataIntegrity();
217
218 return bResult;
219}
220
221} // namespace oox::crypto
222
223/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
css::uno::Sequence< css::beans::NamedValue > getAsConstNamedValueList() const
OUString readUnicodeArray(sal_Int32 nChars)
Reads a Unicode character array and returns the string.
sal_Int64 getRemaining() const
Returns the size of the remaining data available in the stream, if stream supports size() and tell(),...
bool isEof() const
Returns true, if the stream position is invalid (EOF).
Wraps a UNO input stream and provides convenient access functions.
virtual void skip(sal_Int32 nBytes, size_t nAtomSize=1) override
Seeks the stream forward by the passed number of bytes.
virtual sal_Int32 readData(StreamDataSequence &orData, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the passed sequence.
virtual sal_Int64 size() const override
Returns the size of the stream, if wrapped stream is seekable, otherwise -1.
Base class for storage access implementations.
Definition: storagebase.hxx:52
OUString getPath() const
Returns the full path of this storage.
void getElementNames(::std::vector< OUString > &orElementNames) const
Fills the passed vector with the names of all direct elements of this storage.
css::uno::Reference< css::io::XInputStream > openInputStream(const OUString &rStreamName)
Opens and returns the specified input stream from the storage.
bool isStorage() const
Returns true, if the object represents a valid storage.
Definition: storagebase.cxx:90
StorageRef openSubStorage(const OUString &rStorageName, bool bCreateMissing)
Opens and returns the specified sub storage from the storage.
bool isRootStorage() const
Returns true, if the object represents the root storage.
Definition: storagebase.cxx:95
bool decrypt(const css::uno::Reference< css::io::XStream > &xDocumentStream)
DocumentDecryption(css::uno::Reference< css::uno::XComponentContext > xContext, oox::ole::OleStorage &rOleStorage)
css::uno::Reference< css::packages::XPackageEncryption > mxPackageEncryption
oox::ole::OleStorage & mrOleStorage
css::uno::Sequence< css::beans::NamedValue > createEncryptionData(const OUString &rPassword)
css::uno::Reference< css::uno::XComponentContext > mxContext
css::uno::Sequence< css::beans::NamedValue > maStreamsSequence
bool generateEncryptionKey(const OUString &rPassword)
Implements stream access for binary OLE storages.
Definition: olestorage.hxx:44
uno::Reference< uno::XComponentContext > mxContext
OString sName
Definition: drawingml.cxx:4451
Sequence< PropertyValue > aArguments
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
std::shared_ptr< StorageBase > StorageRef
Definition: storagebase.hxx:42