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 #include "oox/core/encryption.hxx"
26
27 #include <com/sun/star/io/XStream.hpp>
28 #include <com/sun/star/logging/LogLevel.hpp>
29 #include <comphelper/docpasswordhelper.hxx>
30 #include <comphelper/mediadescriptor.hxx>
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::logging;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::xml::sax;
50
51 using ::comphelper::MediaDescriptor;
52 using ::comphelper::SequenceAsHashMap;
53 using ::rtl::OUString;
54
55 // ============================================================================
56
FilterDetectDocHandler(OUString & rFilterName)57 FilterDetectDocHandler::FilterDetectDocHandler( OUString& rFilterName ) :
58 mrFilterName( rFilterName )
59 {
60 maContextStack.reserve( 2 );
61 }
62
~FilterDetectDocHandler()63 FilterDetectDocHandler::~FilterDetectDocHandler()
64 {
65 }
66
startDocument()67 void SAL_CALL FilterDetectDocHandler::startDocument()
68 {
69 }
70
endDocument()71 void SAL_CALL FilterDetectDocHandler::endDocument()
72 {
73 }
74
setDocumentLocator(const Reference<XLocator> &)75 void SAL_CALL FilterDetectDocHandler::setDocumentLocator( const Reference<XLocator>& /*xLocator*/ )
76 {
77 }
78
startFastElement(sal_Int32 nElement,const Reference<XFastAttributeList> & rAttribs)79 void SAL_CALL FilterDetectDocHandler::startFastElement(
80 sal_Int32 nElement, const Reference< XFastAttributeList >& rAttribs )
81 {
82 AttributeList aAttribs( rAttribs );
83 switch ( nElement )
84 {
85 // cases for _rels/.rels
86 case PR_TOKEN( Relationships ):
87 break;
88 case PR_TOKEN( Relationship ):
89 if( !maContextStack.empty() && (maContextStack.back() == PR_TOKEN( Relationships )) )
90 parseRelationship( aAttribs );
91 break;
92
93 // cases for [Content_Types].xml
94 case PC_TOKEN( Types ):
95 break;
96 case PC_TOKEN( Default ):
97 if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
98 parseContentTypesDefault( aAttribs );
99 break;
100 case PC_TOKEN( Override ):
101 if( !maContextStack.empty() && (maContextStack.back() == PC_TOKEN( Types )) )
102 parseContentTypesOverride( aAttribs );
103 break;
104 }
105 maContextStack.push_back( nElement );
106 }
107
startUnknownElement(const OUString &,const OUString &,const Reference<XFastAttributeList> &)108 void SAL_CALL FilterDetectDocHandler::startUnknownElement(
109 const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/ )
110 {
111 }
112
endFastElement(sal_Int32)113 void SAL_CALL FilterDetectDocHandler::endFastElement( sal_Int32 /*nElement*/ )
114 {
115 maContextStack.pop_back();
116 }
117
endUnknownElement(const OUString &,const OUString &)118 void SAL_CALL FilterDetectDocHandler::endUnknownElement(
119 const OUString& /*Namespace*/, const OUString& /*Name*/ )
120 {
121 }
122
createFastChildContext(sal_Int32,const Reference<XFastAttributeList> &)123 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createFastChildContext(
124 sal_Int32 /*Element*/, const Reference<XFastAttributeList>& /*Attribs*/ )
125 {
126 return this;
127 }
128
createUnknownChildContext(const OUString &,const OUString &,const Reference<XFastAttributeList> &)129 Reference<XFastContextHandler> SAL_CALL FilterDetectDocHandler::createUnknownChildContext(
130 const OUString& /*Namespace*/, const OUString& /*Name*/, const Reference<XFastAttributeList>& /*Attribs*/)
131 {
132 return this;
133 }
134
characters(const OUString &)135 void SAL_CALL FilterDetectDocHandler::characters( const OUString& /*aChars*/ )
136 {
137 }
138
ignorableWhitespace(const OUString &)139 void SAL_CALL FilterDetectDocHandler::ignorableWhitespace( const OUString& /*aWhitespaces*/ )
140 {
141 }
142
processingInstruction(const OUString &,const OUString &)143 void SAL_CALL FilterDetectDocHandler::processingInstruction(
144 const OUString& /*aTarget*/, const OUString& /*aData*/ )
145 {
146 }
147
parseRelationship(const AttributeList & rAttribs)148 void FilterDetectDocHandler::parseRelationship( const AttributeList& rAttribs )
149 {
150 OUString aType = rAttribs.getString( XML_Type, OUString() );
151 if( aType.equalsAscii( "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" ) )
152 maTargetPath = RelationsFragment::removeDuplicateSlashes( OUString( sal_Unicode( '/' ) ) + rAttribs.getString( XML_Target, OUString() ) );
153 }
154
getFilterNameFromContentType(const OUString & rContentType) const155 OUString FilterDetectDocHandler::getFilterNameFromContentType( const OUString& rContentType ) const
156 {
157 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" ) ||
158 rContentType.equalsAscii( "application/vnd.ms-word.document.macroEnabled.main+xml" ) )
159 return CREATE_OUSTRING( "writer_MS_Word_2007" );
160
161 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml" ) ||
162 rContentType.equalsAscii( "application/vnd.ms-word.template.macroEnabledTemplate.main+xml" ) )
163 return CREATE_OUSTRING( "writer_MS_Word_2007_Template" );
164
165 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" ) ||
166 rContentType.equalsAscii( "application/vnd.ms-excel.sheet.macroEnabled.main+xml" ) )
167 return CREATE_OUSTRING( "MS Excel 2007 XML" );
168
169 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml" ) ||
170 rContentType.equalsAscii( "application/vnd.ms-excel.template.macroEnabled.main+xml" ) )
171 return CREATE_OUSTRING( "MS Excel 2007 XML Template" );
172
173 if( rContentType.equalsAscii( "application/vnd.ms-excel.sheet.binary.macroEnabled.main" ) )
174 return CREATE_OUSTRING( "MS Excel 2007 Binary" );
175
176 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml" ) ||
177 rContentType.equalsAscii( "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml" ) )
178 return CREATE_OUSTRING( "MS PowerPoint 2007 XML" );
179
180 if( rContentType.equalsAscii( "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml" ) ||
181 rContentType.equalsAscii( "application/vnd.ms-powerpoint.template.macroEnabled.main+xml" ) )
182 return CREATE_OUSTRING( "MS PowerPoint 2007 XML Template" );
183
184 return OUString();
185 }
186
parseContentTypesDefault(const AttributeList & rAttribs)187 void FilterDetectDocHandler::parseContentTypesDefault( const AttributeList& rAttribs )
188 {
189 // only if no overridden part name found
190 if( mrFilterName.getLength() == 0 )
191 {
192 // check if target path ends with extension
193 OUString aExtension = rAttribs.getString( XML_Extension, OUString() );
194 sal_Int32 nExtPos = maTargetPath.getLength() - aExtension.getLength();
195 if( (nExtPos > 0) && (maTargetPath[ nExtPos - 1 ] == '.') && maTargetPath.match( aExtension, nExtPos ) )
196 mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
197 }
198 }
199
parseContentTypesOverride(const AttributeList & rAttribs)200 void FilterDetectDocHandler::parseContentTypesOverride( const AttributeList& rAttribs )
201 {
202 if( rAttribs.getString( XML_PartName, OUString() ).equals( maTargetPath ) )
203 mrFilterName = getFilterNameFromContentType( rAttribs.getString( XML_ContentType, OUString() ) );
204 }
205
206 // ============================================================================
207
208 /* Helper for XServiceInfo */
FilterDetect_getSupportedServiceNames()209 Sequence< OUString > FilterDetect_getSupportedServiceNames()
210 {
211 Sequence< OUString > aServiceNames( 1 );
212 aServiceNames[ 0 ] = CREATE_OUSTRING( "com.sun.star.frame.ExtendedTypeDetection" );
213 return aServiceNames;
214 }
215
216 /* Helper for XServiceInfo */
FilterDetect_getImplementationName()217 OUString FilterDetect_getImplementationName()
218 {
219 return CREATE_OUSTRING( "com.sun.star.comp.oox.FormatDetector" );
220 }
221
222 /* Helper for registry */
FilterDetect_createInstance(const Reference<XComponentContext> & rxContext)223 Reference< XInterface > SAL_CALL FilterDetect_createInstance( const Reference< XComponentContext >& rxContext )
224 {
225 return static_cast< ::cppu::OWeakObject* >( new FilterDetect( rxContext ) );
226 }
227
228 // ----------------------------------------------------------------------------
229
FilterDetect(const Reference<XComponentContext> & rxContext)230 FilterDetect::FilterDetect( const Reference< XComponentContext >& rxContext ) :
231 mxContext( rxContext, UNO_SET_THROW ),
232 logger( rxContext )
233 {
234 }
235
~FilterDetect()236 FilterDetect::~FilterDetect()
237 {
238 }
239
240 namespace {
241
242 // ----------------------------------------------------------------------------
243
lclIsZipPackage(const Reference<XComponentContext> & rxContext,const Reference<XInputStream> & rxInStrm)244 bool lclIsZipPackage( const Reference< XComponentContext >& rxContext, const Reference< XInputStream >& rxInStrm )
245 {
246 ZipStorage aZipStorage( rxContext, rxInStrm );
247 return aZipStorage.isStorage();
248 }
249
250 // the password verifier ------------------------------------------------------
251
252 class PasswordVerifier : public ::comphelper::IDocPasswordVerifier
253 {
254 public:
255 explicit PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo >& rEncryptInfo, const ::comphelper::EventLogger& rLogger );
256
257 virtual ::comphelper::DocPasswordVerifierResult
258 verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData );
259 virtual ::comphelper::DocPasswordVerifierResult
260 verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData );
261
262 private:
263 const ::boost::shared_ptr< EncryptionInfo> encryptionInfo;
264 const ::comphelper::EventLogger logger;
265 };
266
PasswordVerifier(const::boost::shared_ptr<EncryptionInfo> & rEncryptInfo,const::comphelper::EventLogger & rLogger)267 PasswordVerifier::PasswordVerifier( const ::boost::shared_ptr< EncryptionInfo>& rEncryptInfo, const ::comphelper::EventLogger& rLogger ) :
268 encryptionInfo( rEncryptInfo ),
269 logger( rLogger )
270 {
271 }
272
verifyPassword(const OUString & rPassword,Sequence<NamedValue> & o_rEncryptionData)273 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyPassword( const OUString& rPassword, Sequence< NamedValue >& o_rEncryptionData )
274 {
275 try
276 {
277 o_rEncryptionData = encryptionInfo->verifyPassword( rPassword );
278 if( o_rEncryptionData.hasElements() )
279 {
280 logger.log( LogLevel::FINE, OUString::createFromAscii( "Password is correct" ) );
281 return ::comphelper::DocPasswordVerifierResult_OK;
282 }
283 else
284 {
285 logger.log( LogLevel::WARNING, OUString::createFromAscii( "Password is incorrect" ) );
286 return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
287 }
288 }
289 catch ( const Exception &e )
290 {
291 logger.log( LogLevel::WARNING, "Error verifying password: $1$", e.Message );
292 return ::comphelper::DocPasswordVerifierResult_ABORT;
293 }
294 }
295
verifyEncryptionData(const Sequence<NamedValue> & rEncryptionData)296 ::comphelper::DocPasswordVerifierResult PasswordVerifier::verifyEncryptionData( const Sequence< NamedValue >& rEncryptionData )
297 {
298 try
299 {
300 bool bResult = encryptionInfo->verifyEncryptionData( rEncryptionData );
301 if( bResult )
302 {
303 logger.log( LogLevel::FINE, OUString::createFromAscii( "EncryptionData is correct" ) );
304 return ::comphelper::DocPasswordVerifierResult_OK;
305 }
306 else
307 {
308 logger.log( LogLevel::WARNING, OUString::createFromAscii( "EncryptionData is incorrect" ) );
309 return ::comphelper::DocPasswordVerifierResult_WRONG_PASSWORD;
310 }
311 }
312 catch ( const Exception& e )
313 {
314 logger.log( LogLevel::WARNING, "Error verifying EncryptionData: $1$", e.Message );
315 return ::comphelper::DocPasswordVerifierResult_ABORT;
316 }
317 }
318
319 } // namespace
320
321 // ----------------------------------------------------------------------------
322
extractUnencryptedPackage(MediaDescriptor & rMediaDesc) const323 Reference< XInputStream > FilterDetect::extractUnencryptedPackage( MediaDescriptor& rMediaDesc ) const
324 {
325 // try the plain input stream
326 Reference< XInputStream > xInStrm( rMediaDesc[ MediaDescriptor::PROP_INPUTSTREAM() ], UNO_QUERY );
327 if( !xInStrm.is() || lclIsZipPackage( mxContext, xInStrm ) )
328 return xInStrm;
329
330 // check if a temporary file is passed in the 'ComponentData' property
331 Reference< XStream > xDecrypted( rMediaDesc.getComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ) ), UNO_QUERY );
332 if( xDecrypted.is() )
333 {
334 Reference< XInputStream > xDecrInStrm = xDecrypted->getInputStream();
335 if( lclIsZipPackage( mxContext, xDecrInStrm ) )
336 return xDecrInStrm;
337 }
338
339 // try to decrypt an encrypted OLE package
340 ::oox::ole::OleStorage aOleStorage( mxContext, xInStrm, false );
341 if( aOleStorage.isStorage() ) try
342 {
343 // open the required input streams in the encrypted package
344 Reference< XInputStream > xEncryptionInfo( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptionInfo" ) ), UNO_SET_THROW );
345 Reference< XInputStream > xEncryptedPackage( aOleStorage.openInputStream( CREATE_OUSTRING( "EncryptedPackage" ) ), UNO_SET_THROW );
346
347 // read the encryption info stream
348 ::boost::shared_ptr< EncryptionInfo > encryptionInfo( EncryptionInfo::readEncryptionInfo( mxContext, xEncryptionInfo ) );
349
350 // check flags and algorithm IDs, required are AES128 and SHA-1
351 bool bImplemented = encryptionInfo->isImplemented();
352 if( bImplemented )
353 {
354 /* "VelvetSweatshop" is the built-in default encryption
355 password used by MS Excel for the "workbook protection"
356 feature with password. Try this first before prompting the
357 user for a password. */
358 ::std::vector< OUString > aDefaultPasswords;
359 aDefaultPasswords.push_back( CREATE_OUSTRING( "VelvetSweatshop" ) );
360
361 /* Use the comphelper password helper to request a password.
362 This helper returns either with the correct password
363 (according to the verifier), or with an empty string if
364 user has cancelled the password input dialog. */
365 PasswordVerifier aVerifier( encryptionInfo, logger );
366 Sequence< NamedValue > aEncryptionData = ::comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
367 aVerifier, rMediaDesc, ::comphelper::DocPasswordRequestType_MS, &aDefaultPasswords );
368
369 if( aEncryptionData.getLength() == 0 )
370 {
371 rMediaDesc[ MediaDescriptor::PROP_ABORTED() ] <<= true;
372 }
373 else
374 {
375 // create temporary file for unencrypted package
376 Reference< XMultiComponentFactory > xFactory( mxContext->getServiceManager(), UNO_QUERY_THROW );
377 Reference< XStream > xTempFile( xFactory->createInstanceWithContext( CREATE_OUSTRING( "com.sun.star.io.TempFile" ), mxContext ), UNO_QUERY_THROW );
378 Reference< XOutputStream > xDecryptedPackage( xTempFile->getOutputStream(), UNO_SET_THROW );
379 BinaryXOutputStream aDecryptedPackage( xDecryptedPackage, true );
380 BinaryXInputStream aEncryptedPackage( xEncryptedPackage, true );
381
382 encryptionInfo->decryptStream( aEncryptedPackage, aDecryptedPackage );
383 aDecryptedPackage.seekToStart();
384
385 // store temp file in media descriptor to keep it alive
386 rMediaDesc.setComponentDataEntry( CREATE_OUSTRING( "DecryptedPackage" ), Any( xTempFile ) );
387
388 Reference< XInputStream > xDecrInStrm = xTempFile->getInputStream();
389 if( lclIsZipPackage( mxContext, xDecrInStrm ) )
390 return xDecrInStrm;
391 }
392 }
393 else
394 logger.log( LogLevel::WARNING, "Encryption type not implemented" );
395 }
396 catch( Exception& e )
397 {
398 logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::extractUnencryptedPackage(): $1$", e.Message );
399 }
400
401 return Reference< XInputStream >();
402 }
403
404 // com.sun.star.lang.XServiceInfo interface -----------------------------------
405
getImplementationName()406 OUString SAL_CALL FilterDetect::getImplementationName()
407 {
408 return FilterDetect_getImplementationName();
409 }
410
supportsService(const OUString & rServiceName)411 sal_Bool SAL_CALL FilterDetect::supportsService( const OUString& rServiceName )
412 {
413 const Sequence< OUString > aServices = FilterDetect_getSupportedServiceNames();
414 const OUString* pArray = aServices.getConstArray();
415 const OUString* pArrayEnd = pArray + aServices.getLength();
416 return ::std::find( pArray, pArrayEnd, rServiceName ) != pArrayEnd;
417 }
418
getSupportedServiceNames()419 Sequence< OUString > SAL_CALL FilterDetect::getSupportedServiceNames()
420 {
421 return FilterDetect_getSupportedServiceNames();
422 }
423
424 // com.sun.star.document.XExtendedFilterDetection interface -------------------
425
detect(Sequence<PropertyValue> & rMediaDescSeq)426 OUString SAL_CALL FilterDetect::detect( Sequence< PropertyValue >& rMediaDescSeq )
427 {
428 OUString aFilterName;
429 MediaDescriptor aMediaDesc( rMediaDescSeq );
430
431 /* Check that the user has not chosen to abort detection, e.g. by hitting
432 'Cancel' in the password input dialog. This may happen because this
433 filter detection is used by different filters. */
434 bool bAborted = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_ABORTED(), false );
435 if( !bAborted ) try
436 {
437 aMediaDesc.addInputStream();
438
439 /* Get the unencrypted input stream. This may include creation of a
440 temporary file that contains the decrypted package. This temporary
441 file will be stored in the 'ComponentData' property of the media
442 descriptor. */
443 Reference< XInputStream > xInStrm( extractUnencryptedPackage( aMediaDesc ), UNO_SET_THROW );
444
445 // stream must be a ZIP package
446 ZipStorage aZipStorage( mxContext, xInStrm );
447 if( aZipStorage.isStorage() )
448 {
449 // create the fast parser, register the XML namespaces, set document handler
450 FastParser aParser( mxContext );
451 aParser.registerNamespace( NMSP_packageRel );
452 aParser.registerNamespace( NMSP_officeRel );
453 aParser.registerNamespace( NMSP_packageContentTypes );
454 aParser.setDocumentHandler( new FilterDetectDocHandler( aFilterName ) );
455
456 /* Parse '_rels/.rels' to get the target path and '[Content_Types].xml'
457 to determine the content type of the part at the target path. */
458 aParser.parseStream( aZipStorage, CREATE_OUSTRING( "_rels/.rels" ) );
459 aParser.parseStream( aZipStorage, CREATE_OUSTRING( "[Content_Types].xml" ) );
460 }
461 }
462 catch( Exception& e )
463 {
464 logger.log( LogLevel::WARNING, "Error in ::oox::core::FilterDetect::detect(): $1$", e.Message );
465 }
466
467 // write back changed media descriptor members
468 aMediaDesc >> rMediaDescSeq;
469 return aFilterName;
470 }
471
472 // ============================================================================
473
474 } // namespace core
475 } // namespace oox
476