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>
21 #include <oox/ole/olestorage.hxx>
23 
24 #include <sal/log.hxx>
25 
26 namespace
27 {
28 void lcl_getListOfStreams(oox::StorageBase* pStorage, std::vector<OUString>& rElementNames)
29 {
30  std::vector<OUString> oElementNames;
31  pStorage->getElementNames(oElementNames);
32  for (const auto& sName : oElementNames)
33  {
34  oox::StorageRef rSubStorage = pStorage->openSubStorage(sName, false);
35  if (rSubStorage && rSubStorage->isStorage())
36  {
37  lcl_getListOfStreams(rSubStorage.get(), rElementNames);
38  }
39  else
40  {
41  if (pStorage->isRootStorage())
42  rElementNames.push_back(sName);
43  else
44  rElementNames.push_back(pStorage->getPath() + "/" + sName);
45  }
46  }
47 }
48 }
49 
50 namespace oox::crypto
51 {
52 using namespace css;
53 
55  const css::uno::Reference<css::uno::XComponentContext>& rxContext,
56  oox::ole::OleStorage& rOleStorage)
57  : mxContext(rxContext)
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 
64  comphelper::SequenceAsHashMap aStreamsData;
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 
88 bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword)
89 {
90  if (mxPackageEncryption.is())
91  return mxPackageEncryption->generateEncryptionKey(rPassword);
92  return false;
93 }
94 
96 {
97  if (!mrOleStorage.isStorage())
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 
185 uno::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 
193 bool 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: */
virtual sal_Int32 readData(StreamDataSequence &orData, sal_Int32 nBytes, size_t nAtomSize=1) override
Reads nBytes bytes to the passed sequence.
DocumentDecryption(const css::uno::Reference< css::uno::XComponentContext > &rxContext, oox::ole::OleStorage &rOleStorage)
const char * sName
Definition: olehelper.cxx:92
Wraps a UNO input stream and provides convenient access functions.
bool isStorage() const
Returns true, if the object represents a valid storage.
Definition: storagebase.cxx:89
uno::Reference< uno::XComponentContext > mxContext
css::uno::Reference< css::uno::XComponentContext > mxContext
css::uno::Sequence< css::beans::NamedValue > getAsConstNamedValueList() const
std::shared_ptr< StorageBase > StorageRef
Definition: storagebase.hxx:42
Implements stream access for binary OLE storages.
Definition: olestorage.hxx:43
oox::ole::OleStorage & mrOleStorage
Sequence< PropertyValue > aArguments
Reference< XInputStream > xStream
Base class for storage access implementations.
Definition: storagebase.hxx:51
css::uno::Reference< css::packages::XPackageEncryption > mxPackageEncryption
css::uno::Sequence< css::beans::NamedValue > createEncryptionData(const OUString &rPassword)
bool decrypt(const css::uno::Reference< css::io::XStream > &xDocumentStream)
css::uno::Reference< css::io::XInputStream > openInputStream(const OUString &rStreamName)
Opens and returns the specified input stream from the storage.
bool generateEncryptionKey(const OUString &rPassword)
sal_Int64 getRemaining() const
Returns the size of the remaining data available in the stream, if stream supports size() and tell()...
css::uno::Sequence< css::beans::NamedValue > maStreamsSequence
virtual sal_Int64 size() const override
Returns the size of the stream, if wrapped stream is seekable, otherwise -1.
#define SAL_WARN_IF(condition, area, stream)
OUString getPath() const
Returns the full path of this storage.
bool isRootStorage() const
Returns true, if the object represents the root storage.
Definition: storagebase.cxx:94
OUString readUnicodeArray(sal_Int32 nChars)
Reads a Unicode character array and returns the string.
#define SAL_WARN(area, stream)
bool isEof() const
Returns true, if the stream position is invalid (EOF).
virtual void skip(sal_Int32 nBytes, size_t nAtomSize=1) override
Seeks the stream forward by the passed number of bytes.
void getElementNames(::std::vector< OUString > &orElementNames) const
Fills the passed vector with the names of all direct elements of this storage.
StorageRef openSubStorage(const OUString &rStorageName, bool bCreateMissing)
Opens and returns the specified sub storage from the storage.