xref: /aoo41x/main/oox/source/core/filterdetect.cxx (revision 74eb9fee)
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 #include "oox/core/filterdetect.hxx"
25 
26 #include <com/sun/star/io/XStream.hpp>
27 #include <comphelper/docpasswordhelper.hxx>
28 #include <comphelper/mediadescriptor.hxx>
29 #include <openssl/evp.h>
30 #include <rtl/digest.h>
31 #include "oox/core/fastparser.hxx"
32 #include "oox/core/relationshandler.hxx"
33 #include "oox/helper/attributelist.hxx"
34 #include "oox/helper/binaryinputstream.hxx"
35 #include "oox/helper/binaryoutputstream.hxx"
36 #include "oox/helper/zipstorage.hxx"
37 #include "oox/ole/olestorage.hxx"
38 
39 namespace oox {
40 namespace core {
41 
42 // ============================================================================
43 
44 using namespace ::com::sun::star::beans;
45 using namespace ::com::sun::star::io;
46 using namespace ::com::sun::star::lang;
47 using namespace ::com::sun::star::uno;
48 using namespace ::com::sun::star::xml::sax;
49 
50 using ::comphelper::MediaDescriptor;
51 using ::comphelper::SequenceAsHashMap;
52 using ::rtl::OUString;
53 
54 // ============================================================================
55 
FilterDetectDocHandler(OUString & rFilterName)56 FilterDetectDocHandler::FilterDetectDocHandler( OUString& rFilterName ) :
57     mrFilterName( rFilterName )
58 {
59     maContextStack.reserve( 2 );
60 }
61 
~FilterDetectDocHandler()62 FilterDetectDocHandler::~FilterDetectDocHandler()
63 {
64 }
65 
startDocument()66 void SAL_CALL FilterDetectDocHandler::startDocument()
67     throw (SAXException, RuntimeException)
68 {
69 }
70 
endDocument()71 void SAL_CALL FilterDetectDocHandler::endDocument()
72     throw (SAXException, RuntimeException)
73 {
74 }
75 
setDocumentLocator(const Reference<XLocator> &)76 void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
77     throw (SAXException, RuntimeException)
78 {
79 }
80 
startFastElement(sal_Int32 nElement,const Reference<XFastAttributeList> & rAttribs)81 void SAL_CALL FilterDetectDocHandler::startFastElement(
82         sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
83     throw (SAXException,RuntimeException)
84 {
85     AttributeList aAttribs( rAttribs );
86     switch ( nElement )
87     {
88         // cases for _rels/.rels
89         case PR_TOKEN( Relationships ):
90         break;
91         case PR_TOKEN( Relationship ):
92             if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
93                 parseRelationship( aAttribs );
94         break;
95 
96         // cases for [Content_Types].xml
97         case PC_TOKEN( Types ):
98         break;
99         case PC_TOKEN( Default ):
100             if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
101                 parseContentTypesDefault( aAttribs );
102         break;
103         case PC_TOKEN( Override ):
104             if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
105                 parseContentTypesOverride( aAttribs );
106         break;
107     }
108     maContextStack.push_back( nElement );
109 }
110 
startUnknownElement(const OUString &,const OUString &,const Reference<XFastAttributeList> &)111 void SAL_CALL FilterDetectDocHandler::startUnknownElement(
112     const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
113     throw (SAXException, RuntimeException)
114 {
115 }
116 
endFastElement(sal_Int32)117 void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
118     throw (SAXException, RuntimeException)
119 {
120     maContextStack.pop_back();
121 }
122 
endUnknownElement(const OUString &,const OUString &)123 void SAL_CALL FilterDetectDocHandler::endUnknownElement(
124     const OUString& /*Namespace*/, const OUString& /*Name*/ ) throw (SAXException, RuntimeException)
125 {
126 }
127 
createFastChildContext(sal_Int32,const Reference<XFastAttributeList> &)128 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
129     sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
130     throw (SAXException, RuntimeException)
131 {
132     return this;
133 }
134 
createUnknownChildContext(const OUString &,const OUString &,const Reference<XFastAttributeList> &)135 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
136     const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
137     throw (SAXException, RuntimeException)
138 {
139     return this;
140 }
141 
characters(const OUString &)142 void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
143     throw (SAXException, RuntimeException)
144 {
145 }
146 
ignorableWhitespace(const OUString &)147 void SAL_CALL FilterDetectDocHandler::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
148     throw (SAXException, RuntimeException)
149 {
150 }
151 
processingInstruction(const OUString &,const OUString &)152 void SAL_CALL FilterDetectDocHandler::processingInstruction(
153     const OUString& /*aTarget*/, const OUString& /*aData*/ )
154     throw (SAXException, RuntimeException)
155 {
156 }
157 
parseRelationship(const AttributeList & rAttribs)158 void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
159 {
160     OUString aType = rAttribs.getString( XML_Type, OUString() );
161     if( aType.equalsAscii( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) )
162         maTargetPath = RelationsFragment::removeDuplicateSlashes( OUString( sal_Unicode( '/' ) ) + rAttribs.getString( XML_Target, OUString() ) );
163 }
164 
getFilterNameFromContentType(const OUString & rContentType) const165 OUString FilterDetectDocHandler::getFilterNameFromContentType( const OUString& rContentType ) const
166 {
167     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" ) ||
168         rContentType.equalsAscii( "application/vnd.ms-word.document.macroEnabled.main+xml" ) )
169         return CREATE_OUSTRING( "writer_MS_Word_2007" );
170 
171     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ) ||
172         rContentType.equalsAscii( "application/vnd.ms-word.template.macroEnabledTemplate.main+xml" ) )
173         return CREATE_OUSTRING( "writer_MS_Word_2007_Template" );
174 
175     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ) ||
176         rContentType.equalsAscii( "application/vnd.ms-excel.sheet.macroEnabled.main+xml" ) )
177         return CREATE_OUSTRING( "MS Excel 2007 XML" );
178 
179     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ) ||
180         rContentType.equalsAscii( "application/vnd.ms-excel.template.macroEnabled.main+xml" ) )
181         return CREATE_OUSTRING( "MS Excel 2007 XML Template" );
182 
183     if( rContentType.equalsAscii( "application/vnd.ms-excel.sheet.binary.macroEnabled.main" ) )
184         return CREATE_OUSTRING( "MS Excel 2007 Binary" );
185 
186     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" ) ||
187         rContentType.equalsAscii( "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" ) )
188         return CREATE_OUSTRING( "MS PowerPoint 2007 XML" );
189 
190     if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ) ||
191         rContentType.equalsAscii( "application/vnd.ms-powerpoint.template.macroEnabled.main+xml" ) )
192         return CREATE_OUSTRING( "MS PowerPoint 2007 XML Template" );
193 
194     return OUString();
195 }
196 
parseContentTypesDefault(const AttributeList & rAttribs)197 void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
198 {
199     // only if no overridden part name found
200     if( mrFilterName.getLength() == 0 )
201     {
202         // check if target path ends with extension
203         OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
204         sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
205         if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
206             mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
207     }
208 }
209 
parseContentTypesOverride(const AttributeList & rAttribs)210 void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
211 {
212     if( rAttribs.getString( XML_PartName, OUString() ).equals( maTargetPath ) )
213         mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
214 }
215 
216 // ============================================================================
217 
218 /* Helper for XServiceInfo */
FilterDetect_getSupportedServiceNames()219 Sequence< OUString > FilterDetect_getSupportedServiceNames()
220 {
221     Sequence< OUString > aServiceNames( 1 );
222     aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" );
223     return aServiceNames;
224 }
225 
226 /* Helper for XServiceInfo */
FilterDetect_getImplementationName()227 OUString FilterDetect_getImplementationName()
228 {
229     return CREATE_OUSTRING( "com.sun.star.comp.oox.FormatDetector" );
230 }
231 
232 /* Helper for registry */
FilterDetect_createInstance(const Reference<XComponentContext> & rxContext)233 Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< XComponentContext >& rxContext ) throw( Exception )
234 {
235     return static_cast< ::cppu::OWeakObject* >( new FilterDetect( rxContext ) );
236 }
237 
238 // ----------------------------------------------------------------------------
239 
FilterDetect(const Reference<XComponentContext> & rxContext)240 FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) throw( RuntimeException ) :
241     mxContext( rxContext, UNO_SET_THROW )
242 {
243 }
244 
~FilterDetect()245 FilterDetect::~FilterDetect()
246 {
247 }
248 
249 /* =========================================================================== */
250 /*  Kudos to Caolan McNamara who provided the core decryption implementations. */
251 /* =========================================================================== */
252 
253 namespace {
254 
255 const sal_uInt32 ENCRYPTINFO_CRYPTOAPI      = 0x00000004;
256 const sal_uInt32 ENCRYPTINFO_DOCPROPS       = 0x00000008;
257 const sal_uInt32 ENCRYPTINFO_EXTERNAL       = 0x00000010;
258 const sal_uInt32 ENCRYPTINFO_AES            = 0x00000020;
259 
260 const sal_uInt32 ENCRYPT_ALGO_AES128        = 0x0000660E;
261 const sal_uInt32 ENCRYPT_ALGO_AES192        = 0x0000660F;
262 const sal_uInt32 ENCRYPT_ALGO_AES256        = 0x00006610;
263 const sal_uInt32 ENCRYPT_ALGO_RC4           = 0x00006801;
264 
265 const sal_uInt32 ENCRYPT_HASH_SHA1          = 0x00008004;
266 
267 // ----------------------------------------------------------------------------
268 
lclIsZipPackage(const Reference<XComponentContext> & rxContext,const Reference<XInputStream> & rxInStrm)269 bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
270 {
271     ZipStorage aZipStorage( rxContext, rxInStrm );
272     return aZipStorage.isStorage();
273 }
274 
275 // ----------------------------------------------------------------------------
276 
277 struct PackageEncryptionInfo
278 {
279     sal_uInt8           mpnSalt[ 16 ];
280     sal_uInt8           mpnEncrVerifier[ 16 ];
281     sal_uInt8           mpnEncrVerifierHash[ 32 ];
282     sal_uInt32          mnFlags;
283     sal_uInt32          mnAlgorithmId;
284     sal_uInt32          mnAlgorithmIdHash;
285     sal_uInt32          mnKeySize;
286     sal_uInt32          mnSaltSize;
287     sal_uInt32          mnVerifierHashSize;
288 };
289 
lclReadEncryptionInfo(PackageEncryptionInfo & rEncrInfo,BinaryInputStream & rStrm)290 bool lclReadEncryptionInfo( PackageEncryptionInfo& rEncrInfo, BinaryInputStream& rStrm )
291 {
292     rStrm.skip( 4 );
293     rStrm >> rEncrInfo.mnFlags;
294     if( getFlag( rEncrInfo.mnFlags, ENCRYPTINFO_EXTERNAL ) )
295         return false;
296 
297     sal_uInt32 nHeaderSize, nRepeatedFlags;
298     rStrm >> nHeaderSize >> nRepeatedFlags;
299     if( (nHeaderSize < 20) || (nRepeatedFlags != rEncrInfo.mnFlags) )
300         return false;
301 
302     rStrm.skip( 4 );
303     rStrm >> rEncrInfo.mnAlgorithmId >> rEncrInfo.mnAlgorithmIdHash >> rEncrInfo.mnKeySize;
304     rStrm.skip( nHeaderSize - 20 );
305     rStrm >> rEncrInfo.mnSaltSize;
306     if( rEncrInfo.mnSaltSize != 16 )
307         return false;
308 
309     rStrm.readMemory( rEncrInfo.mpnSalt, 16 );
310     rStrm.readMemory( rEncrInfo.mpnEncrVerifier, 16 );
311     rStrm >> rEncrInfo.mnVerifierHashSize;
312     rStrm.readMemory( rEncrInfo.mpnEncrVerifierHash, 32 );
313     return !rStrm.isEof();
314 }
315 
316 // ----------------------------------------------------------------------------
317 
lclDeriveKey(const sal_uInt8 * pnHash,sal_uInt32 nHashLen,sal_uInt8 * pnKeyDerived,sal_uInt32 nRequiredKeyLen)318 void lclDeriveKey( const sal_uInt8* pnHash, sal_uInt32 nHashLen, sal_uInt8* pnKeyDerived, sal_uInt32 nRequiredKeyLen )
319 {
320     sal_uInt8 pnBuffer[ 64 ];
321     memset( pnBuffer, 0x36, sizeof( pnBuffer ) );
322     for( sal_uInt32 i = 0; i < nHashLen; ++i )
323         pnBuffer[ i ] ^= pnHash[ i ];
324 
325     rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
326     rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
327     sal_uInt8 pnX1[ RTL_DIGEST_LENGTH_SHA1 ];
328     aError = rtl_digest_get( aDigest, pnX1, RTL_DIGEST_LENGTH_SHA1 );
329     rtl_digest_destroy( aDigest );
330 
331     memset( pnBuffer, 0x5C, sizeof( pnBuffer ) );
332     for( sal_uInt32 i = 0; i < nHashLen; ++i )
333         pnBuffer[ i ] ^= pnHash[ i ];
334 
335     aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
336     aError = rtl_digest_update( aDigest, pnBuffer, sizeof( pnBuffer ) );
337     sal_uInt8 pnX2[ RTL_DIGEST_LENGTH_SHA1 ];
338     aError = rtl_digest_get( aDigest, pnX2, RTL_DIGEST_LENGTH_SHA1 );
339     rtl_digest_destroy( aDigest );
340 
341     if( nRequiredKeyLen > RTL_DIGEST_LENGTH_SHA1 )
342     {
343         memcpy( pnKeyDerived + RTL_DIGEST_LENGTH_SHA1, pnX2, nRequiredKeyLen - RTL_DIGEST_LENGTH_SHA1 );
344         nRequiredKeyLen = RTL_DIGEST_LENGTH_SHA1;
345     }
346     memcpy( pnKeyDerived, pnX1, nRequiredKeyLen );
347 }
348 
349 // ----------------------------------------------------------------------------
350 
lclCheckEncryptionData(const sal_uInt8 * pnKey,sal_uInt32 nKeySize,const sal_uInt8 * pnVerifier,sal_uInt32 nVerifierSize,const sal_uInt8 * pnVerifierHash,sal_uInt32 nVerifierHashSize)351 bool lclCheckEncryptionData( const sal_uInt8* pnKey, sal_uInt32 nKeySize, const sal_uInt8* pnVerifier, sal_uInt32 nVerifierSize, const sal_uInt8* pnVerifierHash, sal_uInt32 nVerifierHashSize )
352 {
353     bool bResult = false;
354 
355     // the only currently supported algorithm needs key size 128
356     if ( nKeySize == 16 && nVerifierSize == 16 && nVerifierHashSize == 32 )
357     {
358         // check password
359         EVP_CIPHER_CTX aes_ctx;
360         EVP_CIPHER_CTX_init( &aes_ctx );
361         EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
362         EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
363         int nOutLen = 0;
364         sal_uInt8 pnTmpVerifier[ 16 ];
365         (void) memset( pnTmpVerifier, 0, sizeof(pnTmpVerifier) );
366 
367         /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifier, &nOutLen, pnVerifier, nVerifierSize );
368         EVP_CIPHER_CTX_cleanup( &aes_ctx );
369 
370         EVP_CIPHER_CTX_init( &aes_ctx );
371         EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, pnKey, 0 );
372         EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
373         sal_uInt8 pnTmpVerifierHash[ 32 ];
374         (void) memset( pnTmpVerifierHash, 0, sizeof(pnTmpVerifierHash) );
375 
376         /*int*/ EVP_DecryptUpdate( &aes_ctx, pnTmpVerifierHash, &nOutLen, pnVerifierHash, nVerifierHashSize );
377         EVP_CIPHER_CTX_cleanup( &aes_ctx );
378 
379         rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
380         rtlDigestError aError = rtl_digest_update( aDigest, pnTmpVerifier, sizeof( pnTmpVerifier ) );
381         sal_uInt8 pnSha1Hash[ RTL_DIGEST_LENGTH_SHA1 ];
382         aError = rtl_digest_get( aDigest, pnSha1Hash, RTL_DIGEST_LENGTH_SHA1 );
383         rtl_digest_destroy( aDigest );
384 
385         bResult = ( memcmp( pnSha1Hash, pnTmpVerifierHash, RTL_DIGEST_LENGTH_SHA1 ) == 0 );
386     }
387 
388     return bResult;
389 }
390 
391 // ----------------------------------------------------------------------------
392 
lclGenerateEncryptionKey(const PackageEncryptionInfo & rEncrInfo,const OUString & rPassword,sal_uInt8 * pnKey,sal_uInt32 nRequiredKeyLen)393 Sequence< NamedValue > lclGenerateEncryptionKey( const PackageEncryptionInfo& rEncrInfo, const OUString& rPassword, sal_uInt8* pnKey, sal_uInt32 nRequiredKeyLen )
394 {
395     size_t nBufferSize = rEncrInfo.mnSaltSize + 2 * rPassword.getLength();
396     sal_uInt8* pnBuffer = new sal_uInt8[ nBufferSize ];
397     memcpy( pnBuffer, rEncrInfo.mpnSalt, rEncrInfo.mnSaltSize );
398 
399     sal_uInt8* pnPasswordLoc = pnBuffer + rEncrInfo.mnSaltSize;
400     const sal_Unicode* pStr = rPassword.getStr();
401     for( sal_Int32 i = 0, nLen = rPassword.getLength(); i < nLen; ++i, ++pStr, pnPasswordLoc += 2 )
402         ByteOrderConverter::writeLittleEndian( pnPasswordLoc, static_cast< sal_uInt16 >( *pStr ) );
403 
404     rtlDigest aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
405     rtlDigestError aError = rtl_digest_update( aDigest, pnBuffer, nBufferSize );
406     delete[] pnBuffer;
407 
408     size_t nHashSize = RTL_DIGEST_LENGTH_SHA1 + 4;
409     sal_uInt8* pnHash = new sal_uInt8[ nHashSize ];
410     aError = rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
411     rtl_digest_destroy( aDigest );
412 
413     for( sal_uInt32 i = 0; i < 50000; ++i )
414     {
415         ByteOrderConverter::writeLittleEndian( pnHash, i );
416         aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
417         aError = rtl_digest_update( aDigest, pnHash, nHashSize );
418         aError = rtl_digest_get( aDigest, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
419         rtl_digest_destroy( aDigest );
420     }
421 
422     memmove( pnHash, pnHash + 4, RTL_DIGEST_LENGTH_SHA1 );
423     memset( pnHash + RTL_DIGEST_LENGTH_SHA1, 0, 4 );
424     aDigest = rtl_digest_create( rtl_Digest_AlgorithmSHA1 );
425     aError = rtl_digest_update( aDigest, pnHash, nHashSize );
426     aError = rtl_digest_get( aDigest, pnHash, RTL_DIGEST_LENGTH_SHA1 );
427     rtl_digest_destroy( aDigest );
428 
429     lclDeriveKey( pnHash, RTL_DIGEST_LENGTH_SHA1, pnKey, nRequiredKeyLen );
430     delete[] pnHash;
431 
432     Sequence< NamedValue > aResult;
433     if( lclCheckEncryptionData( pnKey, nRequiredKeyLen, rEncrInfo.mpnEncrVerifier, sizeof( rEncrInfo.mpnEncrVerifier ), rEncrInfo.mpnEncrVerifierHash, sizeof( rEncrInfo.mpnEncrVerifierHash ) ) )
434     {
435         SequenceAsHashMap aEncryptionData;
436         aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionKey" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( pnKey ), nRequiredKeyLen );
437         aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionSalt" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnSalt ), rEncrInfo.mnSaltSize );
438         aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifier" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifier ), sizeof( rEncrInfo.mpnEncrVerifier ) );
439         aEncryptionData[ CREATE_OUSTRING( "AES128EncryptionVerifierHash" ) ] <<= Sequence< sal_Int8 >( reinterpret_cast< const sal_Int8* >( rEncrInfo.mpnEncrVerifierHash ), sizeof( rEncrInfo.mpnEncrVerifierHash ) );
440         aResult = aEncryptionData.getAsConstNamedValueList();
441     }
442 
443     return aResult;
444 }
445 
446 // the password verifier ------------------------------------------------------
447 
448 class PasswordVerifier : public ::comphelper::IDocPasswordVerifier
449 {
450 public:
451     explicit            PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo );
452 
453     virtual ::comphelper::DocPasswordVerifierResult
454                         verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData );
455     virtual ::comphelper::DocPasswordVerifierResult
456                         verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData );
457 
getKey() const458     inline const sal_uInt8* getKey() const { return &maKey.front(); }
459 
460 private:
461     const PackageEncryptionInfo& mrEncryptInfo;
462     ::std::vector< sal_uInt8 > maKey;
463 };
464 
PasswordVerifier(const PackageEncryptionInfo & rEncryptInfo)465 PasswordVerifier::PasswordVerifier( const PackageEncryptionInfo& rEncryptInfo ) :
466     mrEncryptInfo( rEncryptInfo ),
467     maKey( static_cast< size_t >( rEncryptInfo.mnKeySize / 8 ), 0 )
468 {
469 }
470 
verifyPassword(const OUString & rPassword,Sequence<NamedValue> & o_rEncryptionData)471 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData )
472 {
473     // verifies the password and writes the related decryption key into maKey
474     o_rEncryptionData = lclGenerateEncryptionKey( mrEncryptInfo, rPassword, &maKey.front(), maKey.size() );
475     return o_rEncryptionData.hasElements() ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
476 }
477 
verifyEncryptionData(const Sequence<NamedValue> & rEncryptionData)478 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData )
479 {
480     SequenceAsHashMap aHashData( rEncryptionData );
481     Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionKey" ), Sequence< sal_Int8 >() );
482     Sequence< sal_Int8 > aVerifier = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifier" ), Sequence< sal_Int8 >() );
483     Sequence< sal_Int8 > aVerifierHash = aHashData.getUnpackedValueOrDefault( CREATE_OUSTRING( "AES128EncryptionVerifierHash" ), Sequence< sal_Int8 >() );
484 
485     bool bResult = lclCheckEncryptionData(
486         reinterpret_cast< const sal_uInt8* >( aKey.getConstArray() ), aKey.getLength(),
487         reinterpret_cast< const sal_uInt8* >( aVerifier.getConstArray() ), aVerifier.getLength(),
488         reinterpret_cast< const sal_uInt8* >( aVerifierHash.getConstArray() ), aVerifierHash.getLength() );
489 
490     return bResult ? ::comphelper::DocPasswordVerifierResult_OK : ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
491 }
492 
493 } // namespace
494 
495 // ----------------------------------------------------------------------------
496 
extractUnencryptedPackage(MediaDescriptor & rMediaDesc) const497 Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDesc ) const
498 {
499     // try the plain input stream
500     Reference< XInputStream > xInStrm( rMediaDesc[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY );
501     if( !xInStrm.is() || lclIsZipPackage( mxContext, xInStrm ) )
502         return xInStrm;
503 
504     // check if a temporary file is passed in the 'ComponentData' property
505     Reference< XStream > xDecrypted( rMediaDesc.getComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ) ), UNO_QUERY );
506     if( xDecrypted.is() )
507     {
508         Reference< XInputStream > xDecrInStrm = xDecrypted->getInputStream();
509         if( lclIsZipPackage( mxContext, xDecrInStrm ) )
510             return xDecrInStrm;
511     }
512 
513     // try to decrypt an encrypted OLE package
514     ::oox::ole::OleStorage aOleStorage( mxContext, xInStrm, false );
515     if( aOleStorage.isStorage() ) try
516     {
517         // open the required input streams in the encrypted package
518         Reference< XInputStream > xEncryptionInfo( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptionInfo" ) ), UNO_SET_THROW );
519         Reference< XInputStream > xEncryptedPackage( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptedPackage" ) ), UNO_SET_THROW );
520 
521         // read the encryption info stream
522         PackageEncryptionInfo aEncryptInfo;
523         BinaryXInputStream aInfoStrm( xEncryptionInfo, true );
524         bool bValidInfo = lclReadEncryptionInfo( aEncryptInfo, aInfoStrm );
525 
526         // check flags and agorithm IDs, requiered are AES128 and SHA-1
527         bool bImplemented = bValidInfo &&
528             getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_CRYPTOAPI ) &&
529             getFlag( aEncryptInfo.mnFlags, ENCRYPTINFO_AES ) &&
530             // algorithm ID 0 defaults to AES128 too, if ENCRYPTINFO_AES flag is set
531             ((aEncryptInfo.mnAlgorithmId == 0) || (aEncryptInfo.mnAlgorithmId == ENCRYPT_ALGO_AES128)) &&
532             // hash algorithm ID 0 defaults to SHA-1 too
533             ((aEncryptInfo.mnAlgorithmIdHash == 0) || (aEncryptInfo.mnAlgorithmIdHash == ENCRYPT_HASH_SHA1)) &&
534             (aEncryptInfo.mnVerifierHashSize == 20);
535 
536         if( bImplemented )
537         {
538             /*  "VelvetSweatshop" is the built-in default encryption
539                 password used by MS Excel for the "workbook protection"
540                 feature with password. Try this first before prompting the
541                 user for a password. */
542             ::std::vector< OUString > aDefaultPasswords;
543             aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
544 
545             /*  Use the comphelper password helper to request a password.
546                 This helper returns either with the correct password
547                 (according to the verifier), or with an empty string if
548                 user has cancelled the password input dialog. */
549             PasswordVerifier aVerifier( aEncryptInfo );
550             Sequence< NamedValue > aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
551                 aVerifier, rMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
552 
553             if( aEncryptionData.getLength() == 0 )
554             {
555                 rMediaDesc[ MediaDescriptor::PROP_ABORTED() ] <<= true;
556             }
557             else
558             {
559                 // create temporary file for unencrypted package
560                 Reference< XMultiServiceFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
561                 Reference< XStream > xTempFile( xFactory->createInstance( CREATE_OUSTRING( "com.sun.star.io.TempFile" ) ), UNO_QUERY_THROW );
562                 Reference< XOutputStream > xDecryptedPackage( xTempFile->getOutputStream(), UNO_SET_THROW );
563                 BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true );
564                 BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true );
565 
566                 EVP_CIPHER_CTX aes_ctx;
567                 EVP_CIPHER_CTX_init( &aes_ctx );
568                 EVP_DecryptInit_ex( &aes_ctx, EVP_aes_128_ecb(), 0, aVerifier.getKey(), 0 );
569                 EVP_CIPHER_CTX_set_padding( &aes_ctx, 0 );
570 
571                 sal_uInt8 pnInBuffer[ 1024 ];
572                 sal_uInt8 pnOutBuffer[ 1024 ];
573                 sal_Int32 nInLen;
574                 int nOutLen;
575                 aEncryptedPackage.skip( 8 ); // decrypted size
576                 while( (nInLen = aEncryptedPackage.readMemory( pnInBuffer, sizeof( pnInBuffer ) )) > 0 )
577                 {
578                     EVP_DecryptUpdate( &aes_ctx, pnOutBuffer, &nOutLen, pnInBuffer, nInLen );
579                     aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen );
580                 }
581                 EVP_DecryptFinal_ex( &aes_ctx, pnOutBuffer, &nOutLen );
582                 aDecryptedPackage.writeMemory( pnOutBuffer, nOutLen );
583 
584                 EVP_CIPHER_CTX_cleanup( &aes_ctx );
585                 xDecryptedPackage->flush();
586                 aDecryptedPackage.seekToStart();
587 
588                 // store temp file in media descriptor to keep it alive
589                 rMediaDesc.setComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ), Any( xTempFile ) );
590 
591                 Reference< XInputStream > xDecrInStrm = xTempFile->getInputStream();
592                 if( lclIsZipPackage( mxContext, xDecrInStrm ) )
593                     return xDecrInStrm;
594             }
595         }
596     }
597     catch( Exception& )
598     {
599     }
600 
601     return Reference< XInputStream >();
602 }
603 
604 // com.sun.star.lang.XServiceInfo interface -----------------------------------
605 
getImplementationName()606 OUString SAL_CALL FilterDetect::getImplementationName() throw( RuntimeException )
607 {
608     return FilterDetect_getImplementationName();
609 }
610 
supportsService(const OUString & rServiceName)611 sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName ) throw( RuntimeException )
612 {
613     const Sequence< OUString > aServices = FilterDetect_getSupportedServiceNames();
614     const OUString* pArray = aServices.getConstArray();
615     const OUString* pArrayEnd = pArray + aServices.getLength();
616     return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
617 }
618 
getSupportedServiceNames()619 Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames() throw( RuntimeException )
620 {
621     return FilterDetect_getSupportedServiceNames();
622 }
623 
624 // com.sun.star.document.XExtendedFilterDetection interface -------------------
625 
detect(Sequence<PropertyValue> & rMediaDescSeq)626 OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq ) throw( RuntimeException )
627 {
628     OUString aFilterName;
629     MediaDescriptor aMediaDesc( rMediaDescSeq );
630 
631     /*  Check that the user has not choosen to abort detection, e.g. by hitting
632         'Cancel' in the password input dialog. This may happen because this
633         filter detection is used by different filters. */
634     bool bAborted = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED(), false );
635     if( !bAborted ) try
636     {
637         aMediaDesc.addInputStream();
638 
639         /*  Get the unencrypted input stream. This may include creation of a
640             temporary file that contains the decrypted package. This temporary
641             file will be stored in the 'ComponentData' property of the media
642             descriptor. */
643         Reference< XInputStream > xInStrm( extractUnencryptedPackage( aMediaDesc ), UNO_SET_THROW );
644 
645         // stream must be a ZIP package
646         ZipStorage aZipStorage( mxContext, xInStrm );
647         if( aZipStorage.isStorage() )
648         {
649             // create the fast parser, register the XML namespaces, set document handler
650             FastParser aParser( mxContext );
651             aParser.registerNamespace( NMSP_packageRel );
652             aParser.registerNamespace( NMSP_officeRel );
653             aParser.registerNamespace( NMSP_packageContentTypes );
654             aParser.setDocumentHandler( new FilterDetectDocHandler( aFilterName ) );
655 
656             /*  Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
657                 to determine the content type of the part at the target path. */
658             aParser.parseStream( aZipStorage, CREATE_OUSTRING( "_rels/.rels" ) );
659             aParser.parseStream( aZipStorage, CREATE_OUSTRING( "[Content_Types].xml" ) );
660         }
661     }
662     catch( Exception& )
663     {
664     }
665 
666     // write back changed media descriptor members
667     aMediaDesc >> rMediaDescSeq;
668     return aFilterName;
669 }
670 
671 // ============================================================================
672 
673 } // namespace core
674 } // namespace oox
675