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
;
52 ::rtl::OUString
getElement(::rtl::OUString
const & version
, ::sal_Int32
* index
)
54 while (*index
< version
.getLength() && version
[*index
] == '0') {
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
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()) {
72 } else if (e1
.getLength() > e2
.getLength()) {
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
))
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()))
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)
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
))
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
))
180 mode
= OOo3_0Document
;
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
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( "/" ) );
211 case SignatureModeDocumentContent
:
213 if (mode
== OOo2Document
) //that is, ODF 1.0, 1.1
216 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), false, mode
);
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...
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
);
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...
259 // Everything except META-INF
260 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), true, mode
);
264 case SignatureModeMacros
:
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...
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...
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...
302 case SignatureModePackage
:
304 // Everything except META-INF
305 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), true, mode
);
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();
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..." );
348 //sElementList contains all files which are expected to be signed. Only those files must me signed,
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
)
386 sElementListURI
, rtl_UriCharClassPchar
,
387 rtl_UriEncodeCheckEscapes
, RTL_TEXTENCODING_UTF8
);
389 if (sElementListURI
.equals(sReferenceURI
))
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
)
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
);
418 std::vector
<OUString
> vPathSegments
;
422 OUString aToken
= rPath
.getToken( 0, '/', nIndex
);
423 vPathSegments
.push_back(aToken
);
427 //Now compare each segment of the uri with its counterpart from the path
428 if (vUriSegments
.size() == vPathSegments
.size())
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
))
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" ) );