xref: /core/sfx2/source/doc/docmacromode.cxx (revision 2beaa3be3829303e948d401f492dbfd239d60aad)
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>
23 #include <sfx2/signaturestate.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 
33 #include <comphelper/processfactory.hxx>
34 #include <framework/interaction.hxx>
35 #include <osl/file.hxx>
36 #include <unotools/securityoptions.hxx>
37 #include <svtools/sfxecode.hxx>
38 #include <comphelper/diagnose_ex.hxx>
39 #include <tools/urlobj.hxx>
40 
41 #if defined(_WIN32)
42 #include <o3tl/char16_t2wchar_t.hxx>
43 #include <officecfg/Office/Common.hxx>
44 #include <systools/win32/comtools.hxx>
45 #include <urlmon.h>
46 #endif
47 
48 namespace 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 
71     struct DocumentMacroMode_Data
72     {
73         IMacroDocumentAccess&       m_rDocumentAccess;
74         bool m_bHasUnsignedContentError;
75         /// Is true when macros was disabled due to invalid signatures (when macro security is high)
76         bool m_bHasInvalidSignaturesError;
77 
DocumentMacroMode_Datasfx2::DocumentMacroMode_Data78         explicit DocumentMacroMode_Data( IMacroDocumentAccess& rDocumentAccess )
79             :m_rDocumentAccess( rDocumentAccess )
80             ,m_bHasUnsignedContentError( false )
81             ,m_bHasInvalidSignaturesError( false )
82         {
83         }
84     };
85 
86     namespace
87     {
lcl_showMacroWarning(const Reference<XInteractionHandler> & rxHandler,const OUString & rDocumentLocation)88         bool lcl_showMacroWarning( const Reference< XInteractionHandler >& rxHandler,
89             const OUString& rDocumentLocation )
90         {
91             DocumentMacroConfirmationRequest aRequest;
92             aRequest.DocumentURL = rDocumentLocation;
93             return SfxMedium::CallApproveHandler( rxHandler, Any( aRequest ), true );
94         }
95     }
96 
97     //= DocumentMacroMode
DocumentMacroMode(IMacroDocumentAccess & rDocumentAccess)98     DocumentMacroMode::DocumentMacroMode( IMacroDocumentAccess& rDocumentAccess )
99         :m_xData( std::make_shared<DocumentMacroMode_Data>( rDocumentAccess ) )
100     {
101     }
102 
allowMacroExecution()103     bool DocumentMacroMode::allowMacroExecution()
104     {
105         m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::ALWAYS_EXECUTE_NO_WARN );
106         return true;
107     }
108 
disallowMacroExecution()109     bool DocumentMacroMode::disallowMacroExecution()
110     {
111         m_xData->m_rDocumentAccess.setCurrentMacroExecMode( MacroExecMode::NEVER_EXECUTE );
112         return false;
113     }
114 
adjustMacroMode(const Reference<XInteractionHandler> & rxInteraction,bool bHasValidContentSignature)115     bool DocumentMacroMode::adjustMacroMode( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature )
116     {
117         if ( SvtSecurityOptions::IsMacroDisabled() )
118         {
119             // no macro should be executed at all
120             return disallowMacroExecution();
121         }
122 
123         // get setting from configuration if required
124         enum AutoConfirmation
125         {
126             eNoAutoConfirm,
127             eAutoConfirmApprove,
128             eAutoConfirmReject
129         };
130         AutoConfirmation eAutoConfirm( eNoAutoConfirm );
131 
132         sal_Int16 nMacroExecutionMode = m_xData->m_rDocumentAccess.getCurrentMacroExecMode();
133         if  (   ( nMacroExecutionMode == MacroExecMode::USE_CONFIG )
134             ||  ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION )
135             ||  ( nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION )
136             )
137         {
138             // check confirm first, as nMacroExecutionMode is always overwritten by the GetMacroSecurityLevel() switch
139             if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_REJECT_CONFIRMATION)
140                 eAutoConfirm = eAutoConfirmReject;
141             else if (nMacroExecutionMode == MacroExecMode::USE_CONFIG_APPROVE_CONFIRMATION)
142                 eAutoConfirm = eAutoConfirmApprove;
143 
144             switch ( SvtSecurityOptions::GetMacroSecurityLevel() )
145             {
146                 case 3: // "Very high"
147                     nMacroExecutionMode = MacroExecMode::FROM_LIST_NO_WARN;
148                     break;
149                 case 2: // "High"
150                     nMacroExecutionMode = MacroExecMode::FROM_LIST_AND_SIGNED_WARN;
151                     break;
152                 case 1: // "Medium"
153                     nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE;
154                     break;
155                 case 0: // "Low"
156                     nMacroExecutionMode = MacroExecMode::ALWAYS_EXECUTE_NO_WARN;
157                     break;
158                 default:
159                     OSL_FAIL( "DocumentMacroMode::adjustMacroMode: unexpected macro security level!" );
160                     nMacroExecutionMode = MacroExecMode::NEVER_EXECUTE;
161             }
162         }
163 
164         if ( nMacroExecutionMode == MacroExecMode::NEVER_EXECUTE )
165             return disallowMacroExecution();
166 
167         if ( nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE_NO_WARN )
168             return allowMacroExecution();
169 
170         SignatureState nSignatureState = SignatureState::UNKNOWN;
171         const OUString sURL(m_xData->m_rDocumentAccess.getDocumentLocation());
172         try
173         {
174             // get document location from medium name and check whether it is a trusted one
175             // the service is created without document version, since it is not of interest here
176             Reference< XDocumentDigitalSignatures > xSignatures(DocumentDigitalSignatures::createDefault(::comphelper::getProcessComponentContext()));
177             INetURLObject aURLReferer(sURL);
178 
179             OUString aLocation = aURLReferer.GetMainURL( INetURLObject::DecodeMechanism::NONE );
180 
181             if ( !aLocation.isEmpty() && xSignatures->isLocationTrusted( aLocation ) )
182             {
183                 return allowMacroExecution();
184             }
185 
186             // at this point it is clear that the document is not in the secure location
187             if ( nMacroExecutionMode == MacroExecMode::FROM_LIST_NO_WARN )
188             {
189                 return disallowMacroExecution();
190             }
191 
192             // check whether the document is signed with trusted certificate
193             if ( nMacroExecutionMode != MacroExecMode::FROM_LIST )
194             {
195                 nSignatureState = m_xData->m_rDocumentAccess.getScriptingSignatureState();
196 
197                 if (!bHasValidContentSignature
198                     && (nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN
199                         || nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN)
200                     && m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading())
201                 {
202                     // When macros are required to be signed, and the document has events which call
203                     // macros, the document content needs to be signed, too. Do it here, and avoid
204                     // possible UI asking to always trust certificates, after which the user's choice
205                     // to allow macros would be ignored anyway.
206                     m_xData->m_bHasUnsignedContentError
207                         = nSignatureState == SignatureState::OK
208                           || nSignatureState == SignatureState::NOTVALIDATED;
209                     return disallowMacroExecution();
210                 }
211 
212                 // At this point, the possible values of nMacroExecutionMode are: ALWAYS_EXECUTE,
213                 // FROM_LIST_AND_SIGNED_WARN (the default), FROM_LIST_AND_SIGNED_NO_WARN.
214                 // ALWAYS_EXECUTE corresponds to the Medium security level; it should ask for
215                 // confirmation when macros are unsigned or untrusted. FROM_LIST_AND_SIGNED_NO_WARN
216                 // should not ask any confirmations. FROM_LIST_AND_SIGNED_WARN should only allow
217                 // trusted signed macros at this point; so it may only ask for confirmation to add
218                 // certificates to trusted, and shouldn't show UI when trusted list is read-only
219                 // or the macro signature can't be validated.
220                 const bool bAllowUI
221                     = nMacroExecutionMode != MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN
222                       && eAutoConfirm == eNoAutoConfirm
223                       && (nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE
224                           || !SvtSecurityOptions::IsReadOnly(
225                               SvtSecurityOptions::EOption::MacroTrustedAuthors))
226                       && (nMacroExecutionMode != MacroExecMode::FROM_LIST_AND_SIGNED_WARN
227                           || nSignatureState == SignatureState::OK);
228 
229                 if (nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN
230                     && nSignatureState != SignatureState::NOSIGNATURES
231                     && nSignatureState != SignatureState::OK)
232                 {
233                     // set the flag so that we can show the appropriate error & buttons
234                     // for invalid signatures in the infobar for high macro security.
235                     m_xData->m_bHasInvalidSignaturesError = true;
236                 }
237 
238                 const bool bHasTrustedMacroSignature = m_xData->m_rDocumentAccess.hasTrustedScriptingSignature(bAllowUI ? rxInteraction : nullptr);
239 
240                 if (bHasTrustedMacroSignature)
241                 {
242                     // there is trusted macro signature, allow macro execution
243                     return allowMacroExecution();
244                 }
245                 else if ( nSignatureState == SignatureState::OK
246                        || nSignatureState == SignatureState::NOTVALIDATED )
247                 {
248                     // there is valid signature, but it is not from the trusted author
249                     if (eAutoConfirm == eAutoConfirmApprove
250                         && nMacroExecutionMode == MacroExecMode::ALWAYS_EXECUTE)
251                     {
252                         // For ALWAYS_EXECUTE + eAutoConfirmApprove (USE_CONFIG_APPROVE_CONFIRMATION
253                         // in Medium security mode), do not approve it right here; let Security Zone
254                         // check below do its job first.
255                     }
256                     else
257                     {
258                         // All other cases of valid but untrusted signatures should result in denied
259                         // macros here. This includes explicit reject from user in the UI in cases
260                         // of FROM_LIST_AND_SIGNED_WARN and ALWAYS_EXECUTE
261                         return disallowMacroExecution();
262                     }
263                 }
264                 // Other values of nSignatureState would result in either rejected macros
265                 // (FROM_LIST_AND_SIGNED_*), or a confirmation.
266             }
267         }
268         catch ( const Exception& )
269         {
270             DBG_UNHANDLED_EXCEPTION("sfx.doc");
271         }
272 
273         // at this point it is clear that the document is neither in secure location nor signed with trusted certificate
274         if ((nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_NO_WARN)
275             || (nMacroExecutionMode == MacroExecMode::FROM_LIST_AND_SIGNED_WARN))
276         {
277             return disallowMacroExecution();
278         }
279 
280 #if defined(_WIN32)
281         // Windows specific: try to decide macros loading depending on Windows Security Zones
282         // (is the file local, or it was downloaded from internet, etc?)
283         OUString sFilePath;
284         osl::FileBase::getSystemPathFromFileURL(sURL, sFilePath);
285         sal::systools::COMReference<IZoneIdentifier> pZoneId;
286         pZoneId.CoCreateInstance(CLSID_PersistentZoneIdentifier);
287         sal::systools::COMReference<IPersistFile> pPersist(pZoneId, sal::systools::COM_QUERY);
288         DWORD dwZone;
289         if (!pPersist || !SUCCEEDED(pPersist->Load(o3tl::toW(sFilePath.getStr()), STGM_READ)) ||
290             !SUCCEEDED(pZoneId->GetId(&dwZone)))
291         {
292             // no Security Zone info found -> assume a local file, not
293             // from the internet
294             dwZone = URLZONE_LOCAL_MACHINE;
295         }
296 
297         // determine action from zone and settings
298         sal_Int32 nAction;
299         switch (dwZone) {
300             case URLZONE_LOCAL_MACHINE:
301                 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneLocal::get();
302                 break;
303             case URLZONE_INTRANET:
304                 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneIntranet::get();
305                 break;
306             case URLZONE_TRUSTED:
307                 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneTrusted::get();
308                 break;
309             case URLZONE_INTERNET:
310                 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneInternet::get();
311                 break;
312             case URLZONE_UNTRUSTED:
313                 nAction = officecfg::Office::Common::Security::Scripting::WindowsSecurityZone::ZoneUntrusted::get();
314                 break;
315             default:
316                 // unknown zone, let's ask the user
317                 nAction = 0;
318                 break;
319         }
320 
321         // act on result
322         switch (nAction)
323         {
324             case 0: // Ask
325                 break;
326             case 1: // Allow
327                 if (nSignatureState != SignatureState::BROKEN
328                     && nSignatureState != SignatureState::INVALID)
329                     return allowMacroExecution();
330                 break;
331             case 2: // Deny
332                 return disallowMacroExecution();
333         }
334 #endif
335         // confirmation is required
336         bool bSecure = false;
337 
338         if ( eAutoConfirm == eNoAutoConfirm )
339         {
340             OUString sReferrer(sURL);
341             osl::FileBase::getSystemPathFromFileURL(sReferrer, sReferrer);
342 
343             bSecure = lcl_showMacroWarning( rxInteraction, sReferrer );
344         }
345         else
346             bSecure = ( eAutoConfirm == eAutoConfirmApprove );
347 
348         return ( bSecure ? allowMacroExecution() : disallowMacroExecution() );
349     }
350 
351 
isMacroExecutionDisallowed() const352     bool DocumentMacroMode::isMacroExecutionDisallowed() const
353     {
354         return m_xData->m_rDocumentAccess.getCurrentMacroExecMode() == MacroExecMode::NEVER_EXECUTE;
355     }
356 
357 
containerHasBasicMacros(const Reference<XLibraryContainer> & xContainer)358     bool DocumentMacroMode::containerHasBasicMacros( const Reference< XLibraryContainer >& xContainer )
359     {
360         bool bHasMacroLib = false;
361         try
362         {
363             if ( xContainer.is() )
364             {
365                 // a library container exists; check if it's empty
366 
367                 // if there are libraries except the "Standard" library
368                 // we assume that they are not empty (because they have been created by the user)
369                 if ( !xContainer->hasElements() )
370                     bHasMacroLib = false;
371                 else
372                 {
373                     static constexpr OUStringLiteral aStdLibName( u"Standard" );
374                     static constexpr OUStringLiteral aVBAProject( u"VBAProject" );
375                     const Sequence< OUString > aElements = xContainer->getElementNames();
376                     for( const OUString& aElement : aElements )
377                     {
378                         if( aElement == aStdLibName || aElement == aVBAProject )
379                         {
380                             Reference < XNameAccess > xLib;
381                             Any aAny = xContainer->getByName( aElement );
382                             aAny >>= xLib;
383                             if ( xLib.is() && xLib->hasElements() )
384                                 return true;
385                         }
386                         else
387                             return true;
388                     }
389                 }
390             }
391         }
392         catch( const Exception& )
393         {
394             DBG_UNHANDLED_EXCEPTION("sfx.doc");
395         }
396         return bHasMacroLib;
397     }
398 
399 
hasMacroLibrary() const400     bool DocumentMacroMode::hasMacroLibrary() const
401     {
402         bool bHasMacroLib = false;
403 #if HAVE_FEATURE_SCRIPTING
404         try
405         {
406             Reference< XEmbeddedScripts > xScripts( m_xData->m_rDocumentAccess.getEmbeddedDocumentScripts() );
407             Reference< XLibraryContainer > xContainer;
408             if ( xScripts.is() )
409                 xContainer.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
410             bHasMacroLib = containerHasBasicMacros( xContainer );
411 
412         }
413         catch( const Exception& )
414         {
415             DBG_UNHANDLED_EXCEPTION("sfx.doc");
416         }
417 #endif
418         return bHasMacroLib;
419     }
420 
hasUnsignedContentError() const421     bool DocumentMacroMode::hasUnsignedContentError() const
422     {
423         return m_xData->m_bHasUnsignedContentError;
424     }
425 
hasInvalidSignaturesError() const426     bool DocumentMacroMode::hasInvalidSignaturesError() const
427     {
428         return m_xData->m_bHasInvalidSignaturesError;
429     }
430 
storageHasMacros(const Reference<XStorage> & rxStorage)431     bool DocumentMacroMode::storageHasMacros( const Reference< XStorage >& rxStorage )
432     {
433         bool bHasMacros = false;
434         if ( rxStorage.is() )
435         {
436             try
437             {
438                 static constexpr OUString s_sBasicStorageName( u"Basic"_ustr );
439                 static constexpr OUString s_sScriptsStorageName( u"Scripts"_ustr );
440 
441                 bHasMacros =(   (   rxStorage->hasByName( s_sBasicStorageName )
442                                 &&  rxStorage->isStorageElement( s_sBasicStorageName )
443                                 )
444                             ||  (   rxStorage->hasByName( s_sScriptsStorageName )
445                                 &&  rxStorage->isStorageElement( s_sScriptsStorageName )
446                                 )
447                             );
448             }
449             catch ( const Exception& )
450             {
451                 DBG_UNHANDLED_EXCEPTION("sfx.doc");
452             }
453         }
454         return bHasMacros;
455     }
456 
hasMacros() const457     bool DocumentMacroMode::hasMacros() const
458     {
459         return m_xData->m_rDocumentAccess.documentStorageHasMacros() || hasMacroLibrary() || m_xData->m_rDocumentAccess.macroCallsSeenWhileLoading();
460     }
461 
checkMacrosOnLoading(const Reference<XInteractionHandler> & rxInteraction,bool bHasValidContentSignature,bool bHasMacros)462     bool DocumentMacroMode::checkMacrosOnLoading( const Reference< XInteractionHandler >& rxInteraction, bool bHasValidContentSignature, bool bHasMacros )
463     {
464         bool bAllow = false;
465         if ( SvtSecurityOptions::IsMacroDisabled() )
466         {
467             // no macro should be executed at all
468             bAllow = disallowMacroExecution();
469         }
470         else
471         {
472             if (bHasMacros)
473             {
474                 bAllow = adjustMacroMode( rxInteraction, bHasValidContentSignature );
475             }
476             else if ( !isMacroExecutionDisallowed() )
477             {
478                 // if macros will be added by the user later, the security check is obsolete
479                 bAllow = allowMacroExecution();
480             }
481         }
482         return bAllow;
483     }
484 
485 
486 } // namespace sfx2
487 
488 
489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
490