1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 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. 14 * 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). 20 * 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. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_xmlsecurity.hxx" 30 31 #include <xmlsecurity/documentsignaturehelper.hxx> 32 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" 39 40 #include "comphelper/documentconstants.hxx" 41 #include <tools/debug.hxx> 42 #include "rtl/uri.hxx" 43 44 using namespace ::com::sun::star::uno; 45 //using namespace ::com::sun::star; 46 namespace css = ::com::sun::star; 47 using rtl::OUString; 48 49 50 namespace 51 { 52 ::rtl::OUString getElement(::rtl::OUString const & version, ::sal_Int32 * index) 53 { 54 while (*index < version.getLength() && version[*index] == '0') { 55 ++*index; 56 } 57 return version.getToken(0, '.', *index); 58 } 59 60 61 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) 66 { 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; 78 } 79 } 80 return 0; 81 } 82 } 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) 95 { 96 ::rtl::OUString aMetaInfName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) ); 97 ::rtl::OUString sMimeTypeName (RTL_CONSTASCII_USTRINGPARAM("mimetype")); 98 ::rtl::OUString aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) ); 99 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(); 104 105 for ( sal_Int32 n = 0; n < nElements; n++ ) 106 { 107 if (mode != OOo3_2Document 108 && (pNames[n] == aMetaInfName 109 || pNames[n] == sMimeTypeName)) 110 { 111 continue; 112 } 113 else 114 { 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); 121 122 if ( rxStore->isStreamElement( pNames[n] ) ) 123 { 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); 130 } 131 else if ( bRecursive && rxStore->isStorageElement( pNames[n] ) ) 132 { 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); 136 } 137 } 138 } 139 } 140 141 142 bool DocumentSignatureHelper::isODFPre_1_2(const ::rtl::OUString & sVersion) 143 { 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; 150 } 151 152 bool DocumentSignatureHelper::isOOo3_2_Signature(const SignatureInformation & sigInfo) 153 { 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++) 159 { 160 if (i->ouURI.equals(sManifestURI)) 161 { 162 bOOo3_2 = true; 163 break; 164 } 165 } 166 return bOOo3_2; 167 } 168 169 DocumentSignatureAlgorithm 170 DocumentSignatureHelper::getDocumentAlgorithm( 171 const ::rtl::OUString & sODFVersion, const SignatureInformation & sigInfo) 172 { 173 OSL_ASSERT(sODFVersion.getLength()); 174 DocumentSignatureAlgorithm mode = OOo3_2Document; 175 if (!isOOo3_2_Signature(sigInfo)) 176 { 177 if (isODFPre_1_2(sODFVersion)) 178 mode = OOo2Document; 179 else 180 mode = OOo3_0Document; 181 } 182 return mode; 183 } 184 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. 188 // 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. 197 // 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) 205 { 206 std::vector< rtl::OUString > aElements; 207 ::rtl::OUString aSep( RTL_CONSTASCII_USTRINGPARAM( "/" ) ); 208 209 switch ( eMode ) 210 { 211 case SignatureModeDocumentContent: 212 { 213 if (mode == OOo2Document) //that is, ODF 1.0, 1.1 214 { 215 // 1) Main content 216 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), false, mode); 217 218 // 2) Pictures... 219 rtl::OUString aSubStorageName( rtl::OUString::createFromAscii( "Pictures" ) ); 220 try 221 { 222 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ ); 223 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode); 224 } 225 catch(css::io::IOException& ) 226 { 227 ; // Doesn't have to exist... 228 } 229 // 3) OLE.... 230 aSubStorageName = rtl::OUString::createFromAscii( "ObjectReplacements" ); 231 try 232 { 233 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ ); 234 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode); 235 xSubStore.clear(); 236 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++ ) 244 { 245 if ( ( pNames[n].match( aMatchStr ) ) && rxStore->isStorageElement( pNames[n] ) ) 246 { 247 Reference < css::embed::XStorage > xTmpSubStore = rxStore->openStorageElement( pNames[n], css::embed::ElementModes::READ ); 248 ImplFillElementList(aElements, xTmpSubStore, pNames[n]+aSep, true, mode); 249 } 250 } 251 } 252 catch( com::sun::star::io::IOException& ) 253 { 254 ; // Doesn't have to exist... 255 } 256 } 257 else 258 { 259 // Everything except META-INF 260 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), true, mode); 261 } 262 } 263 break; 264 case SignatureModeMacros: 265 { 266 // 1) Macros 267 rtl::OUString aSubStorageName( rtl::OUString::createFromAscii( "Basic" ) ); 268 try 269 { 270 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ ); 271 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode); 272 } 273 catch( com::sun::star::io::IOException& ) 274 { 275 ; // Doesn't have to exist... 276 } 277 278 // 2) Dialogs 279 aSubStorageName = rtl::OUString::createFromAscii( "Dialogs") ; 280 try 281 { 282 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ ); 283 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode); 284 } 285 catch( com::sun::star::io::IOException& ) 286 { 287 ; // Doesn't have to exist... 288 } 289 // 3) Scripts 290 aSubStorageName = rtl::OUString::createFromAscii( "Scripts") ; 291 try 292 { 293 Reference < css::embed::XStorage > xSubStore = rxStore->openStorageElement( aSubStorageName, css::embed::ElementModes::READ ); 294 ImplFillElementList(aElements, xSubStore, aSubStorageName+aSep, true, mode); 295 } 296 catch( css::io::IOException& ) 297 { 298 ; // Doesn't have to exist... 299 } 300 } 301 break; 302 case SignatureModePackage: 303 { 304 // Everything except META-INF 305 ImplFillElementList(aElements, rxStore, ::rtl::OUString(), true, mode); 306 } 307 break; 308 } 309 310 return aElements; 311 } 312 313 SignatureStreamHelper DocumentSignatureHelper::OpenSignatureStream( 314 const Reference < css::embed::XStorage >& rxStore, sal_Int32 nOpenMode, DocumentSignatureMode eDocSigMode ) 315 { 316 sal_Int32 nSubStorageOpenMode = css::embed::ElementModes::READ; 317 if ( nOpenMode & css::embed::ElementModes::WRITE ) 318 nSubStorageOpenMode = css::embed::ElementModes::WRITE; 319 320 SignatureStreamHelper aHelper; 321 322 try 323 { 324 ::rtl::OUString aSIGStoreName( RTL_CONSTASCII_USTRINGPARAM( "META-INF" ) ); 325 aHelper.xSignatureStorage = rxStore->openStorageElement( aSIGStoreName, nSubStorageOpenMode ); 326 if ( aHelper.xSignatureStorage.is() ) 327 { 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(); 335 336 aHelper.xSignatureStream = aHelper.xSignatureStorage->openStreamElement( aSIGStreamName, nOpenMode ); 337 } 338 } 339 catch(css::io::IOException& ) 340 { 341 // Doesn't have to exist... 342 DBG_ASSERT( nOpenMode == css::embed::ElementModes::READ, "Error creating signature stream..." ); 343 } 344 345 return aHelper; 346 } 347 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) 357 { 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; ) 361 { 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 ) ) 365 { 366 ::rtl::OUString sReferenceURI = rInf.ouURI; 367 if (alg == OOo2Document) 368 { 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); 375 } 376 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++) 380 { 381 ::rtl::OUString sElementListURI = *aIter; 382 if (alg == OOo2Document) 383 { 384 sElementListURI = 385 ::rtl::Uri::encode( 386 sElementListURI, rtl_UriCharClassPchar, 387 rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8); 388 } 389 if (sElementListURI.equals(sReferenceURI)) 390 { 391 nRealCount++; 392 break; 393 } 394 } 395 } 396 } 397 return sElementList.size() == nRealCount; 398 } 399 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. 403 */ 404 bool DocumentSignatureHelper::equalsReferenceUriManifestPath( 405 const OUString & rUri, const OUString & rPath) 406 { 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; 411 do 412 { 413 OUString aToken = rUri.getToken( 0, '/', nIndex ); 414 vUriSegments.push_back(aToken); 415 } 416 while (nIndex >= 0); 417 418 std::vector<OUString> vPathSegments; 419 nIndex = 0; 420 do 421 { 422 OUString aToken = rPath.getToken( 0, '/', nIndex ); 423 vPathSegments.push_back(aToken); 424 } 425 while (nIndex >= 0); 426 427 //Now compare each segment of the uri with its counterpart from the path 428 if (vUriSegments.size() == vPathSegments.size()) 429 { 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++) 434 { 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)) 439 { 440 retVal = false; 441 break; 442 } 443 } 444 } 445 446 return retVal; 447 } 448 449 ::rtl::OUString DocumentSignatureHelper::GetDocumentContentSignatureDefaultStreamName() 450 { 451 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "documentsignatures.xml" ) ); 452 } 453 454 ::rtl::OUString DocumentSignatureHelper::GetScriptingContentSignatureDefaultStreamName() 455 { 456 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "macrosignatures.xml" ) ); 457 } 458 459 ::rtl::OUString DocumentSignatureHelper::GetPackageSignatureDefaultStreamName() 460 { 461 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "packagesignatures.xml" ) ); 462 } 463