Line data Source code
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 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <xmlsecurity/documentsignaturehelper.hxx>
22 :
23 : #include <com/sun/star/container/XNameAccess.hpp>
24 : #include <com/sun/star/lang/XComponent.hpp>
25 : #include <com/sun/star/lang/DisposedException.hpp>
26 : #include <com/sun/star/embed/XStorage.hpp>
27 : #include <com/sun/star/embed/ElementModes.hpp>
28 : #include <com/sun/star/beans/XPropertySet.hpp>
29 :
30 : #include <comphelper/documentconstants.hxx>
31 : #include <tools/debug.hxx>
32 : #include <rtl/uri.hxx>
33 :
34 : using namespace ::com::sun::star::uno;
35 :
36 : namespace
37 : {
38 0 : OUString getElement(OUString const & version, ::sal_Int32 * index)
39 : {
40 0 : while (*index < version.getLength() && version[*index] == '0') {
41 0 : ++*index;
42 : }
43 0 : return version.getToken(0, '.', *index);
44 : }
45 :
46 :
47 :
48 : // Return 1 if version1 is greater then version 2, 0 if they are equal
49 : //and -1 if version1 is less version 2
50 0 : int compareVersions(
51 : OUString const & version1, OUString const & version2)
52 : {
53 0 : for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
54 0 : OUString e1(getElement(version1, &i1));
55 0 : OUString e2(getElement(version2, &i2));
56 0 : if (e1.getLength() < e2.getLength()) {
57 0 : return -1;
58 0 : } else if (e1.getLength() > e2.getLength()) {
59 0 : return 1;
60 0 : } else if (e1 < e2) {
61 0 : return -1;
62 0 : } else if (e1 > e2) {
63 0 : return 1;
64 : }
65 0 : }
66 0 : return 0;
67 : }
68 : }
69 : //If the OOo 3.0 mode is used then we exclude
70 : //'mimetype' and all content of 'META-INF'.
71 : //If the argument 'bSigning' is true then the element list is created for a signing
72 : //operation in which case we use the latest signing algorithm. That is all elements
73 : //we find in the zip storage are added to the list. We do not support the old signatures
74 : //which did not contain all files.
75 : //If 'bSigning' is false, then we validate. If the user enabled validating according to OOo 3.0
76 : //then mimetype and all content of META-INF must be excluded.
77 0 : void ImplFillElementList(
78 : std::vector< OUString >& rList, const Reference < css::embed::XStorage >& rxStore,
79 : const OUString& rRootStorageName, const bool bRecursive,
80 : const DocumentSignatureAlgorithm mode)
81 : {
82 0 : OUString aMetaInfName( "META-INF" );
83 0 : OUString sMimeTypeName ("mimetype");
84 0 : OUString aSep( "/" );
85 :
86 0 : Reference < css::container::XNameAccess > xElements( rxStore, UNO_QUERY );
87 0 : Sequence< OUString > aElements = xElements->getElementNames();
88 0 : sal_Int32 nElements = aElements.getLength();
89 0 : const OUString* pNames = aElements.getConstArray();
90 :
91 0 : for ( sal_Int32 n = 0; n < nElements; n++ )
92 : {
93 0 : if (mode != OOo3_2Document
94 0 : && (pNames[n] == aMetaInfName
95 0 : || pNames[n] == sMimeTypeName))
96 : {
97 0 : continue;
98 : }
99 : else
100 : {
101 : OUString sEncName = ::rtl::Uri::encode(
102 0 : pNames[n], rtl_UriCharClassRelSegment,
103 0 : rtl_UriEncodeStrict, RTL_TEXTENCODING_UTF8);
104 0 : if (sEncName.isEmpty() && !pNames[n].isEmpty())
105 0 : throw css::uno::RuntimeException("Failed to encode element name of XStorage", 0);
106 :
107 0 : if ( rxStore->isStreamElement( pNames[n] ) )
108 : {
109 : //Exclude documentsignatures.xml!
110 0 : if (pNames[n].equals(
111 0 : DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()))
112 0 : continue;
113 0 : OUString aFullName( rRootStorageName + sEncName );
114 0 : rList.push_back(aFullName);
115 : }
116 0 : else if ( bRecursive && rxStore->isStorageElement( pNames[n] ) )
117 : {
118 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( pNames[n], css::embed::ElementModes::READ );
119 0 : OUString aFullRootName( rRootStorageName + sEncName + aSep );
120 0 : ImplFillElementList(rList, xSubStore, aFullRootName, bRecursive, mode);
121 0 : }
122 : }
123 0 : }
124 0 : }
125 :
126 :
127 0 : bool DocumentSignatureHelper::isODFPre_1_2(const OUString & sVersion)
128 : {
129 : //The property version exists only if the document is at least version 1.2
130 : //That is, if the document has version 1.1 and sVersion is empty.
131 : //The constant is defined in comphelper/documentconstants.hxx
132 0 : if (compareVersions(sVersion, ODFVER_012_TEXT) == -1)
133 0 : return true;
134 0 : return false;
135 : }
136 :
137 0 : bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation & sigInfo)
138 : {
139 0 : OUString sManifestURI("META-INF/manifest.xml");
140 0 : bool bOOo3_2 = false;
141 : typedef ::std::vector< SignatureReferenceInformation >::const_iterator CIT;
142 0 : for (CIT i = sigInfo.vSignatureReferenceInfors.begin();
143 0 : i < sigInfo.vSignatureReferenceInfors.end(); ++i)
144 : {
145 0 : if (i->ouURI.equals(sManifestURI))
146 : {
147 0 : bOOo3_2 = true;
148 0 : break;
149 : }
150 : }
151 0 : return bOOo3_2;
152 : }
153 :
154 : DocumentSignatureAlgorithm
155 0 : DocumentSignatureHelper::getDocumentAlgorithm(
156 : const OUString & sODFVersion, const SignatureInformation & sigInfo)
157 : {
158 : OSL_ASSERT(!sODFVersion.isEmpty());
159 0 : DocumentSignatureAlgorithm mode = OOo3_2Document;
160 0 : if (!isOOo3_2_Signature(sigInfo))
161 : {
162 0 : if (isODFPre_1_2(sODFVersion))
163 0 : mode = OOo2Document;
164 : else
165 0 : mode = OOo3_0Document;
166 : }
167 0 : return mode;
168 : }
169 :
170 : //The function creates a list of files which are to be signed or for which
171 : //the signature is to be validated. The strings are UTF8 encoded URIs which
172 : //contain '/' as path separators.
173 : //
174 : //The algorithm how document signatures are created and validated has
175 : //changed over time. The change affects only which files within the document
176 : //are changed. Document signatures created by OOo 2.x only used particular files. Since
177 : //OOo 3.0 everything except "mimetype" and "META-INF" are signed. As of OOo 3.2 everything
178 : //except META-INF/documentsignatures.xml is signed.
179 : //Signatures are validated according to the algorithm which was then used for validation.
180 : //That is, when validating a signature which was created by OOo 3.0, then mimetype and
181 : //META-INF are not used.
182 : //
183 : //When a signature is created then we always use the latest algorithm. That is, we use
184 : //that of OOo 3.2
185 : std::vector< OUString >
186 0 : DocumentSignatureHelper::CreateElementList(
187 : const Reference < css::embed::XStorage >& rxStore,
188 : DocumentSignatureMode eMode,
189 : const DocumentSignatureAlgorithm mode)
190 : {
191 0 : std::vector< OUString > aElements;
192 0 : OUString aSep( "/" );
193 :
194 0 : switch ( eMode )
195 : {
196 : case SignatureModeDocumentContent:
197 : {
198 0 : if (mode == OOo2Document) //that is, ODF 1.0, 1.1
199 : {
200 : // 1) Main content
201 0 : ImplFillElementList(aElements, rxStore, OUString(), false, mode);
202 :
203 : // 2) Pictures...
204 0 : OUString aSubStorageName( "Pictures" );
205 : try
206 : {
207 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
208 0 : ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
209 : }
210 0 : catch(css::io::IOException& )
211 : {
212 : ; // Doesn't have to exist...
213 : }
214 : // 3) OLE....
215 0 : aSubStorageName = "ObjectReplacements";
216 : try
217 : {
218 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
219 0 : ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
220 0 : xSubStore.clear();
221 :
222 : // Object folders...
223 0 : OUString aMatchStr( "Object " );
224 0 : Reference < css::container::XNameAccess > xElements( rxStore, UNO_QUERY );
225 0 : Sequence< OUString > aElementNames = xElements->getElementNames();
226 0 : sal_Int32 nElements = aElementNames.getLength();
227 0 : const OUString* pNames = aElementNames.getConstArray();
228 0 : for ( sal_Int32 n = 0; n < nElements; n++ )
229 : {
230 0 : if ( ( pNames[n].match( aMatchStr ) ) && rxStore->isStorageElement( pNames[n] ) )
231 : {
232 0 : Reference < css::embed::XStorage > xTmpSubStore = rxStore->openStorageElement( pNames[n], css::embed::ElementModes::READ );
233 0 : ImplFillElementList(aElements, xTmpSubStore, pNames[n]+aSep, true, mode);
234 : }
235 0 : }
236 : }
237 0 : catch( com::sun::star::io::IOException& )
238 : {
239 : ; // Doesn't have to exist...
240 0 : }
241 : }
242 : else
243 : {
244 : // Everything except META-INF
245 0 : ImplFillElementList(aElements, rxStore, OUString(), true, mode);
246 : }
247 : }
248 0 : break;
249 : case SignatureModeMacros:
250 : {
251 : // 1) Macros
252 0 : OUString aSubStorageName( "Basic" );
253 : try
254 : {
255 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
256 0 : ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
257 : }
258 0 : catch( com::sun::star::io::IOException& )
259 : {
260 : ; // Doesn't have to exist...
261 : }
262 :
263 : // 2) Dialogs
264 0 : aSubStorageName = "Dialogs";
265 : try
266 : {
267 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
268 0 : ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
269 : }
270 0 : catch( com::sun::star::io::IOException& )
271 : {
272 : ; // Doesn't have to exist...
273 : }
274 : // 3) Scripts
275 0 : aSubStorageName = "Scripts";
276 : try
277 : {
278 0 : Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
279 0 : ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
280 : }
281 0 : catch( css::io::IOException& )
282 : {
283 : ; // Doesn't have to exist...
284 0 : }
285 : }
286 0 : break;
287 : case SignatureModePackage:
288 : {
289 : // Everything except META-INF
290 0 : ImplFillElementList(aElements, rxStore, OUString(), true, mode);
291 : }
292 0 : break;
293 : }
294 :
295 0 : return aElements;
296 : }
297 :
298 214 : SignatureStreamHelper DocumentSignatureHelper::OpenSignatureStream(
299 : const Reference < css::embed::XStorage >& rxStore, sal_Int32 nOpenMode, DocumentSignatureMode eDocSigMode )
300 : {
301 214 : sal_Int32 nSubStorageOpenMode = css::embed::ElementModes::READ;
302 214 : if ( nOpenMode & css::embed::ElementModes::WRITE )
303 0 : nSubStorageOpenMode = css::embed::ElementModes::WRITE;
304 :
305 214 : SignatureStreamHelper aHelper;
306 :
307 : try
308 : {
309 214 : OUString aSIGStoreName( "META-INF" );
310 214 : aHelper.xSignatureStorage = rxStore->openStorageElement( aSIGStoreName, nSubStorageOpenMode );
311 214 : if ( aHelper.xSignatureStorage.is() )
312 : {
313 214 : OUString aSIGStreamName;
314 214 : if ( eDocSigMode == SignatureModeDocumentContent )
315 214 : aSIGStreamName = DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
316 0 : else if ( eDocSigMode == SignatureModeMacros )
317 0 : aSIGStreamName = DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
318 : else
319 0 : aSIGStreamName = DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
320 :
321 214 : aHelper.xSignatureStream = aHelper.xSignatureStorage->openStreamElement( aSIGStreamName, nOpenMode );
322 214 : }
323 : }
324 214 : catch(css::io::IOException& )
325 : {
326 : // Doesn't have to exist...
327 : DBG_ASSERT( nOpenMode == css::embed::ElementModes::READ, "Error creating signature stream..." );
328 : }
329 :
330 214 : return aHelper;
331 : }
332 :
333 : //sElementList contains all files which are expected to be signed. Only those files must me signed,
334 : //no more, no less.
335 : //The DocumentSignatureAlgorithm indicates if the document was created with OOo 2.x. Then
336 : //the uri s in the Reference elements in the signature, were not properly encoded.
337 : // For example: <Reference URI="ObjectReplacements/Object 1">
338 0 : bool DocumentSignatureHelper::checkIfAllFilesAreSigned(
339 : const ::std::vector< OUString > & sElementList,
340 : const SignatureInformation & sigInfo,
341 : const DocumentSignatureAlgorithm alg)
342 : {
343 : // Can only be valid if ALL streams are signed, which means real stream count == signed stream count
344 0 : unsigned int nRealCount = 0;
345 0 : for ( int i = sigInfo.vSignatureReferenceInfors.size(); i; )
346 : {
347 0 : const SignatureReferenceInformation& rInf = sigInfo.vSignatureReferenceInfors[--i];
348 : // There is also an extra entry of type TYPE_SAMEDOCUMENT_REFERENCE because of signature date.
349 0 : if ( ( rInf.nType == TYPE_BINARYSTREAM_REFERENCE ) || ( rInf.nType == TYPE_XMLSTREAM_REFERENCE ) )
350 : {
351 0 : OUString sReferenceURI = rInf.ouURI;
352 0 : if (alg == OOo2Document)
353 : {
354 : //Comparing URIs is a difficult. Therefore we kind of normalize
355 : //it before comparing. We assume that our URI do not have a leading "./"
356 : //and fragments at the end (...#...)
357 0 : sReferenceURI = ::rtl::Uri::encode(
358 : sReferenceURI, rtl_UriCharClassPchar,
359 0 : rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
360 : }
361 :
362 : //find the file in the element list
363 : typedef ::std::vector< OUString >::const_iterator CIT;
364 0 : for (CIT aIter = sElementList.begin(); aIter != sElementList.end(); ++aIter)
365 : {
366 0 : OUString sElementListURI = *aIter;
367 0 : if (alg == OOo2Document)
368 : {
369 0 : sElementListURI =
370 : ::rtl::Uri::encode(
371 : sElementListURI, rtl_UriCharClassPchar,
372 0 : rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
373 : }
374 0 : if (sElementListURI.equals(sReferenceURI))
375 : {
376 0 : nRealCount++;
377 0 : break;
378 : }
379 0 : }
380 : }
381 : }
382 0 : return sElementList.size() == nRealCount;
383 : }
384 :
385 : /*Compares the Uri which are obtained from CreateElementList with
386 : the path obtained from the manifest.xml.
387 : Returns true if both strings are equal.
388 : */
389 0 : bool DocumentSignatureHelper::equalsReferenceUriManifestPath(
390 : const OUString & rUri, const OUString & rPath)
391 : {
392 0 : bool retVal = false;
393 : //split up the uri and path into segments. Both are separated by '/'
394 0 : std::vector<OUString> vUriSegments;
395 0 : sal_Int32 nIndex = 0;
396 0 : do
397 : {
398 0 : OUString aToken = rUri.getToken( 0, '/', nIndex );
399 0 : vUriSegments.push_back(aToken);
400 : }
401 0 : while (nIndex >= 0);
402 :
403 0 : std::vector<OUString> vPathSegments;
404 0 : nIndex = 0;
405 0 : do
406 : {
407 0 : OUString aToken = rPath.getToken( 0, '/', nIndex );
408 0 : vPathSegments.push_back(aToken);
409 : }
410 0 : while (nIndex >= 0);
411 :
412 : //Now compare each segment of the uri with its counterpart from the path
413 0 : if (vUriSegments.size() == vPathSegments.size())
414 : {
415 0 : retVal = true;
416 : typedef std::vector<OUString>::const_iterator CIT;
417 0 : for (CIT i = vUriSegments.begin(), j = vPathSegments.begin();
418 0 : i != vUriSegments.end(); ++i, ++j)
419 : {
420 : //Decode the uri segment, so that %20 becomes ' ', etc.
421 : OUString sDecUri = ::rtl::Uri::decode(
422 0 : *i, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
423 0 : if (!sDecUri.equals(*j))
424 : {
425 0 : retVal = false;
426 0 : break;
427 : }
428 0 : }
429 : }
430 :
431 0 : return retVal;
432 : }
433 :
434 214 : OUString DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()
435 : {
436 214 : return OUString( "documentsignatures.xml" );
437 : }
438 :
439 0 : OUString DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()
440 : {
441 0 : return OUString( "macrosignatures.xml" );
442 : }
443 :
444 0 : OUString DocumentSignatureHelper::GetPackageSignatureDefaultStreamName()
445 : {
446 0 : return OUString( "packagesignatures.xml" );
447 : }
448 :
449 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|