1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: documentsignaturehelper.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_xmlsecurity.hxx"
34 #include <xmlsecurity/documentsignaturehelper.hxx>
36 #include <com/sun/star/container/XNameAccess.hpp>
37 #include <com/sun/star/lang/XComponent.hpp>
38 #include <com/sun/star/lang/DisposedException.hpp>
39 #include <com/sun/star/embed/XStorage.hpp>
40 #include <com/sun/star/embed/ElementModes.hpp>
41 #include "com/sun/star/beans/XPropertySet.hpp"
43 #include "comphelper/documentconstants.hxx"
44 #include <tools/debug.hxx>
45 #include "rtl/uri.hxx"
47 using namespace ::com::sun::star::uno
;
48 //using namespace ::com::sun::star;
49 namespace css
= ::com::sun::star
;
55 ::rtl::OUString
getElement(::rtl::OUString
const & version
, ::sal_Int32
* index
)
57 while (*index
< version
.getLength() && version
[*index
] == '0') {
60 return version
.getToken(0, '.', *index
);
65 // Return 1 if version1 is greater then version 2, 0 if they are equal
66 //and -1 if version1 is less version 2
68 ::rtl::OUString
const & version1
, ::rtl::OUString
const & version2
)
70 for (::sal_Int32 i1
= 0, i2
= 0; i1
>= 0 || i2
>= 0;) {
71 ::rtl::OUString
e1(getElement(version1
, &i1
));
72 ::rtl::OUString
e2(getElement(version2
, &i2
));
73 if (e1
.getLength() < e2
.getLength()) {
75 } else if (e1
.getLength() > e2
.getLength()) {
86 //If the OOo 3.0 mode is used then we exclude
87 //'mimetype' and all content of 'META-INF'.
88 //If the argument 'bSigning' is true then the element list is created for a signing
89 //operation in which case we use the latest signing algorithm. That is all elements
90 //we find in the zip storage are added to the list. We do not support the old signatures
91 //which did not contain all files.
92 //If 'bSigning' is false, then we validate. If the user enabled validating according to OOo 3.0
93 //then mimetype and all content of META-INF must be excluded.
94 void ImplFillElementList(
95 std::vector
< rtl::OUString
>& rList
, const Reference
< css::embed::XStorage
>& rxStore
,
96 const ::rtl::OUString rRootStorageName
, const bool bRecursive
,
97 const DocumentSignatureAlgorithm mode
)
99 ::rtl::OUString
aMetaInfName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) );
100 ::rtl::OUString
sMimeTypeName (RTL_CONSTASCII_USTRINGPARAM("mimetype"));
101 ::rtl::OUString
aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) );
103 Reference
< css::container::XNameAccess
> xElements( rxStore
, UNO_QUERY
);
104 Sequence
< ::rtl::OUString
> aElements
= xElements
->getElementNames();
105 sal_Int32 nElements
= aElements
.getLength();
106 const ::rtl::OUString
* pNames
= aElements
.getConstArray();
108 for ( sal_Int32 n
= 0; n
< nElements
; n
++ )
110 if (mode
!= OOo3_2Document
111 && (pNames
[n
] == aMetaInfName
112 || pNames
[n
] == sMimeTypeName
))
118 ::rtl::OUString sEncName
= ::rtl::Uri::encode(
119 pNames
[n
], rtl_UriCharClassRelSegment
,
120 rtl_UriEncodeStrict
, RTL_TEXTENCODING_UTF8
);
121 if (sEncName
.getLength() == 0 && pNames
[n
].getLength() != 0)
122 throw css::uno::Exception(::rtl::OUString(
123 RTL_CONSTASCII_USTRINGPARAM("Failed to encode element name of XStorage")), 0);
125 if ( rxStore
->isStreamElement( pNames
[n
] ) )
127 //Exclude documentsignatures.xml!
128 if (pNames
[n
].equals(
129 DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()))
131 ::rtl::OUString
aFullName( rRootStorageName
+ sEncName
);
132 rList
.push_back(aFullName
);
134 else if ( bRecursive
&& rxStore
->isStorageElement( pNames
[n
] ) )
136 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( pNames
[n
], css::embed::ElementModes::READ
);
137 rtl::OUString
aFullRootName( rRootStorageName
+ sEncName
+ aSep
);
138 ImplFillElementList(rList
, xSubStore
, aFullRootName
, bRecursive
, mode
);
145 bool DocumentSignatureHelper::isODFPre_1_2(const ::rtl::OUString
& sVersion
)
147 //The property version exists only if the document is at least version 1.2
148 //That is, if the document has version 1.1 and sVersion is empty.
149 //The constant is defined in comphelper/documentconstants.hxx
150 if (compareVersions(sVersion
, ODFVER_012_TEXT
) == -1)
155 bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation
& sigInfo
)
157 ::rtl::OUString
sManifestURI(RTL_CONSTASCII_USTRINGPARAM("META-INF/manifest.xml"));
158 bool bOOo3_2
= false;
159 typedef ::std::vector
< SignatureReferenceInformation
>::const_iterator CIT
;
160 for (CIT i
= sigInfo
.vSignatureReferenceInfors
.begin();
161 i
< sigInfo
.vSignatureReferenceInfors
.end(); i
++)
163 if (i
->ouURI
.equals(sManifestURI
))
172 DocumentSignatureAlgorithm
173 DocumentSignatureHelper::getDocumentAlgorithm(
174 const ::rtl::OUString
& sODFVersion
, const SignatureInformation
& sigInfo
)
176 OSL_ASSERT(sODFVersion
.getLength());
177 DocumentSignatureAlgorithm mode
= OOo3_2Document
;
178 if (!isOOo3_2_Signature(sigInfo
))
180 if (isODFPre_1_2(sODFVersion
))
183 mode
= OOo3_0Document
;
188 //The function creates a list of files which are to be signed or for which
189 //the signature is to be validated. The strings are UTF8 encoded URIs which
190 //contain '/' as path separators.
192 //The algorithm how document signatures are created and validated has
193 //changed over time. The change affects only which files within the document
194 //are changed. Document signatures created by OOo 2.x only used particular files. Since
195 //OOo 3.0 everything except "mimetype" and "META-INF" are signed. As of OOo 3.2 everything
196 //except META-INF/documentsignatures.xml is signed.
197 //Signatures are validated according to the algorithm which was then used for validation.
198 //That is, when validating a signature which was created by OOo 3.0, then mimetype and
199 //META-INF are not used.
201 //When a signature is created then we always use the latest algorithm. That is, we use
203 std::vector
< rtl::OUString
>
204 DocumentSignatureHelper::CreateElementList(
205 const Reference
< css::embed::XStorage
>& rxStore
,
206 const ::rtl::OUString
/*rRootStorageName*/, DocumentSignatureMode eMode
,
207 const DocumentSignatureAlgorithm mode
)
209 std::vector
< rtl::OUString
> aElements
;
210 ::rtl::OUString
aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) );
214 case SignatureModeDocumentContent
:
216 if (mode
== OOo2Document
) //that is, ODF 1.0, 1.1
219 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), false, mode
);
222 rtl::OUString
aSubStorageName( rtl::OUString::createFromAscii( "Pictures" ) );
225 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( aSubStorageName
, css::embed::ElementModes::READ
);
226 ImplFillElementList(aElements
, xSubStore
, aSubStorageName
+aSep
, true, mode
);
228 catch(css::io::IOException
& )
230 ; // Doesn't have to exist...
233 aSubStorageName
= rtl::OUString::createFromAscii( "ObjectReplacements" );
236 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( aSubStorageName
, css::embed::ElementModes::READ
);
237 ImplFillElementList(aElements
, xSubStore
, aSubStorageName
+aSep
, true, mode
);
241 rtl::OUString
aMatchStr( rtl::OUString::createFromAscii( "Object " ) );
242 Reference
< css::container::XNameAccess
> xElements( rxStore
, UNO_QUERY
);
243 Sequence
< ::rtl::OUString
> aElementNames
= xElements
->getElementNames();
244 sal_Int32 nElements
= aElementNames
.getLength();
245 const ::rtl::OUString
* pNames
= aElementNames
.getConstArray();
246 for ( sal_Int32 n
= 0; n
< nElements
; n
++ )
248 if ( ( pNames
[n
].match( aMatchStr
) ) && rxStore
->isStorageElement( pNames
[n
] ) )
250 Reference
< css::embed::XStorage
> xTmpSubStore
= rxStore
->openStorageElement( pNames
[n
], css::embed::ElementModes::READ
);
251 ImplFillElementList(aElements
, xTmpSubStore
, pNames
[n
]+aSep
, true, mode
);
255 catch( com::sun::star::io::IOException
& )
257 ; // Doesn't have to exist...
262 // Everything except META-INF
263 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), true, mode
);
267 case SignatureModeMacros
:
270 rtl::OUString
aSubStorageName( rtl::OUString::createFromAscii( "Basic" ) );
273 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( aSubStorageName
, css::embed::ElementModes::READ
);
274 ImplFillElementList(aElements
, xSubStore
, aSubStorageName
+aSep
, true, mode
);
276 catch( com::sun::star::io::IOException
& )
278 ; // Doesn't have to exist...
282 aSubStorageName
= rtl::OUString::createFromAscii( "Dialogs") ;
285 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( aSubStorageName
, css::embed::ElementModes::READ
);
286 ImplFillElementList(aElements
, xSubStore
, aSubStorageName
+aSep
, true, mode
);
288 catch( com::sun::star::io::IOException
& )
290 ; // Doesn't have to exist...
293 aSubStorageName
= rtl::OUString::createFromAscii( "Scripts") ;
296 Reference
< css::embed::XStorage
> xSubStore
= rxStore
->openStorageElement( aSubStorageName
, css::embed::ElementModes::READ
);
297 ImplFillElementList(aElements
, xSubStore
, aSubStorageName
+aSep
, true, mode
);
299 catch( css::io::IOException
& )
301 ; // Doesn't have to exist...
305 case SignatureModePackage
:
307 // Everything except META-INF
308 ImplFillElementList(aElements
, rxStore
, ::rtl::OUString(), true, mode
);
316 SignatureStreamHelper
DocumentSignatureHelper::OpenSignatureStream(
317 const Reference
< css::embed::XStorage
>& rxStore
, sal_Int32 nOpenMode
, DocumentSignatureMode eDocSigMode
)
319 sal_Int32 nSubStorageOpenMode
= css::embed::ElementModes::READ
;
320 if ( nOpenMode
& css::embed::ElementModes::WRITE
)
321 nSubStorageOpenMode
= css::embed::ElementModes::WRITE
;
323 SignatureStreamHelper aHelper
;
327 ::rtl::OUString
aSIGStoreName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) );
328 aHelper
.xSignatureStorage
= rxStore
->openStorageElement( aSIGStoreName
, nSubStorageOpenMode
);
329 if ( aHelper
.xSignatureStorage
.is() )
331 ::rtl::OUString aSIGStreamName
;
332 if ( eDocSigMode
== SignatureModeDocumentContent
)
333 aSIGStreamName
= DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName();
334 else if ( eDocSigMode
== SignatureModeMacros
)
335 aSIGStreamName
= DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName();
337 aSIGStreamName
= DocumentSignatureHelper::GetPackageSignatureDefaultStreamName();
339 aHelper
.xSignatureStream
= aHelper
.xSignatureStorage
->openStreamElement( aSIGStreamName
, nOpenMode
);
342 catch(css::io::IOException
& )
344 // Doesn't have to exist...
345 DBG_ASSERT( nOpenMode
== css::embed::ElementModes::READ
, "Error creating signature stream..." );
351 //sElementList contains all files which are expected to be signed. Only those files must me signed,
353 //The DocumentSignatureAlgorithm indicates if the document was created with OOo 2.x. Then
354 //the uri s in the Reference elements in the signature, were not properly encoded.
355 // For example: <Reference URI="ObjectReplacements/Object 1">
356 bool DocumentSignatureHelper::checkIfAllFilesAreSigned(
357 const ::std::vector
< ::rtl::OUString
> & sElementList
,
358 const SignatureInformation
& sigInfo
,
359 const DocumentSignatureAlgorithm alg
)
361 // Can only be valid if ALL streams are signed, which means real stream count == signed stream count
362 unsigned int nRealCount
= 0;
363 for ( int i
= sigInfo
.vSignatureReferenceInfors
.size(); i
; )
365 const SignatureReferenceInformation
& rInf
= sigInfo
.vSignatureReferenceInfors
[--i
];
366 // There is also an extra entry of type TYPE_SAMEDOCUMENT_REFERENCE because of signature date.
367 if ( ( rInf
.nType
== TYPE_BINARYSTREAM_REFERENCE
) || ( rInf
.nType
== TYPE_XMLSTREAM_REFERENCE
) )
369 ::rtl::OUString sReferenceURI
= rInf
.ouURI
;
370 if (alg
== OOo2Document
)
372 //Comparing URIs is a difficult. Therefore we kind of normalize
373 //it before comparing. We assume that our URI do not have a leading "./"
374 //and fragments at the end (...#...)
375 sReferenceURI
= ::rtl::Uri::encode(
376 sReferenceURI
, rtl_UriCharClassPchar
,
377 rtl_UriEncodeCheckEscapes
, RTL_TEXTENCODING_UTF8
);
380 //find the file in the element list
381 typedef ::std::vector
< ::rtl::OUString
>::const_iterator CIT
;
382 for (CIT aIter
= sElementList
.begin(); aIter
< sElementList
.end(); aIter
++)
384 ::rtl::OUString sElementListURI
= *aIter
;
385 if (alg
== OOo2Document
)
389 sElementListURI
, rtl_UriCharClassPchar
,
390 rtl_UriEncodeCheckEscapes
, RTL_TEXTENCODING_UTF8
);
392 if (sElementListURI
.equals(sReferenceURI
))
400 return sElementList
.size() == nRealCount
;
403 /*Compares the Uri which are obtained from CreateElementList with
404 the path obtained from the manifest.xml.
405 Returns true if both strings are equal.
407 bool DocumentSignatureHelper::equalsReferenceUriManifestPath(
408 const OUString
& rUri
, const OUString
& rPath
)
411 //split up the uri and path into segments. Both are separated by '/'
412 std::vector
<OUString
> vUriSegments
;
413 sal_Int32 nIndex
= 0;
416 OUString aToken
= rUri
.getToken( 0, '/', nIndex
);
417 vUriSegments
.push_back(aToken
);
421 std::vector
<OUString
> vPathSegments
;
425 OUString aToken
= rPath
.getToken( 0, '/', nIndex
);
426 vPathSegments
.push_back(aToken
);
430 //Now compare each segment of the uri with its counterpart from the path
431 if (vUriSegments
.size() == vPathSegments
.size())
434 typedef std::vector
<OUString
>::const_iterator CIT
;
435 for (CIT i
= vUriSegments
.begin(), j
= vPathSegments
.begin();
436 i
!= vUriSegments
.end(); i
++, j
++)
438 //Decode the uri segment, so that %20 becomes ' ', etc.
439 OUString sDecUri
= ::rtl::Uri::decode(
440 *i
, rtl_UriDecodeWithCharset
, RTL_TEXTENCODING_UTF8
);
441 if (!sDecUri
.equals(*j
))
452 ::rtl::OUString
DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName()
454 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "documentsignatures.xml" ) );
457 ::rtl::OUString
DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName()
459 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "macrosignatures.xml" ) );
462 ::rtl::OUString
DocumentSignatureHelper::GetPackageSignatureDefaultStreamName()
464 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "packagesignatures.xml" ) );