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

Generated by: LCOV version 1.10