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