LibreOffice Module sfx2 (master) 1
docmacromode.cxx
Go to the documentation of this file.
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 <config_features.h>
21
22#include <sfx2/docmacromode.hxx>
24#include <sfx2/docfile.hxx>
25
26#include <com/sun/star/document/MacroExecMode.hpp>
27#include <com/sun/star/task/ErrorCodeRequest.hpp>
28#include <com/sun/star/task/DocumentMacroConfirmationRequest.hpp>
29#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
30#include <com/sun/star/script/XLibraryContainer.hpp>
31#include <com/sun/star/document/XEmbeddedScripts.hpp>
32
35#include <osl/file.hxx>
37#include <svtools/sfxecode.hxx>
39#include <tools/urlobj.hxx>
40
41#if defined(_WIN32)
43#include <officecfg/Office/Common.hxx>
44#include <systools/win32/comtools.hxx>
45#include <urlmon.h>
46#endif
47
48namespace sfx2
49{
50
51
52 using ::com::sun::star::uno::Reference;
53 using ::com::sun::star::task::XInteractionHandler;
54 using ::com::sun::star::uno::Any;
55 using ::com::sun::star::uno::Sequence;
56 using ::com::sun::star::task::DocumentMacroConfirmationRequest;
57 using ::com::sun::star::uno::Exception;
58 using ::com::sun::star::security::DocumentDigitalSignatures;
59 using ::com::sun::star::security::XDocumentDigitalSignatures;
60 using ::com::sun::star::embed::XStorage;
61 using ::com::sun::star::document::XEmbeddedScripts;
62 using ::com::sun::star::script::XLibraryContainer;
63 using ::com::sun::star::container::XNameAccess;
64 using ::com::sun::star::uno::UNO_QUERY_THROW;
65
66 namespace MacroExecMode = ::com::sun::star::document::MacroExecMode;
67
68
69 //= DocumentMacroMode_Data
70
72 {
75
76 explicit DocumentMacroMode_Data( IMacroDocumentAccess& rDocumentAccess )
77 :m_rDocumentAccess( rDocumentAccess )
79 {
80 }
81 };
82
83 namespace
84 {
85 bool lcl_showMacroWarning( const Reference< XInteractionHandler >& rxHandler,
86 const OUString& rDocumentLocation )
87 {
88 DocumentMacroConfirmationRequest aRequest;
89 aRequest.DocumentURL = rDocumentLocation;
90 return SfxMedium::CallApproveHandler( rxHandler, Any( aRequest ), true );
91 }
92 }
93
94 //= DocumentMacroMode
96 :m_xData( std::make_shared<DocumentMacroMode_Data>( rDocumentAccess ) )
97 {
98 }
99
101 {
102 m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::ALWAYS_EXECUTE_NO_WARN );
103 return true;
104 }
105
107 {
108 m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::NEVER_EXECUTE );
109 return false;
110 }
111
112 bool DocumentMacroMode::adjustMacroMode( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature )
113 {
114 sal_uInt16 nMacroExecutionMode = m_xData->m_rDocumentAccess.getCurrentMacroExecMode();
115
117 {
118 // no macro should be executed at all
119 return disallowMacroExecution();
120 }
121
122 // get setting from configuration if required
123 enum AutoConfirmation
124 {
125 eNoAutoConfirm,
126 eAutoConfirmApprove,
127 eAutoConfirmReject
128 };
129 AutoConfirmation eAutoConfirm( eNoAutoConfirm );
130
131 if ( ( nMacroExecutionMode == MacroExecMode::USE_CONFIG )
132 || ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION )
133 || ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION )
134 )
135 {
136 // check confirm first, as nMacroExecutionMode is always overwritten by the GetMacroSecurityLevel() switch
137 if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION)
138 eAutoConfirm = eAutoConfirmReject;
139 else if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION)
140 eAutoConfirm = eAutoConfirmApprove;
141
143 {
144 case 3:
145 nMacroExecutionMode = MacroExecMode::FROM_LIST_NO_WARN;
146 break;
147 case 2:
148 nMacroExecutionMode = MacroExecMode::FROM_LIST_AND_SIGNED_WARN;
149 break;
150 case 1:
151 nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE;
152 break;
153 case 0:
154 nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE_NO_WARN;
155 break;
156 default:
157 OSL_FAIL( "DocumentMacroMode::adjustMacroMode: unexpected macro security level!" );
158 nMacroExecutionMode = MacroExecMode::NEVER_EXECUTE;
159 }
160 }
161
162 if ( nMacroExecutionMode == MacroExecMode::NEVER_EXECUTE )
163 return false;
164
165 if ( nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE_NO_WARN )
166 return true;
167
168 const OUString sURL(m_xData->m_rDocumentAccess.getDocumentLocation());
169 try
170 {
171 // get document location from medium name and check whether it is a trusted one
172 // the service is created without document version, since it is not of interest here
173 Reference< XDocumentDigitalSignatures > xSignatures(DocumentDigitalSignatures::createDefault(::comphelper::getProcessComponentContext()));
174 INetURLObject aURLReferer(sURL);
175
176 OUString aLocation;
177 if ( aURLReferer.removeSegment() )
178 aLocation = aURLReferer.GetMainURL( INetURLObject::DecodeMechanism::NONE );
179
180 if ( !aLocation.isEmpty() && xSignatures->isLocationTrusted( aLocation ) )
181 {
182 return allowMacroExecution();
183 }
184
185 // at this point it is clear that the document is not in the secure location
186 if ( nMacroExecutionMode == MacroExecMode::FROM_LIST_NO_WARN )
187 {
188 return disallowMacroExecution();
189 }
190
191 // check whether the document is signed with trusted certificate
192 if ( nMacroExecutionMode != MacroExecMode::FROM_LIST )
193 {
194 // the trusted macro check will also retrieve the signature state ( small optimization )
195 const bool bAllowUIToAddAuthor = nMacroExecutionMode != MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN
196 && (nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE
198 const bool bHasTrustedMacroSignature = m_xData->m_rDocumentAccess.hasTrustedScriptingSignature(bAllowUIToAddAuthor);
199
200 SignatureState nSignatureState = m_xData->m_rDocumentAccess.getScriptingSignatureState();
201 if ( nSignatureState == SignatureState::BROKEN )
202 {
203 return disallowMacroExecution();
204 }
205 else if ( m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading() &&
206 bHasTrustedMacroSignature &&
207 !bHasValidContentSignature)
208 {
209 // When macros are signed, and the document has events which call macros, the document content needs to be signed too.
210 m_xData->m_bHasUnsignedContentError = true;
211 return disallowMacroExecution();
212 }
213 else if ( bHasTrustedMacroSignature )
214 {
215 // there is trusted macro signature, allow macro execution
216 return allowMacroExecution();
217 }
218 else if ( nSignatureState == SignatureState::OK
219 || nSignatureState == SignatureState::NOTVALIDATED )
220 {
221 // there is valid signature, but it is not from the trusted author
222 return disallowMacroExecution();
223 }
224 }
225
226 // at this point it is clear that the document is neither in secure location nor signed with trusted certificate
227 if ( ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN )
228 || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN )
229 )
230 {
231 return disallowMacroExecution();
232 }
233 }
234 catch ( const Exception& )
235 {
236 if ( ( nMacroExecutionMode == MacroExecMode::FROM_LIST_NO_WARN )
237 || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN )
238 || ( nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN )
239 )
240 {
241 return disallowMacroExecution();
242 }
243 }
244
245#if defined(_WIN32)
246 // Windows specific: try to decide macros loading depending on Windows Security Zones
247 // (is the file local, or it was downloaded from internet, etc?)
248 OUString sFilePath;
249 osl::FileBase::getSystemPathFromFileURL(sURL, sFilePath);
250 sal::systools::COMReference<IZoneIdentifier> pZoneId;
251 pZoneId.CoCreateInstance(CLSID_PersistentZoneIdentifier);
252 sal::systools::COMReference<IPersistFile> pPersist(pZoneId, sal::systools::COM_QUERY_THROW);
253 DWORD dwZone;
254 if (!SUCCEEDED(pPersist->Load(o3tl::toW(sFilePath.getStr()), STGM_READ)) ||
255 !SUCCEEDED(pZoneId->GetId(&dwZone)))
256 {
257 // no Security Zone info found -> assume a local file, not
258 // from the internet
259 dwZone = URLZONE_LOCAL_MACHINE;
260 }
261
262 // determine action from zone and settings
263 sal_Int32 nAction;
264 switch (dwZone) {
265 case URLZONE_LOCAL_MACHINE:
266 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneLocal::get();
267 break;
268 case URLZONE_INTRANET:
269 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneIntranet::get();
270 break;
271 case URLZONE_TRUSTED:
272 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneTrusted::get();
273 break;
274 case URLZONE_INTERNET:
275 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneInternet::get();
276 break;
277 case URLZONE_UNTRUSTED:
278 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneUntrusted::get();
279 break;
280 default:
281 // unknown zone, let's ask the user
282 nAction = 0;
283 break;
284 }
285
286 // act on result
287 switch (nAction)
288 {
289 case 0: // Ask
290 break;
291 case 1: // Allow
292 return allowMacroExecution();
293 case 2: // Deny
294 return disallowMacroExecution();
295 }
296#endif
297 // confirmation is required
298 bool bSecure = false;
299
300 if ( eAutoConfirm == eNoAutoConfirm )
301 {
302 OUString sReferrer(sURL);
303
304 OUString aSystemFileURL;
305 if ( osl::FileBase::getSystemPathFromFileURL( sReferrer, aSystemFileURL ) == osl::FileBase::E_None )
306 sReferrer = aSystemFileURL;
307
308 bSecure = lcl_showMacroWarning( rxInteraction, sReferrer );
309 }
310 else
311 bSecure = ( eAutoConfirm == eAutoConfirmApprove );
312
313 return ( bSecure ? allowMacroExecution() : disallowMacroExecution() );
314 }
315
316
318 {
319 return m_xData->m_rDocumentAccess.getCurrentMacroExecMode() == MacroExecMode::NEVER_EXECUTE;
320 }
321
322
323 bool DocumentMacroMode::containerHasBasicMacros( const Reference< XLibraryContainer >& xContainer )
324 {
325 bool bHasMacroLib = false;
326 try
327 {
328 if ( xContainer.is() )
329 {
330 // a library container exists; check if it's empty
331
332 // if there are libraries except the "Standard" library
333 // we assume that they are not empty (because they have been created by the user)
334 if ( !xContainer->hasElements() )
335 bHasMacroLib = false;
336 else
337 {
338 static constexpr OUStringLiteral aStdLibName( u"Standard" );
339 static constexpr OUStringLiteral aVBAProject( u"VBAProject" );
340 const Sequence< OUString > aElements = xContainer->getElementNames();
341 for( const OUString& aElement : aElements )
342 {
343 if( aElement == aStdLibName || aElement == aVBAProject )
344 {
345 Reference < XNameAccess > xLib;
346 Any aAny = xContainer->getByName( aElement );
347 aAny >>= xLib;
348 if ( xLib.is() && xLib->hasElements() )
349 return true;
350 }
351 else
352 return true;
353 }
354 }
355 }
356 }
357 catch( const Exception& )
358 {
359 DBG_UNHANDLED_EXCEPTION("sfx.doc");
360 }
361 return bHasMacroLib;
362 }
363
364
366 {
367 bool bHasMacroLib = false;
368#if HAVE_FEATURE_SCRIPTING
369 try
370 {
371 Reference< XEmbeddedScripts > xScripts( m_xData->m_rDocumentAccess.getEmbeddedDocumentScripts() );
372 Reference< XLibraryContainer > xContainer;
373 if ( xScripts.is() )
374 xContainer.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
375 bHasMacroLib = containerHasBasicMacros( xContainer );
376
377 }
378 catch( const Exception& )
379 {
380 DBG_UNHANDLED_EXCEPTION("sfx.doc");
381 }
382#endif
383 return bHasMacroLib;
384 }
385
387 {
388 return m_xData->m_bHasUnsignedContentError;
389 }
390
391
392 bool DocumentMacroMode::storageHasMacros( const Reference< XStorage >& rxStorage )
393 {
394 bool bHasMacros = false;
395 if ( rxStorage.is() )
396 {
397 try
398 {
399 static constexpr OUStringLiteral s_sBasicStorageName( u"Basic" );
400 static constexpr OUStringLiteral s_sScriptsStorageName( u"Scripts" );
401
402 bHasMacros =( ( rxStorage->hasByName( s_sBasicStorageName )
403 && rxStorage->isStorageElement( s_sBasicStorageName )
404 )
405 || ( rxStorage->hasByName( s_sScriptsStorageName )
406 && rxStorage->isStorageElement( s_sScriptsStorageName )
407 )
408 );
409 }
410 catch ( const Exception& )
411 {
412 DBG_UNHANDLED_EXCEPTION("sfx.doc");
413 }
414 }
415 return bHasMacros;
416 }
417
418
419 bool DocumentMacroMode::checkMacrosOnLoading( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature )
420 {
421 bool bAllow = false;
423 {
424 // no macro should be executed at all
425 bAllow = disallowMacroExecution();
426 }
427 else
428 {
429 if (m_xData->m_rDocumentAccess.documentStorageHasMacros() || hasMacroLibrary() || m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading())
430 {
431 bAllow = adjustMacroMode( rxInteraction, bHasValidContentSignature );
432 }
433 else if ( !isMacroExecutionDisallowed() )
434 {
435 // if macros will be added by the user later, the security check is obsolete
436 bAllow = allowMacroExecution();
437 }
438 }
439 return bAllow;
440 }
441
442
443} // namespace sfx2
444
445
446/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool removeSegment(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true)
static bool CallApproveHandler(const css::uno::Reference< css::task::XInteractionHandler > &xHandler, const css::uno::Any &rRequest, bool bAllowAbort)
Definition: docfile.cxx:4376
bool hasUnsignedContentError() const
bool hasMacroLibrary() const
determines whether the document actually has a macros library
bool allowMacroExecution()
allows macro execution in the document
std::shared_ptr< DocumentMacroMode_Data > m_xData
bool disallowMacroExecution()
disallows macro execution in the document
bool adjustMacroMode(const css::uno::Reference< css::task::XInteractionHandler > &_rxInteraction, bool bHasValidContentSignature=false)
checks whether the document allows executing contained macros.
DocumentMacroMode(IMacroDocumentAccess &_rDocumentAccess)
creates an instance
static bool containerHasBasicMacros(const css::uno::Reference< css::script::XLibraryContainer > &xContainer)
bool checkMacrosOnLoading(const css::uno::Reference< css::task::XInteractionHandler > &_rxInteraction, bool bHasValidContentSignature=false)
checks the macro execution mode while loading the document.
bool isMacroExecutionDisallowed() const
determines whether macro execution is disallowed
static bool storageHasMacros(const css::uno::Reference< css::embed::XStorage > &_rxStorage)
determines whether the given document storage has sub storages containing scripts or macros.
provides access to several settings of a document, which are needed by ->DocumentMacroMode to properl...
#define DBG_UNHANDLED_EXCEPTION(...)
float u
bool IsReadOnly(EOption eOption)
sal_Int32 GetMacroSecurityLevel()
@ Exception
std::shared_ptr< T > make_shared(Args &&... args)
SignatureState
IMacroDocumentAccess & m_rDocumentAccess
DocumentMacroMode_Data(IMacroDocumentAccess &rDocumentAccess)