merged tag ooo/OOO330_m14
[LibreOffice.git] / xmlsecurity / source / helper / documentsignaturehelper.cxx
blobd0fba345588cd53a41714391cd2ddd48c128d4c3
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_xmlsecurity.hxx"
31 #include <xmlsecurity/documentsignaturehelper.hxx>
33 #include <com/sun/star/container/XNameAccess.hpp>
34 #include <com/sun/star/lang/XComponent.hpp>
35 #include <com/sun/star/lang/DisposedException.hpp>
36 #include <com/sun/star/embed/XStorage.hpp>
37 #include <com/sun/star/embed/ElementModes.hpp>
38 #include "com/sun/star/beans/XPropertySet.hpp"
40 #include "comphelper/documentconstants.hxx"
41 #include <tools/debug.hxx>
42 #include "rtl/uri.hxx"
44 using namespace ::com::sun::star::uno;
45 //using namespace ::com::sun::star;
46 namespace css = ::com::sun::star;
47 using rtl::OUString;
50 namespace
52 ::rtl::OUString getElement(::rtl::OUString const & version, ::sal_Int32 * index)
54 while (*index < version.getLength() && version[*index] == '0') {
55 ++*index;
57 return version.getToken(0, '.', *index);
62 // Return 1 if version1 is greater then version 2, 0 if they are equal
63 //and -1 if version1 is less version 2
64 int compareVersions(
65 ::rtl::OUString const & version1, ::rtl::OUString const & version2)
67 for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
68 ::rtl::OUString e1(getElement(version1, &i1));
69 ::rtl::OUString e2(getElement(version2, &i2));
70 if (e1.getLength() < e2.getLength()) {
71 return -1;
72 } else if (e1.getLength() > e2.getLength()) {
73 return 1;
74 } else if (e1 < e2) {
75 return -1;
76 } else if (e1 > e2) {
77 return 1;
80 return 0;
83 //If the OOo 3.0 mode is used then we exclude
84 //'mimetype' and all content of 'META-INF'.
85 //If the argument 'bSigning' is true then the element list is created for a signing
86 //operation in which case we use the latest signing algorithm. That is all elements
87 //we find in the zip storage are added to the list. We do not support the old signatures
88 //which did not contain all files.
89 //If 'bSigning' is false, then we validate. If the user enabled validating according to OOo 3.0
90 //then mimetype and all content of META-INF must be excluded.
91 void ImplFillElementList(
92 std::vector< rtl::OUString >& rList, const Reference < css::embed::XStorage >& rxStore,
93 const ::rtl::OUString rRootStorageName, const bool bRecursive,
94 const DocumentSignatureAlgorithm mode)
96 ::rtl::OUString aMetaInfName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) );
97 ::rtl::OUString sMimeTypeName (RTL_CONSTASCII_USTRINGPARAM("mimetype"));
98 ::rtl::OUString aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) );
100 Reference < css::container::XNameAccess > xElements( rxStore, UNO_QUERY );
101 Sequence< ::rtl::OUString > aElements = xElements->getElementNames();
102 sal_Int32 nElements = aElements.getLength();
103 const ::rtl::OUString* pNames = aElements.getConstArray();
105 for ( sal_Int32 n = 0; n < nElements; n++ )
107 if (mode != OOo3_2Document
108 && (pNames[n] == aMetaInfName
109 || pNames[n] == sMimeTypeName))
111 continue;
113 else
115 ::rtl::OUString sEncName = ::rtl::Uri::encode(
116 pNames[n], rtl_UriCharClassRelSegment,
117 rtl_UriEncodeStrict, RTL_TEXTENCODING_UTF8);
118 if (sEncName.getLength() == 0 && pNames[n].getLength() != 0)
119 throw css::uno::Exception(::rtl::OUString(
120 RTL_CONSTASCII_USTRINGPARAM("Failed to encode element name of XStorage")), 0);
122 if ( rxStore->isStreamElement( pNames[n] ) )
124 //Exclude documentsignatures.xml!
125 if (pNames[n].equals(
126 DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()))
127 continue;
128 ::rtl::OUString aFullName( rRootStorageName + sEncName );
129 rList.push_back(aFullName);
131 else if ( bRecursive && rxStore->isStorageElement( pNames[n] ) )
133 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( pNames[n], css::embed::ElementModes::READ );
134 rtl::OUString aFullRootName( rRootStorageName + sEncName + aSep );
135 ImplFillElementList(rList, xSubStore, aFullRootName, bRecursive, mode);
142 bool DocumentSignatureHelper::isODFPre_1_2(const ::rtl::OUString & sVersion)
144 //The property version exists only if the document is at least version 1.2
145 //That is, if the document has version 1.1 and sVersion is empty.
146 //The constant is defined in comphelper/documentconstants.hxx
147 if (compareVersions(sVersion, ODFVER_012_TEXT) == -1)
148 return true;
149 return false;
152 bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation & sigInfo)
154 ::rtl::OUString sManifestURI(RTL_CONSTASCII_USTRINGPARAM("META-INF/manifest.xml"));
155 bool bOOo3_2 = false;
156 typedef ::std::vector< SignatureReferenceInformation >::const_iterator CIT;
157 for (CIT i = sigInfo.vSignatureReferenceInfors.begin();
158 i < sigInfo.vSignatureReferenceInfors.end(); i++)
160 if (i->ouURI.equals(sManifestURI))
162 bOOo3_2 = true;
163 break;
166 return bOOo3_2;
169 DocumentSignatureAlgorithm
170 DocumentSignatureHelper::getDocumentAlgorithm(
171 const ::rtl::OUString & sODFVersion, const SignatureInformation & sigInfo)
173 OSL_ASSERT(sODFVersion.getLength());
174 DocumentSignatureAlgorithm mode = OOo3_2Document;
175 if (!isOOo3_2_Signature(sigInfo))
177 if (isODFPre_1_2(sODFVersion))
178 mode = OOo2Document;
179 else
180 mode = OOo3_0Document;
182 return mode;
185 //The function creates a list of files which are to be signed or for which
186 //the signature is to be validated. The strings are UTF8 encoded URIs which
187 //contain '/' as path separators.
189 //The algorithm how document signatures are created and validated has
190 //changed over time. The change affects only which files within the document
191 //are changed. Document signatures created by OOo 2.x only used particular files. Since
192 //OOo 3.0 everything except "mimetype" and "META-INF" are signed. As of OOo 3.2 everything
193 //except META-INF/documentsignatures.xml is signed.
194 //Signatures are validated according to the algorithm which was then used for validation.
195 //That is, when validating a signature which was created by OOo 3.0, then mimetype and
196 //META-INF are not used.
198 //When a signature is created then we always use the latest algorithm. That is, we use
199 //that of OOo 3.2
200 std::vector< rtl::OUString >
201 DocumentSignatureHelper::CreateElementList(
202 const Reference < css::embed::XStorage >& rxStore,
203 const ::rtl::OUString /*rRootStorageName*/, DocumentSignatureMode eMode,
204 const DocumentSignatureAlgorithm mode)
206 std::vector< rtl::OUString > aElements;
207 ::rtl::OUString aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) );
209 switch ( eMode )
211 case SignatureModeDocumentContent:
213 if (mode == OOo2Document) //that is, ODF 1.0, 1.1
215 // 1) Main content
216 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), false, mode);
218 // 2) Pictures...
219 rtl::OUString aSubStorageName( rtl::OUString::createFromAscii( "Pictures" ) );
222 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
223 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
225 catch(css::io::IOException& )
227 ; // Doesn't have to exist...
229 // 3) OLE....
230 aSubStorageName = rtl::OUString::createFromAscii( "ObjectReplacements" );
233 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
234 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
235 xSubStore.clear();
237 // Object folders...
238 rtl::OUString aMatchStr( rtl::OUString::createFromAscii( "Object " ) );
239 Reference < css::container::XNameAccess > xElements( rxStore, UNO_QUERY );
240 Sequence< ::rtl::OUString > aElementNames = xElements->getElementNames();
241 sal_Int32 nElements = aElementNames.getLength();
242 const ::rtl::OUString* pNames = aElementNames.getConstArray();
243 for ( sal_Int32 n = 0; n < nElements; n++ )
245 if ( ( pNames[n].match( aMatchStr ) ) && rxStore->isStorageElement( pNames[n] ) )
247 Reference < css::embed::XStorage > xTmpSubStore = rxStore->openStorageElement( pNames[n], css::embed::ElementModes::READ );
248 ImplFillElementList(aElements, xTmpSubStore, pNames[n]+aSep, true, mode);
252 catch( com::sun::star::io::IOException& )
254 ; // Doesn't have to exist...
257 else
259 // Everything except META-INF
260 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), true, mode);
263 break;
264 case SignatureModeMacros:
266 // 1) Macros
267 rtl::OUString aSubStorageName( rtl::OUString::createFromAscii( "Basic" ) );
270 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
271 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
273 catch( com::sun::star::io::IOException& )
275 ; // Doesn't have to exist...
278 // 2) Dialogs
279 aSubStorageName = rtl::OUString::createFromAscii( "Dialogs") ;
282 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
283 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
285 catch( com::sun::star::io::IOException& )
287 ; // Doesn't have to exist...
289 // 3) Scripts
290 aSubStorageName = rtl::OUString::createFromAscii( "Scripts") ;
293 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ );
294 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode);
296 catch( css::io::IOException& )
298 ; // Doesn't have to exist...
301 break;
302 case SignatureModePackage:
304 // Everything except META-INF
305 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), true, mode);
307 break;
310 return aElements;
313 SignatureStreamHelper DocumentSignatureHelper::OpenSignatureStream(
314 const Reference < css::embed::XStorage >& rxStore, sal_Int32 nOpenMode, DocumentSignatureMode eDocSigMode )
316 sal_Int32 nSubStorageOpenMode = css::embed::ElementModes::READ;
317 if ( nOpenMode & css::embed::ElementModes::WRITE )
318 nSubStorageOpenMode = css::embed::ElementModes::WRITE;
320 SignatureStreamHelper aHelper;
324 ::rtl::OUString aSIGStoreName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) );
325 aHelper.xSignatureStorage = rxStore->openStorageElement( aSIGStoreName, nSubStorageOpenMode );
326 if ( aHelper.xSignatureStorage.is() )
328 ::rtl::OUString aSIGStreamName;
329 if ( eDocSigMode == SignatureModeDocumentContent )
330 aSIGStreamName = DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
331 else if ( eDocSigMode == SignatureModeMacros )
332 aSIGStreamName = DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
333 else
334 aSIGStreamName = DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
336 aHelper.xSignatureStream = aHelper.xSignatureStorage->openStreamElement( aSIGStreamName, nOpenMode );
339 catch(css::io::IOException& )
341 // Doesn't have to exist...
342 DBG_ASSERT( nOpenMode == css::embed::ElementModes::READ, "Error creating signature stream..." );
345 return aHelper;
348 //sElementList contains all files which are expected to be signed. Only those files must me signed,
349 //no more, no less.
350 //The DocumentSignatureAlgorithm indicates if the document was created with OOo 2.x. Then
351 //the uri s in the Reference elements in the signature, were not properly encoded.
352 // For example: <Reference URI="ObjectReplacements/Object 1">
353 bool DocumentSignatureHelper::checkIfAllFilesAreSigned(
354 const ::std::vector< ::rtl::OUString > & sElementList,
355 const SignatureInformation & sigInfo,
356 const DocumentSignatureAlgorithm alg)
358 // Can only be valid if ALL streams are signed, which means real stream count == signed stream count
359 unsigned int nRealCount = 0;
360 for ( int i = sigInfo.vSignatureReferenceInfors.size(); i; )
362 const SignatureReferenceInformation& rInf = sigInfo.vSignatureReferenceInfors[--i];
363 // There is also an extra entry of type TYPE_SAMEDOCUMENT_REFERENCE because of signature date.
364 if ( ( rInf.nType == TYPE_BINARYSTREAM_REFERENCE ) || ( rInf.nType == TYPE_XMLSTREAM_REFERENCE ) )
366 ::rtl::OUString sReferenceURI = rInf.ouURI;
367 if (alg == OOo2Document)
369 //Comparing URIs is a difficult. Therefore we kind of normalize
370 //it before comparing. We assume that our URI do not have a leading "./"
371 //and fragments at the end (...#...)
372 sReferenceURI = ::rtl::Uri::encode(
373 sReferenceURI, rtl_UriCharClassPchar,
374 rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
377 //find the file in the element list
378 typedef ::std::vector< ::rtl::OUString >::const_iterator CIT;
379 for (CIT aIter = sElementList.begin(); aIter < sElementList.end(); aIter++)
381 ::rtl::OUString sElementListURI = *aIter;
382 if (alg == OOo2Document)
384 sElementListURI =
385 ::rtl::Uri::encode(
386 sElementListURI, rtl_UriCharClassPchar,
387 rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8);
389 if (sElementListURI.equals(sReferenceURI))
391 nRealCount++;
392 break;
397 return sElementList.size() == nRealCount;
400 /*Compares the Uri which are obtained from CreateElementList with
401 the path obtained from the manifest.xml.
402 Returns true if both strings are equal.
404 bool DocumentSignatureHelper::equalsReferenceUriManifestPath(
405 const OUString & rUri, const OUString & rPath)
407 bool retVal = false;
408 //split up the uri and path into segments. Both are separated by '/'
409 std::vector<OUString> vUriSegments;
410 sal_Int32 nIndex = 0;
413 OUString aToken = rUri.getToken( 0, '/', nIndex );
414 vUriSegments.push_back(aToken);
416 while (nIndex >= 0);
418 std::vector<OUString> vPathSegments;
419 nIndex = 0;
422 OUString aToken = rPath.getToken( 0, '/', nIndex );
423 vPathSegments.push_back(aToken);
425 while (nIndex >= 0);
427 //Now compare each segment of the uri with its counterpart from the path
428 if (vUriSegments.size() == vPathSegments.size())
430 retVal = true;
431 typedef std::vector<OUString>::const_iterator CIT;
432 for (CIT i = vUriSegments.begin(), j = vPathSegments.begin();
433 i != vUriSegments.end(); i++, j++)
435 //Decode the uri segment, so that %20 becomes ' ', etc.
436 OUString sDecUri = ::rtl::Uri::decode(
437 *i, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
438 if (!sDecUri.equals(*j))
440 retVal = false;
441 break;
446 return retVal;
449 ::rtl::OUString DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()
451 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "documentsignatures.xml" ) );
454 ::rtl::OUString DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()
456 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "macrosignatures.xml" ) );
459 ::rtl::OUString DocumentSignatureHelper::GetPackageSignatureDefaultStreamName()
461 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "packagesignatures.xml" ) );