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

Generated by: LCOV version 1.10