LCOV - code coverage report
Current view: top level - xmlsecurity/source/helper - documentsignaturehelper.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 16 180 8.9 %
Date: 2015-06-13 12:38:46 Functions: 2 13 15.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.11