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
