/src/libreoffice/oox/source/crypto/DocumentDecryption.cxx
Line | Count | Source |
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 | | |
11 | | #include <oox/crypto/DocumentDecryption.hxx> |
12 | | |
13 | | #include <comphelper/sequenceashashmap.hxx> |
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> |
22 | | #include <oox/helper/binaryinputstream.hxx> |
23 | | |
24 | | #include <sal/log.hxx> |
25 | | #include <utility> |
26 | | |
27 | | namespace |
28 | | { |
29 | | void lcl_getListOfStreams(oox::StorageBase* pStorage, std::vector<OUString>& rElementNames) |
30 | 32.8k | { |
31 | 32.8k | std::vector<OUString> oElementNames; |
32 | 32.8k | pStorage->getElementNames(oElementNames); |
33 | 32.8k | for (const auto& sName : oElementNames) |
34 | 96.3k | { |
35 | 96.3k | oox::StorageRef rSubStorage = pStorage->openSubStorage(sName, false); |
36 | 96.3k | if (rSubStorage && rSubStorage->isStorage()) |
37 | 26.5k | { |
38 | 26.5k | lcl_getListOfStreams(rSubStorage.get(), rElementNames); |
39 | 26.5k | } |
40 | 69.7k | else |
41 | 69.7k | { |
42 | 69.7k | if (pStorage->isRootStorage()) |
43 | 10.4k | rElementNames.push_back(sName); |
44 | 59.3k | else |
45 | 59.3k | rElementNames.push_back(pStorage->getPath() + "/" + sName); |
46 | 69.7k | } |
47 | 96.3k | } |
48 | 32.8k | } |
49 | | } |
50 | | |
51 | | namespace oox::crypto |
52 | | { |
53 | | using namespace css; |
54 | | |
55 | | DocumentDecryption::DocumentDecryption(css::uno::Reference<css::uno::XComponentContext> xContext, |
56 | | oox::ole::OleStorage& rOleStorage) |
57 | 6.22k | : mxContext(std::move(xContext)) |
58 | 6.22k | , mrOleStorage(rOleStorage) |
59 | 6.22k | { |
60 | | // Get OLE streams into sequences for later use in CryptoEngine |
61 | 6.22k | std::vector<OUString> aStreamNames; |
62 | 6.22k | lcl_getListOfStreams(&mrOleStorage, aStreamNames); |
63 | | |
64 | 6.22k | comphelper::SequenceAsHashMap aStreamsData; |
65 | 6.22k | for (const auto& sStreamName : aStreamNames) |
66 | 32.6k | { |
67 | 32.6k | uno::Reference<io::XInputStream> xStream = mrOleStorage.openInputStream(sStreamName); |
68 | 32.6k | if (!xStream.is()) |
69 | 876 | throw io::IOException("Cannot open OLE input stream for " + sStreamName + "!"); |
70 | | |
71 | 31.7k | BinaryXInputStream aBinaryInputStream(xStream, true); |
72 | | |
73 | 31.7k | css::uno::Sequence<sal_Int8> oData; |
74 | 31.7k | sal_Int32 nStreamSize = aBinaryInputStream.size(); |
75 | 31.7k | sal_Int32 nReadBytes = aBinaryInputStream.readData(oData, nStreamSize); |
76 | | |
77 | 31.7k | if (nStreamSize != nReadBytes) |
78 | 0 | { |
79 | 0 | SAL_WARN("oox", "OLE stream invalid content"); |
80 | 0 | throw io::IOException("OLE stream invalid content for " + sStreamName + "!"); |
81 | 0 | } |
82 | | |
83 | 31.7k | aStreamsData[sStreamName] <<= oData; |
84 | 31.7k | } |
85 | 5.34k | maStreamsSequence = aStreamsData.getAsConstNamedValueList(); |
86 | 5.34k | } |
87 | | |
88 | | bool DocumentDecryption::generateEncryptionKey(const OUString& rPassword) |
89 | 46 | { |
90 | 46 | if (mxPackageEncryption.is()) |
91 | 46 | return mxPackageEncryption->generateEncryptionKey(rPassword); |
92 | 0 | return false; |
93 | 46 | } |
94 | | |
95 | | bool DocumentDecryption::readEncryptionInfo() |
96 | 5.34k | { |
97 | 5.34k | if (!mrOleStorage.isStorage()) |
98 | 0 | return false; |
99 | | |
100 | | // Read 0x6DataSpaces/DataSpaceMap |
101 | 5.34k | uno::Reference<io::XInputStream> xDataSpaceMap |
102 | 5.34k | = mrOleStorage.openInputStream(u"\006DataSpaces/DataSpaceMap"_ustr); |
103 | 5.34k | OUString sDataSpaceName; |
104 | | |
105 | 5.34k | if (xDataSpaceMap.is()) |
106 | 288 | { |
107 | 288 | bool bBroken = false; |
108 | | |
109 | 288 | BinaryXInputStream aDataSpaceStream(xDataSpaceMap, true); |
110 | 288 | sal_uInt32 aHeaderLength = aDataSpaceStream.readuInt32(); |
111 | 288 | SAL_WARN_IF(aHeaderLength != 8, "oox", |
112 | 288 | "DataSpaceMap length != 8 is not supported. Some content may be skipped"); |
113 | 288 | sal_uInt32 aEntryCount = aDataSpaceStream.readuInt32(); |
114 | 288 | SAL_WARN_IF(aEntryCount != 1, "oox", |
115 | 288 | "DataSpaceMap contains more than one entry. Some content may be skipped"); |
116 | | |
117 | | // Read each DataSpaceMapEntry (MS-OFFCRYPTO 2.1.6.1) |
118 | 2.74k | for (sal_uInt32 i = 0; i < aEntryCount && !bBroken; i++) |
119 | 2.52k | { |
120 | | // entryLen unused for the moment |
121 | 2.52k | aDataSpaceStream.skip(sizeof(sal_uInt32)); |
122 | | |
123 | | // Read each DataSpaceReferenceComponent (MS-OFFCRYPTO 2.1.6.2) |
124 | 2.52k | sal_uInt32 aReferenceComponentCount = aDataSpaceStream.readuInt32(); |
125 | 8.29k | for (sal_uInt32 j = 0; j < aReferenceComponentCount && !bBroken; j++) |
126 | 5.87k | { |
127 | | // Read next reference component |
128 | | // refComponentType unused for the moment |
129 | 5.87k | aDataSpaceStream.skip(sizeof(sal_uInt32)); |
130 | 5.87k | sal_uInt32 aReferenceComponentNameLength = aDataSpaceStream.readuInt32(); |
131 | | // sReferenceComponentName unused for the moment |
132 | 5.87k | if (aDataSpaceStream.getRemaining() < aReferenceComponentNameLength) |
133 | 101 | { |
134 | 101 | bBroken = true; |
135 | 101 | break; |
136 | 101 | } |
137 | 5.77k | aDataSpaceStream.readUnicodeArray(aReferenceComponentNameLength / 2); |
138 | 5.77k | aDataSpaceStream.skip((4 - (aReferenceComponentNameLength & 3)) |
139 | 5.77k | & 3); // Skip padding |
140 | | |
141 | 5.77k | bBroken |= aDataSpaceStream.isEof(); |
142 | 5.77k | } |
143 | | |
144 | 2.52k | sal_uInt32 aDataSpaceNameLength = aDataSpaceStream.readuInt32(); |
145 | 2.52k | if (aDataSpaceStream.getRemaining() < aDataSpaceNameLength) |
146 | 68 | { |
147 | 68 | bBroken = true; |
148 | 68 | break; |
149 | 68 | } |
150 | 2.45k | sDataSpaceName = aDataSpaceStream.readUnicodeArray(aDataSpaceNameLength / 2); |
151 | 2.45k | aDataSpaceStream.skip((4 - (aDataSpaceNameLength & 3)) & 3); // Skip padding |
152 | | |
153 | 2.45k | bBroken |= aDataSpaceStream.isEof(); |
154 | 2.45k | } |
155 | | |
156 | 288 | if (bBroken) |
157 | 120 | { |
158 | 120 | SAL_WARN("oox", "EOF on parsing DataSpaceMapEntry table"); |
159 | 120 | return false; |
160 | 120 | } |
161 | 288 | } |
162 | 5.06k | else |
163 | 5.06k | { |
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 | 5.06k | SAL_WARN("oox", "Encrypted package does not contain DataSpaceMap"); |
167 | 5.06k | sDataSpaceName = "StrongEncryptionDataSpace"; |
168 | 5.06k | } |
169 | | |
170 | 5.22k | uno::Sequence<uno::Any> aArguments; |
171 | 5.22k | mxPackageEncryption.set( |
172 | 5.22k | mxContext->getServiceManager()->createInstanceWithArgumentsAndContext( |
173 | 5.22k | "com.sun.star.comp.oox.crypto." + sDataSpaceName, aArguments, mxContext), |
174 | 5.22k | css::uno::UNO_QUERY); |
175 | | |
176 | 5.22k | if (!mxPackageEncryption.is()) |
177 | 49 | { |
178 | | // we do not know how to decrypt this document |
179 | 49 | return false; |
180 | 49 | } |
181 | | |
182 | 5.18k | return mxPackageEncryption->readEncryptionInfo(maStreamsSequence); |
183 | 5.22k | } |
184 | | |
185 | | uno::Sequence<beans::NamedValue> DocumentDecryption::createEncryptionData(const OUString& rPassword) |
186 | 29 | { |
187 | 29 | if (!mxPackageEncryption.is()) |
188 | 0 | return uno::Sequence<beans::NamedValue>(); |
189 | | |
190 | 29 | return mxPackageEncryption->createEncryptionData(rPassword); |
191 | 29 | } |
192 | | |
193 | | bool DocumentDecryption::decrypt(const uno::Reference<io::XStream>& xDocumentStream) |
194 | 29 | { |
195 | 29 | bool bResult = false; |
196 | | |
197 | 29 | if (!mrOleStorage.isStorage()) |
198 | 0 | return false; |
199 | | |
200 | 29 | if (!mxPackageEncryption.is()) |
201 | 0 | return false; |
202 | | |
203 | | // open the required input streams in the encrypted package |
204 | 29 | uno::Reference<io::XInputStream> xEncryptedPackage |
205 | 29 | = mrOleStorage.openInputStream(u"EncryptedPackage"_ustr); |
206 | | |
207 | | // create temporary file for unencrypted package |
208 | 29 | uno::Reference<io::XOutputStream> xDecryptedPackage = xDocumentStream->getOutputStream(); |
209 | | |
210 | 29 | bResult = mxPackageEncryption->decrypt(xEncryptedPackage, xDecryptedPackage); |
211 | | |
212 | 29 | css::uno::Reference<io::XSeekable> xSeekable(xDecryptedPackage, css::uno::UNO_QUERY); |
213 | 29 | xSeekable->seek(0); |
214 | | |
215 | 29 | if (bResult) |
216 | 29 | return mxPackageEncryption->checkDataIntegrity(); |
217 | | |
218 | 0 | return bResult; |
219 | 29 | } |
220 | | |
221 | | } // namespace oox::crypto |
222 | | |
223 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |