1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER( update_precomp.py ): autogen include statement, do not remove
25 #include "precompiled_package.hxx"
26 #include <ManifestImport.hxx>
27 #include <ManifestDefines.hxx>
28 #include <Base64Codec.hxx>
29 
30 #include <com/sun/star/xml/sax/XAttributeList.hpp>
31 #include <com/sun/star/xml/crypto/DigestID.hpp>
32 #include <com/sun/star/xml/crypto/CipherID.hpp>
33 
34 using namespace com::sun::star::uno;
35 using namespace com::sun::star::beans;
36 using namespace com::sun::star;
37 using namespace rtl;
38 using namespace std;
39 
40 // helper for ignoring multiple settings of the same property
41 #define setProperty(e,v) do{ if(!maValues[e].hasValue()) maValues[e] <<= v;} while(0)
42 
43 static const char* getMnfstPropName( int nManifestPropId )
44 {
45 	const char* pName;
46 	switch( nManifestPropId )
47 	{
48 		case PKG_MNFST_MEDIATYPE:	pName = "MediaType"; break;
49 		case PKG_MNFST_VERSION:		pName = "Version"; break;
50 		case PKG_MNFST_FULLPATH:	pName = "FullPath"; break;
51 		case PKG_MNFST_INIVECTOR:	pName = "InitialisationVector"; break;
52 		case PKG_MNFST_SALT:		pName = "Salt"; break;
53 		case PKG_MNFST_ITERATION:	pName = "IterationCount"; break;
54 		case PKG_MNFST_UCOMPSIZE:	pName = "Size"; break;
55 		case PKG_MNFST_DIGEST:		pName = "Digest"; break;
56 		case PKG_MNFST_ENCALG:		pName = "EncryptionAlgorithm"; break;
57 		case PKG_MNFST_STARTALG:	pName = "StartKeyAlgorithm"; break;
58 		case PKG_MNFST_DIGESTALG:	pName = "DigestAlgorithm"; break;
59 		case PKG_MNFST_DERKEYSIZE:	pName = "DerivedKeySize"; break;
60 		default: pName = NULL;
61 	}
62 	return pName;
63 }
64 
65 // ---------------------------------------------------
66 ManifestImport::ManifestImport( vector < Sequence < PropertyValue > > & rNewManVector )
67 : rManVector ( rNewManVector )
68 , nDerivedKeySize( 0 )
69 , bIgnoreEncryptData( false )
70 
71 , sCdataAttribute     			( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_CDATA ) )
72 , sMediaTypeAttribute 			( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_MEDIA_TYPE ) )
73 , sVersionAttribute 			( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_VERSION ) )
74 , sFullPathAttribute  			( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_FULL_PATH ) )
75 , sSizeAttribute 				( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_SIZE ) )
76 , sSaltAttribute 				( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_SALT ) )
77 , sInitialisationVectorAttribute(RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_INITIALISATION_VECTOR ) )
78 , sIterationCountAttribute 		( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_ITERATION_COUNT ) )
79 , sKeySizeAttribute            ( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_KEY_SIZE ) )
80 , sAlgorithmNameAttribute 		( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_ALGORITHM_NAME ) )
81 , sStartKeyAlgNameAttribute    ( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_START_KEY_GENERATION_NAME ) )
82 , sKeyDerivationNameAttribute 	( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_KEY_DERIVATION_NAME ) )
83 , sChecksumAttribute 			( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_CHECKSUM ) )
84 , sChecksumTypeAttribute 		( RTL_CONSTASCII_USTRINGPARAM ( ATTRIBUTE_CHECKSUM_TYPE ) )
85 {
86     aStack.reserve( 10 );
87 }
88 
89 // ---------------------------------------------------
90 ManifestImport::~ManifestImport ( void )
91 {
92 }
93 
94 // ---------------------------------------------------
95 void SAL_CALL ManifestImport::startDocument(  )
96 		throw( xml::sax::SAXException, uno::RuntimeException )
97 {
98 }
99 
100 // ---------------------------------------------------
101 void SAL_CALL ManifestImport::endDocument(  )
102 		throw( xml::sax::SAXException, uno::RuntimeException )
103 {
104 }
105 
106 // ---------------------------------------------------
107 void SAL_CALL ManifestImport::startElement( const OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs )
108 		throw( xml::sax::SAXException, uno::RuntimeException )
109 {
110     StringHashMap aConvertedAttribs;
111     ::rtl::OUString aConvertedName = PushNameAndNamespaces( aName, xAttribs, aConvertedAttribs );
112 
113 	if ( aConvertedName.equalsAscii( ELEMENT_FILE_ENTRY ) )
114 	{
115 		setProperty( PKG_MNFST_FULLPATH, aConvertedAttribs[sFullPathAttribute]);
116 		setProperty( PKG_MNFST_MEDIATYPE, aConvertedAttribs[sMediaTypeAttribute]);
117 
118 		const OUString& sVersion = aConvertedAttribs[sVersionAttribute];
119         if ( sVersion.getLength() )
120        		setProperty( PKG_MNFST_VERSION, sVersion );
121 
122 		const OUString& sSize = aConvertedAttribs[sSizeAttribute];
123 		if ( sSize.getLength() )
124        		setProperty( PKG_MNFST_UCOMPSIZE, sSize.toInt32() );
125 	}
126 	else if ( aStack.size() > 1 )
127 	{
128         ManifestStack::reverse_iterator aIter = aStack.rbegin();
129         aIter++;
130 
131 		if ( aIter->m_aConvertedName.equalsAscii( ELEMENT_FILE_ENTRY ) )
132         {
133             if ( aConvertedName.equalsAscii( ELEMENT_ENCRYPTION_DATA ) )
134             {
135                 // If this element exists, then this stream is encrypted and we need
136                 // to import the initialisation vector, salt and iteration count used
137                 nDerivedKeySize = 0;
138                 if ( !bIgnoreEncryptData )
139                 {
140                 	long nDigestId = 0;
141                     const OUString& rChecksumType = aConvertedAttribs[sChecksumTypeAttribute];
142                     if( rChecksumType.equalsAscii( SHA1_1K_NAME )
143                     ||  rChecksumType.equalsAscii( SHA1_1K_URL ) )
144        		            nDigestId = xml::crypto::DigestID::SHA1_1K;
145                     else if ( rChecksumType.equalsAscii( SHA256_1K_URL ) )
146        		            nDigestId = xml::crypto::DigestID::SHA256_1K;
147                     else
148                         bIgnoreEncryptData = true;
149 
150                     if ( !bIgnoreEncryptData )
151                     {
152                         setProperty( PKG_MNFST_DIGESTALG, nDigestId );
153                         const OUString& sChecksumData = aConvertedAttribs[sChecksumAttribute];
154                         uno::Sequence < sal_Int8 > aDecodeBuffer;
155                         Base64Codec::decodeBase64( aDecodeBuffer, sChecksumData );
156                         setProperty( PKG_MNFST_DIGEST, aDecodeBuffer );
157                     }
158                 }
159             }
160         }
161 		else if ( aIter->m_aConvertedName.equalsAscii( ELEMENT_ALGORITHM ) )
162         {
163             if ( aConvertedName.equalsAscii( ELEMENT_ALGORITHM ) )
164             {
165                 if ( !bIgnoreEncryptData )
166                 {
167                     long nCypherId = 0;
168                     const OUString& rAlgoName = aConvertedAttribs[sAlgorithmNameAttribute];
169                     if ( rAlgoName.equalsAscii( BLOWFISH_NAME )
170                     ||   rAlgoName.equalsAscii( BLOWFISH_URL ) )
171                     	 nCypherId = xml::crypto::CipherID::BLOWFISH_CFB_8;
172                     else if( rAlgoName.equalsAscii( AES256_URL ) )
173                     {
174                     	 nCypherId = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
175                         OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 32, "Unexpected derived key length!" );
176                         nDerivedKeySize = 32;
177                     }
178                     else if( rAlgoName.equalsAscii( AES192_URL ) )
179                     {
180                     	 nCypherId = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
181                         OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 24, "Unexpected derived key length!" );
182                         nDerivedKeySize = 24;
183                     }
184                     else if( rAlgoName.equalsAscii( AES128_URL ) )
185                     {
186                     	 nCypherId = xml::crypto::CipherID::AES_CBC_W3C_PADDING;
187                         OSL_ENSURE( !nDerivedKeySize || nDerivedKeySize == 16, "Unexpected derived key length!" );
188                         nDerivedKeySize = 16;
189                     }
190                     else
191                         bIgnoreEncryptData = true;
192 
193                     if ( !bIgnoreEncryptData )
194                     {
195                     	 setProperty( PKG_MNFST_ENCALG, nCypherId );
196                         const OUString& sInitVector = aConvertedAttribs[sInitialisationVectorAttribute];
197                         uno::Sequence < sal_Int8 > aDecodeBuffer;
198                         Base64Codec::decodeBase64 ( aDecodeBuffer, sInitVector );
199                     	 setProperty( PKG_MNFST_INIVECTOR, aDecodeBuffer );
200                     }
201                 }
202             }
203             else if ( aConvertedName.equalsAscii( ELEMENT_KEY_DERIVATION ) )
204             {
205                 if ( !bIgnoreEncryptData )
206                 {
207                     const OUString& rKeyDerivString = aConvertedAttribs[sKeyDerivationNameAttribute];
208                     if ( rKeyDerivString.equalsAscii( PBKDF2_NAME ) || rKeyDerivString.equalsAscii( PBKDF2_URL ) )
209                     {
210                         const OUString& rSaltString = aConvertedAttribs[sSaltAttribute];
211                         uno::Sequence < sal_Int8 > aDecodeBuffer;
212                         Base64Codec::decodeBase64 ( aDecodeBuffer, rSaltString );
213                     	 setProperty( PKG_MNFST_SALT, aDecodeBuffer );
214 
215                         const OUString& rIterationCount = aConvertedAttribs[sIterationCountAttribute];
216                         setProperty( PKG_MNFST_ITERATION, rIterationCount.toInt32() );
217 
218                         const OUString& rKeySize = aConvertedAttribs[sKeySizeAttribute];
219                         if ( rKeySize.getLength() )
220                         {
221                             const sal_Int32 nKey = rKeySize.toInt32();
222                             OSL_ENSURE( !nDerivedKeySize || nKey == nDerivedKeySize , "Provided derived key length differs from the expected one!" );
223                             nDerivedKeySize = nKey;
224                         }
225                         else if ( !nDerivedKeySize )
226                             nDerivedKeySize = 16;
227                         else if ( nDerivedKeySize != 16 )
228                             OSL_ENSURE( sal_False, "Default derived key length differs from the expected one!" );
229 
230                         setProperty( PKG_MNFST_DERKEYSIZE, nDerivedKeySize );
231                     }
232                     else
233                         bIgnoreEncryptData = true;
234                 }
235             }
236             else if ( aConvertedName.equalsAscii( ELEMENT_START_KEY_GENERATION ) )
237             {
238                 const OUString& rSKeyAlg = aConvertedAttribs[sStartKeyAlgNameAttribute];
239                 if ( rSKeyAlg.equalsAscii( SHA256_URL ) )
240                 	setProperty( PKG_MNFST_STARTALG, xml::crypto::DigestID::SHA256 );
241                 else if ( rSKeyAlg.equalsAscii( SHA1_NAME ) || rSKeyAlg.equalsAscii( SHA1_URL ) )
242                 	setProperty( PKG_MNFST_STARTALG, xml::crypto::DigestID::SHA1 );
243                 else
244                     bIgnoreEncryptData = true;
245             }
246         }
247 	}
248 }
249 
250 // ---------------------------------------------------
251 void SAL_CALL ManifestImport::endElement( const OUString& aName )
252 	throw( xml::sax::SAXException, uno::RuntimeException )
253 {
254 	if( aStack.empty() )
255 		return;
256 
257 	const OUString aConvertedName = ConvertName( aName );
258 	if( !aConvertedName.equalsAscii( ELEMENT_FILE_ENTRY ) )
259 		return;
260 	if( !aStack.rbegin()->m_aConvertedName.equals( aConvertedName ) )
261 		return;
262 
263 	aStack.pop_back();
264 
265 	// create the property sequence
266 	// Put full-path property first for MBA
267 	// TODO: get rid of fullpath-first requirement
268 	const bool bHasFullPath = maValues[PKG_MNFST_FULLPATH].hasValue();
269 	OSL_ENSURE( bHasFullPath, "Full path missing in manifest" );
270 
271 	int nNumProperty = bHasFullPath ? 1 : 0;
272 	PropertyValue aProperties[ PKG_SIZE_ENCR_MNFST ];
273 	for( int i = 0; i < PKG_SIZE_ENCR_MNFST; ++i)
274 	{
275 		if(! maValues[i].hasValue() )
276 			continue;
277 
278 		const int nDest = (i == PKG_MNFST_FULLPATH) ? 0 : nNumProperty++;
279 		PropertyValue& rProp = aProperties[ nDest ];
280 		rProp.Name = OUString::createFromAscii( getMnfstPropName(i));
281 		rProp.Value = maValues[i];
282 		maValues[i].clear();
283 	}
284 
285 	// add the property sequence to the vector of manifests
286 	rManVector.push_back ( PropertyValues( aProperties, nNumProperty ) );
287 	bIgnoreEncryptData = false;
288 }
289 
290 // ---------------------------------------------------
291 void SAL_CALL ManifestImport::characters( const OUString& /*aChars*/ )
292 		throw( xml::sax::SAXException, uno::RuntimeException )
293 {
294 }
295 
296 // ---------------------------------------------------
297 void SAL_CALL ManifestImport::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
298 		throw( xml::sax::SAXException, uno::RuntimeException )
299 {
300 }
301 
302 // ---------------------------------------------------
303 void SAL_CALL ManifestImport::processingInstruction( const OUString& /*aTarget*/, const OUString& /*aData*/ )
304 		throw( xml::sax::SAXException, uno::RuntimeException )
305 {
306 }
307 
308 // ---------------------------------------------------
309 void SAL_CALL ManifestImport::setDocumentLocator( const uno::Reference< xml::sax::XLocator >& /*xLocator*/ )
310 		throw( xml::sax::SAXException, uno::RuntimeException )
311 {
312 }
313 
314 // ---------------------------------------------------
315 ::rtl::OUString ManifestImport::PushNameAndNamespaces( const ::rtl::OUString& aName, const uno::Reference< xml::sax::XAttributeList >& xAttribs, StringHashMap& o_aConvertedAttribs )
316 {
317     StringHashMap aNamespaces;
318     ::std::vector< ::std::pair< ::rtl::OUString, ::rtl::OUString > > aAttribsStrs;
319 
320     if ( xAttribs.is() )
321     {
322 	    sal_Int16 nAttrCount = xAttribs.is() ? xAttribs->getLength() : 0;
323         aAttribsStrs.reserve( nAttrCount );
324 
325 	    for( sal_Int16 nInd = 0; nInd < nAttrCount; nInd++ )
326 	    {
327 		    ::rtl::OUString aAttrName = xAttribs->getNameByIndex( nInd );
328             ::rtl::OUString aAttrValue = xAttribs->getValueByIndex( nInd );
329             if ( aAttrName.getLength() >= 5
330               && aAttrName.compareToAscii( "xmlns", 5 ) == 0
331               && ( aAttrName.getLength() == 5 || aAttrName.getStr()[5] == ( sal_Unicode )':' ) )
332             {
333                 // this is a namespace declaration
334                 ::rtl::OUString aNsName( ( aAttrName.getLength() == 5 ) ? ::rtl::OUString() : aAttrName.copy( 6 ) );
335                 aNamespaces[aNsName] = aAttrValue;
336             }
337             else
338             {
339                 // this is no namespace declaration
340                 aAttribsStrs.push_back( pair< ::rtl::OUString, ::rtl::OUString >( aAttrName, aAttrValue ) );
341             }
342         }
343     }
344 
345     ::rtl::OUString aConvertedName = ConvertNameWithNamespace( aName, aNamespaces );
346     if ( !aConvertedName.getLength() )
347         aConvertedName = ConvertName( aName );
348 
349     aStack.push_back( ManifestScopeEntry( aConvertedName, aNamespaces ) );
350 
351     for ( sal_uInt16 nInd = 0; nInd < aAttribsStrs.size(); nInd++ )
352     {
353         // convert the attribute names on filling
354         o_aConvertedAttribs[ConvertName( aAttribsStrs[nInd].first )] = aAttribsStrs[nInd].second;
355     }
356 
357     return aConvertedName;
358 }
359 
360 
361 // ---------------------------------------------------
362 ::rtl::OUString ManifestImport::ConvertNameWithNamespace( const ::rtl::OUString& aName, const StringHashMap& aNamespaces )
363 {
364     ::rtl::OUString aNsAlias;
365     ::rtl::OUString aPureName = aName;
366 
367     sal_Int32 nInd = aName.indexOf( ( sal_Unicode )':' );
368     if ( nInd != -1 && nInd < aName.getLength() )
369     {
370         aNsAlias = aName.copy( 0, nInd );
371         aPureName = aName.copy( nInd + 1 );
372     }
373 
374     ::rtl::OUString aResult;
375 
376     StringHashMap::const_iterator aIter = aNamespaces.find( aNsAlias );
377     if ( aIter != aNamespaces.end()
378       && ( aIter->second.equalsAscii( MANIFEST_NAMESPACE )
379         || aIter->second.equalsAscii( MANIFEST_OASIS_NAMESPACE ) ) )
380     {
381         // no check for manifest.xml consistency currently since the old versions have supported inconsistent documents as well
382         aResult = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( MANIFEST_NSPREFIX ) );
383         aResult += aPureName;
384     }
385 
386     return aResult;
387 }
388 
389 // ---------------------------------------------------
390 ::rtl::OUString ManifestImport::ConvertName( const ::rtl::OUString& aName )
391 {
392     ::rtl::OUString aConvertedName;
393     for ( ManifestStack::reverse_iterator aIter = aStack.rbegin(); !aConvertedName.getLength() && aIter != aStack.rend(); aIter++ )
394     {
395         if ( !aIter->m_aNamespaces.empty() )
396             aConvertedName = ConvertNameWithNamespace( aName, aIter->m_aNamespaces );
397     }
398 
399     if ( !aConvertedName.getLength() )
400         aConvertedName = aName;
401 
402     return aConvertedName;
403 }
404 
405