FIREWALL.MMH - Adding and Removing Windows Firewall Exceptions |
This information and/or code in this section was provided by Christoph Mockenhaupt (thank you).
The supplied sample code to exercise the new command:
;--- Firewall exception list entry for MyApp.exe ----------------------------- ; create data to be passed to the firewall macro #data "FwMyApp" "FullPath" "[INSTALLDIR]MyApp.exe" "FriendlyName" "<$ProdInfo.ProductName>" "RemoteAddresses" "10.0.42.0/255.255.255.0" ;; this can be omitted or "*" for all #data <$CaAddApplicationToFirewallExceptionList "FwMyApp" DescriptionAdd="Adding MyApp to the firewall exception list" DescriptionRemove="Removing MyApp from the firewall exception list">
The required header code (also installed by MAKEMSI):
;---------------------------------------------------------------------------- ; ; MODULE NAME: FireWall[ChristophMockenhaupt].mmh ; ; $Author: USER "Dennis" $ ; $Revision: 1.3 $ ; $Date: 18 Feb 2010 17:54:36 $ ; $Logfile: C:/DBAREIS/Projects.PVCS/Win32/MakeMsi/FireWall[ChristophMockenhaupt].mmh.pvcs $ ; ; This was supplied by Christoph Vogtlaender in email dated 27 Jan 2010, ; his comments: ; ; I created an macro to modify the MS Windows Firewall exception list ; at install time. ; ; The action is rollback aware, the macro can be called more than once ; to add more than one file, and can handle features. ; ; I think there is enough documentation in the code to understand what it does. ;---------------------------------------------------------------------------- ; Description: ; Adds/removes a program from the windows firewall exception list on install/uninstall ; #ifndef FIREWALL_MMH #define FIREWALL_MMH #NextId #NextId LOCK "FIREWALL.MMH" ;---------------------------------------------------------------------------- ;--- Define Feature and Component state queries ;--- for further information see "Conditions - Predefined by MAKEMSI" in ;--- the MAKEMSI doco ;---------------------------------------------------------------------------- #ifndef FeatureIs ;;The user may have already set thee up themselves... ;--- Nope they hadn't... ------------------------------------------------ #define FeatureIs (!{$#1} <$INSTALLCOMPARE_{$IS=^EQUAL^}> <$INSTALLSTATE_{$STATE=^LOCAL^}>) ;; Feature is already installed #define FeatureWillBe (&{$#1} <$INSTALLCOMPARE_{$IS=^EQUAL^}> <$INSTALLSTATE_{$STATE=^LOCAL^}>) ;; Feature is being installed #define ComponentIs (?{$#1} <$INSTALLCOMPARE_{$IS=^EQUAL^}> <$INSTALLSTATE_{$STATE=^LOCAL^}>) ;; Component is already installed #define ComponentWillBe (${$#1} <$INSTALLCOMPARE_{$IS=^EQUAL^}> <$INSTALLSTATE_{$STATE=^LOCAL^}>) ;; Component is being installed ;--- Define valid "IS" parameter comparison types --------------------------- #define INSTALLCOMPARE_EQUAL = #define INSTALLCOMPARE_NOT_EQUAL <> ;--- Define valid "STATE" parameter values ---------------------------------- #define INSTALLSTATE_UNKNOWN -1 ;; No action to be taken on the feature or component. #define INSTALLSTATE_ADVERTISED 1 ;; Advertised feature. This state is not available for components. #define INSTALLSTATE_ABSENT 2 ;; Feature or component is not present. #define INSTALLSTATE_LOCAL 3 ;; Feature or component on the local computer. #define INSTALLSTATE_SOURCE 4 ;; Feature or component run from the source. #endif ; The vbs code to add/remove an application to/from the firewall exception list #( '<?NewLine>' #define @@FirewallVbs ; first we need a VBScript custom action which uses the windows firewall manager api to add/remove a program <$VbsCa Binary="Firewall.vbs" Pause="N"> <$VbsCaEntry "AddApplicationToExceptionList"> CaDebug 1, "Adding application to Windows firewall exception list..." Const NET_FW_IP_VERSION_ANY = 2 Const NET_FW_SCOPE_ALL = 0 Dim objFirewall Dim objAuthApp Dim objProfile Dim strRemoteAddresses : strRemoteAddresses = "" ' Enable Application Err.Clear On Error resume next 'on SP1 there is no firewall object Set objFirewall = CreateObject("HNetCfg.FwMgr") Set objAuthApp = CreateObject("HNetCfg.FwAuthorizedApplication") Set objProfile = objFirewall.LocalPolicy.CurrentProfile if 0 = Err.Number then On Error Goto 0 CaDebug 2, "Setting friendly application name to " & VbsCaCadGet("FriendlyName") objAuthApp.Name = VbsCaCadGet("FriendlyName") CaDebug 2, "Setting image file name to " & VbsCaCadGet("FullPath") objAuthApp.ProcessImageFileName = VbsCaCadGet("FullPath") objAuthApp.Enabled = True objAuthApp.IpVersion = NET_FW_IP_VERSION_ANY objAuthApp.Scope = NET_FW_SCOPE_ALL ' the scope will be overwritten if RemoteAddress other than "" or "*" is specified CaDebug 0, "If the following call of VbsCaCadGet(RemoteAddresses) fails, no addresses were specified in the data field. This is OK." On Error Resume Next strRemoteAddresses = VbsCaCadGet("RemoteAddresses") Err.Clear On Error Goto 0 if strRemoteAddresses <> "" then CaDebug 2, "Setting remote addresses to " & strRemoteAddresses objAuthApp.RemoteAddresses = strRemoteAddresses end if objProfile.AuthorizedApplications.Add objAuthApp end if Err.Clear On Error Goto 0 CaDebug 2, "Done" <$/VbsCaEntry> <$VbsCaEntry "RemoveApplicationFromExceptionList"> CaDebug 1, "Removing application from Windows firewall exception list" Const HKEY_LOCAL_MACHINE = 2 Dim oFirewall : Set oFirewall = Nothing Dim oProfile : Set oProfile = Nothing Dim oInstaller : Set oInstaller = Nothing Dim strSharedComponentKey : strSharedComponentKey = "" Dim iReferenceCount : iReferenceCount = 0 CaDebug 0, "creating windows installer object" Set oInstaller = CreateObject("WindowsInstaller.Installer") CaDebug 0, "done" ' Disable Application only if the shared reference count is 0 ' (this applies even if we sequence the action before FileRemove) On Error Resume Next strSharedComponentKey = VbsCaCadGet("SharedComponentKey") Err.Clear On Error Goto 0 CaDebug 0, "strSharedComponentKey: " & strSharedComponentKey if strSharedComponentKey <> "" then CaDebug 0, "checking if reference count for " & strSharedComponentKey & " is greater than ""1""" On Error Resume Next iReferenceCount = oInstaller.RegistryValue(HKEY_LOCAL_MACHINE, "Software\Microsoft\Windows\CurrentVersion\SharedDLLs", strSharedComponentKey) Err.Clear On Error Goto 0 CaDebug 0, "Reference count is """ & iReferenceCount & """" end if if iReferenceCount <= 0 then CaDebug 0, "Refcount is 0... deleting firewall entry" On Error resume next 'on SP1 there is no firewall object Set oFirewall = CreateObject("HNetCfg.FwMgr") Set oProfile = oFirewall.LocalPolicy.CurrentProfile if 0 = Err.Number then On Error Goto 0 CaDebug 0, "Using path " & VbsCaCadGet("FullPath") & " for removal" oProfile.AuthorizedApplications.Remove VbsCaCadGet("FullPath") end if Err.Clear On Error Goto 0 else CaDebug 0, "Refcount is > 0... firewall entry will NOT be deleted" end if CaDebug 2, "Done" <$/VbsCaEntry> <$/VbsCa> #) ; include the Firewall.vbs code (must only be added once) dim @@FirewallBinaryExists : @@FirewallBinaryExists = "" <$Table "Binary"> <$Row @Where="Name = 'Firewall.vbs'" @Code="Y"> @@FirewallBinaryExists = <$COLSTR.Binary.Name> <$/Row> <$/Table> if @@FirewallBinaryExists = "" then say "Adding firewall vbs code to the binary table." <$@@FirewallVbs> end if #( '<?NewLine>' ;### ;### This Macro is used to create a custom action to add an application to the Windows firewall execption list (Ports are not supported). ;### Parameters: ;### Data (positional) - the name of a two, three, or four column #data block with the Parameters "FullPath", "FriendlyName", "RemoteAddresses" (optional), and "SharedComponentKey" (optional) ;### * FullPath: The full path to the application (e.g. "[INSTALLDIR]TryMy.exe") ;### * FriendlyName: A friendly name for the application, this will be shown in the exception list (e.g. "TryMe (simple MAKEMSI test MSI)" or simply "<$ProdInfo.ProductName>") ;### * RemoteAddresses: RemoteAddresses property of the INetFwAuthorizedApplication Interface, use "" or "*" to disable (see http://msdn.microsoft.com/en-us/library/aa365270(VS.85).aspx) ;### * SharedComponentKey: The KeyFile of a shared component, the firewall entry will only be removed if the reference count will be "0" after removal ;### DescriptionAdd (optional) - This will appear in any MSI log as well as progress bar line #1 when an entry is added to the exception list, defaults to "Setting up Windows firewall" ;### DescriptionRemove(optional) - This will appear in any MSI log as well as progress bar line #1 when an entry is removed from the exception list, defaults to "Setting up Windows firewall" ;### Feature (optional) - The name of the feature the generated firewall rule belongs to ;### ;### This Macro can be called serveral times to add more than one file to the exception list. The name of the supplied data field must be unique! ;### #define CaAddApplicationToFirewallExceptionList ;--- Do some parameter validations -------------------------------------- {$!KEYWORDS} ;;Don't expect any keywords {$!:#1,DESCRIPTIONADD,DESCRIPTIONREMOVE,FEATURE} ;;List all valid parameters #if ['<?Data:{$#1}>' == ''] #error ^The "{$#1}" data structure does not exist!^ #else #if [<?Data:{$#1}.?> < 2] #error ^Please pass a valid #data structure. It must have at least two rows defining the parameters "FullPath" and "FriendlyName". Parameter "RemoteAddresses" is optional.^ #endif #endif ;--- Set up the deferred custom actions ----------------------------------- ; these are the standard definitions, add to exception list on install, remove it on uninstall #define+ @@CONDITION_FIREWALL_INSTALL <$CONDITION_INSTALL_ONLY> ;;condition for install and rollback during install #define+ @@CONDITION_FIREWALL_UNINSTALL <$CONDITION_UNINSTALL_ONLY> ;;condition for uninstall and rollback during uninstall #define+ @@SharedFeature {$Feature=^^} ;;Get "feature" parameter passed by invoker #if ['<$@@SharedFeature>' = ''] ;--- No feature passed, are we nested within one -------------------- #define+ @@SharedFeature <$Feature? QUERY="Y"> #endif #if ['<$@@SharedFeature>' <> ''] ; INFO: there could be more than one feature the custom action belongs to, adding all conditions in the sequence table may exceed the maximum string length ; we create a component which belongs to all these features (this works because component<->feature dependencies are set in the FeatureComponent-table and not just in a Condition-field) ; then we can use the <$ComponentWillBe> <$ComponentIs> macros and connect our custom action to the component <$Component "{$#1}_Firewall" Feature="<$@@SharedFeature>" LM="Y" Directory_="<$AnyDir>"> ; if this action is based on features, generate if the component connected to the features will be or is installed; remove it if the component will be uninstalled or becomes advertised #define+ @@CONDITION_FIREWALL_INSTALL <$ComponentWillBe "{$#1}_Firewall" STATE="LOCAL"> #define+ @@CONDITION_FIREWALL_UNINSTALL <$ComponentIs "{$#1}_Firewall" STATE="LOCAL"> AND (<$ComponentWillBe "{$#1}_Firewall" STATE="ABSENT"> OR <$ComponentWillBe "{$#1}_Firewall" STATE="ADVERTISED">) <$/Component> #endif ; on rollback during install <$VbsCaSetup Data="{$#1}" Binary="Firewall.vbs" Key="{$#1}_FirewallRollBackAdd" Entry="RemoveApplicationFromExceptionList" Seq="InstallFiles-" CONDITION=^<$@@CONDITION_FIREWALL_INSTALL>^ Type="Deferred Rollback System" Description="{$DescriptionRemove='Setting up Windows firewall'}"> ; on install <$VbsCaSetup Data="{$#1}" Binary="Firewall.vbs" Key="{$#1}_FirewallAdd" Entry="AddApplicationToExceptionList" Seq="InstallFiles-" CONDITION=^<$@@CONDITION_FIREWALL_INSTALL>^ Type="Deferred System" Description="{$DescriptionAdd='Setting up Windows firewall'}"> ; on uninstall <$VbsCaSetup Data="{$#1}" Binary="Firewall.vbs" Key="{$#1}_FirewallRemove" Entry="RemoveApplicationFromExceptionList" Seq="<-RemoveFiles" CONDITION=^<$@@CONDITION_FIREWALL_UNINSTALL>^ Type="Deferred System" Description="{$DescriptionRemove}"> ; on rollback during uninstall <$VbsCaSetup Data="{$#1}" Binary="Firewall.vbs" Key="{$#1}_FirewallRollBackRemove" Entry="AddApplicationToExceptionList" Seq="<-RemoveFiles" CONDITION=^<$@@CONDITION_FIREWALL_UNINSTALL>^ Type="Deferred Rollback System" Description="{$DescriptionAdd}"> #) #NextId UNLOCK "FIREWALL.MMH" #endif ;; !FIREWALL_MMH
Note that the above uses the "$FeatureWillBe" and "FeatureIs" macros which you'll also need to define.