Securing the Java Runtime Environment

This forms part of a Java series including:

Java Deployment Rule Set and Active Directory Certificates

Java Deployment Rule Set and Self Signed Certificates

Java Runtime Environment (JRE) is used extensively in the enterprise for Rich Internet Applications (RIA) launched from the browser, and local Java-based desktop applications.

It’s difficult to support Java if every user has a different configuration, and coupled with this it’s not advisable to let users configure Java themselves due to the security implications.

Luckily its easy enough to lock down JRE settings with a few configuration files.

Create the deployment.config file

First start by creating the file: C:\Windows\Sun\Java\Deployment\deployment.config.  The file MUST exist in this location.  And paste and save the following inside the file:

deployment.system.config=file\:C\:/WINDOWS/Sun/Java/Deployment/deployment.properties
deployment.system.config.mandatory=true

The first setting points to the location of the deployment.properties file – this file will ultimately configure and lock down the Java settings.  We can store this file on a network but it’s advisable to keep it locally (see below), and due to it being a system-wide setting it should be in a per-machine location.  Hence for simplicity we keep it in the same location as the deployment.config file.

The second setting enforces that the deployment.properties file is used.  If the file cannot be located when mandatory is set to true (perhaps the file is stored on a network and the network location isn’t available) then Java will not load the application.

Create the deployment.properties file

Now we can start configuring and locking down the settings.  For JRE 7 (which is what we are locking down) we can see which Java settings are configurable.  Unfortunately not all settings are documented, but it’s a decent guide for most.

Create the file: C:\Windows\Sun\Java\Deployment\deployment.properties, then paste and save the following inside the file:

#*************************************
#************GENERAL TAB*************
#*************************************

deployment.proxy.type=PROX_TYPE_BROWSER
deployment.proxy.type.locked

deployment.javapi.cache.enabled=true
deployment.javapi.cache.enabled.locked

#*************************************
#************UPDATE TAB*************
#*************************************

deployment.expiration.check.enabled=false
deployment.expiration.check.enabled.locked

deployment.expiration.decision=NEVER
deployment.expiration.decision.locked

deployment.expiration.decision.suppression=true
deployment.expiration.decision.suppression.locked

deployment.javaws.autodownload=NEVER
deployment.javaws.autodownload.locked

#*************************************
#************SECURITY TAB*************
#*************************************

deployment.security.level=MEDIUM
deployment.security.level.locked

deployment.webjava.enabled=true
deployment.webjava.enabled.locked

deployment.user.security.exception.sites=C\:/WINDOWS/Sun/Java/Deployment/exception.sites
deployment.user.security.exception.sites.locked

#*************************************
#************ADVANCED TAB*************
#*************************************

#Start Debugging
deployment.trace=false
deployment.trace.locked
deployment.log=false
deployment.log.locked
deployment.javapi.lifecycle.exception=false
deployment.javapi.lifecycle.exception.locked
#End Debugging

#Start Java console
deployment.console.startup.mode=HIDE
deployment.console.startup.mode.locked
#End Java console

#Start Shortcut creation
deployment.javaws.shortcut=ASK_IF_HINTED
deployment.javaws.shortcut.locked
#End Shortcut creation

#Start JNLP File/MIME association
deployment.javaws.associations=ASSOCIATION_ASK_USER
deployment.javaws.associations.locked
#End JNLP File/MIME association

#Start Application installation
deployment.javaws.install=IF_HINT
deployment.javaws.install.locked
#End Application installation

#Start Secure environment execution
deployment.security.askgrantdialog.show=true
deployment.security.askgrantdialog.show.locked

deployment.security.sandbox.awtwarningwindow=true
deployment.security.sandbox.awtwarningwindow.locked

deployment.security.sandbox.jnlp.enhanced=true
deployment.security.sandbox.jnlp.enhanced.locked

deployment.security.clientauth.keystore.auto=true
deployment.security.clientauth.keystore.auto.locked

deployment.security.jsse.hostmismatch.warning=true
deployment.security.jsse.hostmismatch.warning.locked

deployment.security.https.warning.show=false
deployment.security.https.warning.show.locked
#End Secure environment execution

#Start Mixed code
deployment.security.mixcode=ENABLE
deployment.security.mixcode.locked
#End Mixed code

#Start Perform certificate revocation checks
deployment.security.revocation.check=ALL_CERTIFICATES
deployment.security.revocation.check.locked
#End Perform certificate revocation checks

#Start Check for certificate revocation - *bug* only locks if deployment.security.revocation.check=NO_CHECK
deployment.security.validation.crl=true
deployment.security.validation.crl.locked

deployment.security.validation.ocsp=true
deployment.security.validation.ocsp.locked
#End Check for certificate revocation

#Start Advanced security settings
deployment.security.browser.keystore.use=true
deployment.security.browser.keystore.use.locked

deployment.security.blacklist.check=true
deployment.security.blacklist.check.locked

deployment.security.password.cache=true
deployment.security.password.cache.locked

deployment.security.SSLv2Hello=false
deployment.security.SSLv2Hello.locked

deployment.security.SSLv3=true
deployment.security.SSLv3.locked

deployment.security.TLSv1=true
deployment.security.TLSv1.locked

deployment.security.TLSv1.1=false
deployment.security.TLSv1.1.locked

deployment.security.TLSv1.2=false
deployment.security.TLSv1.2.locked
#End Advanced security settings

#Start Miscellaneous
deployment.system.tray.icon=false
deployment.system.tray.icon.locked
#End Miscellaneous

Create the exception.sites file

Now create the file: C:\Windows\Sun\Java\Deployment\exception.sites.  This file can remain blank for now.  But adding application URLs to this list allows users to run RIAs that would normally be blocked by security checks.  An example of an entry in this file might be:

https://www.whatever.com:8443/

A limitation of this file is that it cannot accept wildcards.  I recently encountered an issue where a website would use a dynamic port range of around 20,000 ports!  So rather than add 20,000 rows to site.exceptions (not advisable!) I created a Java deployment ruleset with an Active Directory code signing certificate.  You can also create a Java deployment ruleset with a self signed certificate.  With the ruleset.xml file you can specify wildcards for the port in a single rule.

Disable updates via the registry

Finally there are a few registry values that we can, to prevent Java from automatically updating.  Bear in mind that this is for a 32-bit install of Java on a 64-bit platform, and hence the ‘Wow6432Node’ location:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\JavaSoft\Java Update\Policy]
"EnableJavaUpdate"=dword:00000000
"EnableAutoUpdateCheck"=dword:00000000
"NotifyDownload"=dword:00000000
"NotifyInstall"=dword:00000000

And finally, we’re done!

 

Java Deployment Rule Set and Self Signed Certificates

This forms part of a Java series including:

Securing Java Runtime Environment

Java Deployment Rule Set and Active Directory Certificates

Creating a Java Deployment Ruleset is actually a trivial process – it’s signing it that can be a pain in the backside.

In this post we are signing our Java Deployment Rule Set with an self signed certificate.  I am using jdk1.7.0_80 (yes, you need to download the Java Development Kit) to create my Java Deployment Rule Set.

Do NOT use a version of the JDK that has a major version that is greater than the JRE in your environment (for example if you are running Java 7 in your environment, use the Java 7 JDK and not the Java 8 JDK!).  If you do, it will appear to sign successfully but when you view the Java deployment rule set in the Java control panel you will see something like this (timestamp not available):

timestamp not available

instead of this:

timestamp java deployment ruleset

Create ruleset.xml

Create a file called ruleset.xml.  It MUST be called ruleset.xml since this is what Java specifically searches for inside the compiles JAR file.  The beauty of the ruleset.xml is that you can use wildcards for the URL and for the port.  With the exception.sites file you can’t do this.  Here is a basic example:

<ruleset version="1.0+">
	<rule>
		<id location="http://*.java.com" />
		<action permission="run" />
	</rule>
	<rule>
		<id location="https://*.java.com" />
		<action permission="run" />
	</rule>	
	<rule>
		<id />
		<action permission="block">
			<message>This applet has been blocked by Java.  Please contact your administrator for assistance.</message>
		</action>
	</rule>
</ruleset>

You obviously need to add your own URLs to this, and give them permission to “run”.  Interestingly if you have multiple versions of JRE installed, in the <action> tag you can provide a specific JRE version for that URL to use.  For example:

<action permission="run" version="1.6.0_31" force="true" />

When we set permission to “run”, the following types of RIAs are allowed to run without prompts:

  • Signed with a valid certificate from a trusted certificate authority
  • Signed with an expired certificate
  • Self-signed
  • Unsigned
  • Missing required JAR file manifest attributes

So Java deployment rule set is great for hiding some of these warnings.  A Rich Internet Application (RIA) is blocked if it is signed with a certificate that is blacklisted or known to be revoked, even though the action is set to “run”.

Also (untested) you should also pay close attention to the process flow of Java Rich Internet Applications.  In the above example, if a URL match cannot be found the RIA will be blocked.  However if we wanted default processing to continue (so that the Java exception.sites file gets processed, for example) we could either delete the rule with the “block” permission or replace it with:

<rule>
		<id />
		<action permission="default" />
</rule>

Compile DeploymentRuleset.jar

Compile ruleset.xml into a JAR file.  This MUST be called DeploymentRuleset.jar because again, this is what Java specifically looks for.  When we compile the JAR file, we should NOT specify a full path name to the ruleset.xml because Java cannot locate it within the JAR file.  So you should first ‘CD’ to c:\Alkane and then run the following command line:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jar.exe" -cvf "C:\Alkane\DeploymentRuleSet.jar" "ruleset.xml"

Create a Java Keystore

Here we generate a new Java keystore.  The keystore will include a key pair (a public key and associated private key), and will wrap the public key into an X.509 v3 self-signed certificate.

I would recommend keeping the alias name in lower case.  I’ve noticed that if you give it a mixed case alias name and attempt to verify it later (jarsigner.exe -verify) with the mixed case alias name, you’ll see a message:

This jar contains signed entries which are not signed by the specified alias(es)

Even though the jarsigner.exe -verify command will work when the alias name is specified in lower case, I’d recommend keeping the alias name lower case throughout for consistency.

You can grab the distinguished name (dname) parameter for your environment by running this line of PowerShell:

Get-ADDomain | Select DistinguishedName
"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\keytool.exe" -genkeypair -alias "selfsigned" -keystore "c:\alkane\DeploymentRulesetKeyStore.jks" -keyalg RSA -keysize 2048 -keypass "alkanecertpass" -startdate "2019/01/01 00:00:00" -validity 18262 -dname "DC=alkane,DC=solutions,DC=ad" -storepass "alkanestorepass" -storetype JKS

Sign the JAR file

Now we sign the JAR file.

Because I am signing behind a proxy, I needed to specify a proxy host and port so I could timestamp my signature.  Timestamping is an important part of the process and ensures the signed JAR will remain valid indefinitely – even after the certificate expires.

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jarsigner.exe" -keystore "c:\alkane\DeploymentRulesetKeyStore.jks" "c:\alkane\DeploymentRuleSet.jar" "selfsigned" -storepass "alkanestorepass" -storetype JKS -keypass "alkanecertpass" -tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp -J-Dhttp.proxyHost=alkanehost -J-Dhttp.proxyPort=alkaneport

Verify The Signature Of DeploymentRuleset.jar

We can verify the signature with the following command:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jarsigner.exe" -verify -verbose -certs -keystore "c:\alkane\DeploymentRulesetKeyStore.jks" -storetype JKS -storepass "alkanestorepass" "C:\Alkane\DeploymentRuleSet.jar" "selfsigned"

DeploymentRuleset.jar Testing

Now that everything is signed and verified successfully, we need to test it.

Our Java is locked down by a deployment.config file and a deployment.properties file located in C:\Windows\SUN\Java\Deployment.

As such to test the DeploymentRuleSet.jar file it’s as easy as placing it into this same location.  Then launch the Java control panel applet and head over to the security tab (this may differ for later versions of Java).

There should be a blue link towards the bottom called ‘View the active Deployment Ruleset’.  Click it and you should see the contents of your ruleset.xml file and a verification that it is valid and timestamped.  Click ‘View Certificate Details’ to verify the certificate.

Of course if you were testing browser redirection specifically (assuming you have JRE 1.7.0_51 and JRE 1.6.0_31 installed locally), you could add a rule like this:

<rule>
		<id location="https://*.java.com" />
		<action permission="run" version="1.6.0_31" force="true" />
</rule>

By default Java in the browser should use the latest version installed (1.7.0_51 in our case) but the rule above forces it to load any applets on java.com with version 1.6.0_31.  Once this rule is in place, head over to the Java verify page and ensure it shows the ‘older’ version of Java!

Java Deployment Rule Set and Active Directory Certificates

This forms part of a Java series including:

Securing Java Runtime Environment

Java Deployment Rule Set and Self Signed Certificates

Creating a Java Deployment Ruleset is actually a trivial process – it’s signing it that can be a pain in the backside.

In this post we are signing our Java Deployment Rule Set with an Active Directory code signing certificate.  I am using jdk1.7.0_80 (yes, you need to download the Java Development Kit) to create my Java Deployment Rule Set.

Do NOT use a version of the JDK that has a major version that is greater than the JRE in your environment (for example if you are running Java 7 in your environment, use the Java 7 JDK and not the Java 8 JDK!).  If you do, it will appear to sign successfully but when you view the Java deployment rule set in the Java control panel you will see something like this (timestamp not available):

timestamp not available

instead of this:

timestamp java deployment ruleset

Create ruleset.xml

Create a file called ruleset.xml.  It MUST be called ruleset.xml since this is what Java specifically searches for inside the compiles JAR file.  The beauty of the ruleset.xml is that you can use wildcards for the URL and for the port.  With exception.sites you can’t do this.  Here is a basic example:

<ruleset version="1.0+">
	<rule>
		<id location="http://*.java.com" />
		<action permission="run" />
	</rule>
	<rule>
		<id location="https://*.java.com" />
		<action permission="run" />
	</rule>	
	<rule>
		<id />
		<action permission="block">
			<message>This applet has been blocked by Java.  Please contact your administrator for assistance.</message>
		</action>
	</rule>
</ruleset>

You obviously need to add your own URLs to this, and give them permission to “run”.  Interestingly if you have multiple versions of JRE installed, in the <action> tag you can provide a specific JRE version for that URL to use.  For example:

<action permission="run" version="1.6.0_31" force="true" />

When we set permission to “run”, the following types of RIAs are allowed to run without prompts:

  • Signed with a valid certificate from a trusted certificate authority
  • Signed with an expired certificate
  • Self-signed
  • Unsigned
  • Missing required JAR file manifest attributes

So Java deployment rule set is great for hiding some of these warnings.  A Rich Internet Application (RIA) is blocked if it is signed with a certificate that is blacklisted or known to be revoked, even though the action is set to “run”.

Also (untested) you should also pay close attention to the process flow of Java Rich Internet Applications.  In the above example, if a URL match cannot be found the RIA will be blocked.  However if we wanted default processing to continue (so that the Java exception.sites file gets processed, for example) we could either delete the rule with the “block” permission or replace it with:

<rule>
		<id />
		<action permission="default" />
</rule>

Compile DeploymentRuleset.jar

Compile ruleset.xml into a JAR file.  This MUST be called DeploymentRuleset.jar because again, this is what Java specifically looks for.  When we compile the JAR file, we should NOT specify a full path name to the ruleset.xml because Java cannot locate it within the JAR file.  So you should first ‘CD’ to c:\Alkane and then run the following command line:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jar.exe" -cvf "C:\Alkane\DeploymentRuleSet.jar" "ruleset.xml"

Obtain a Code Signing Certificate

Follow this article on obtaining a code signing certificate from Active Directory Certificate Services.  Essentially you enable the code signing template in Certificate Services Manager, you then grant permissions to allow a user (yourself!) to request a Code Signing certificate, and you then you request a new certificate.

Export a Code Signing Certificate in PFX format

So now we can assume you’ve followed the steps above and you have a code signing certificate.  My code signing certificate forms a chain of:

  • Root certificate
  • Intermediate certificate
  • Code signing certificate

So let’s export them all in PFX format.  A PFX will include all certificates in the chain, as well as the private key.  And we need all this to create our Java keystore.  So:

From the Certificates MMC snap-in, navigate to your Personal certificate store.

Right-click the certificate > All Tasks > Export.

Click Next

Click ‘Yes, export the private key’

You are only given the option to export it as a PFX certificate.  This is fine, but make sure you select ‘Include all certificates in the certification path if possible’ and also ‘Export all extended properties’.  Click next.

Specify a password for the private key – let’s use alkanecertpass for this example.  Click next.

Provide a path to save your certificate in – let’s save it as C:\Alkane\Alkane.pfx.  Click next.  Click finish.

A .pfx file uses the same format as a .p12 or PKCS12 file. They are used to bundle a private key with its X.509 certificate(s) and to bundle all the members of a chain of trust.

Get the Alias Name of the PFX

Run the following command to get the alias name for your PFX.  We need this later on:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\keytool.exe" -list -storetype PKCS12 -keystore "C:\Alkane\Alkane.pfx" -storepass "alkanecertpass" -v

My PFX alias name is: alkanealias

Just a note – if you decide to change the alias name by using a command line similar to:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\keytool.exe" -changealias -alias ...

I would recommend keeping the alias name in lower case.  I’ve noticed that if you give it a mixed case alias name and attempt to verify it later (jarsigner.exe -verify) with the mixed case alias name, you’ll see a message:

This jar contains signed entries which are not signed by the specified alias(es)

Even though the jarsigner.exe -verify command will work when the alias name is specified in lower case, I’d recommend keeping the alias name lower case throughout for consistency.

Create a JKS Java Keytore

Now we can create our Java keystore from our PFX file.  There are a few formats we can create the keystore in (JKS, PKCS12, JCEKS for example) but we will create it in JKS format.  Be mindful that:

  • If you create your keystore in PKCS12 format, -deststorepass and -destkeypass should be the same
  • Change -deststoretype to PKCS12 if you require a keystore in PKCS12 format
  • Be careful if you change the -destalias from the -srcalias since when I verified my signed JAR file i got the following message:
    This jar contains signed entries which are not signed by the specified alias(es).

I keep the Java keystore alias name the same like so:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\keytool.exe" -importkeystore -srckeystore "C:\Alkane\Alkane.pfx" -srcstoretype pkcs12 -srcstorepass "alkanecertpass" -srcalias "alkanealias" -destkeystore "C:\Alkane\trusted.certs" -deststoretype JKS -deststorepass "alkanestorepass" -destkeypass "alkanestorepass" -destalias "alkanealias"

This will create: C:\Alkane\trusted.certs

Add Trusted Root Certificate

By default when we sign the JAR file it looks in the cacerts file (which is a form of keystore called a ‘truststore’ – see the difference between a keystore and a truststore) of the signing JDK (in my example, C:\Program Files (x86)\Java\jdk1.7.0_80\jre\lib\security\cacerts) to see if the root certificate is trusted. Because it isn’t in there it deems the root certificate as being untrusted and you will see errors when you verify such as:

  • The signer’s certificate chain is not validated.
  • CertPath not validated: Path does not chain with any of the trust anchors

So we add the root certificate as a trusted certificate in our own keystore like so:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\keytool.exe" -importcert -file "C:\Alkane\Root.cer" -alias "AlkaneRoot" -keystore "c:\Alkane\trusted.certs" -storepass "alkanestorepass" -noprompt

Sign DeploymentRuleset.jar With Java Keystore

Now we can sign the DeploymentRuleset.jar file with our Java keystore:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jarsigner.exe" -verbose -keystore "C:\Alkane\trusted.certs" "C:\Alkane\DeploymentRuleSet.jar" "alkanealias" -storepass "alkanestorepass" -tsa http://sha256timestamp.ws.symantec.com/sha256/timestamp -J-Dhttp.proxyHost=alkanehost -J-Dhttp.proxyPort=alkaneport

Because I am signing behind a proxy, I needed to specify a proxy host and port so I could timestamp my signature.  Timestamping is an important part of the process and ensures the signed JAR will remain valid indefinitely – even after the certificate expires.

Verify The Signature Of DeploymentRuleset.jar

Now we need to verify the signature of the JAR file against out keystore.  Change the storetype parameter to PKCS12 for PKCS12 store types:

"C:\Program Files (x86)\Java\jdk1.7.0_80\bin\jarsigner.exe" -verify -verbose -certs -keystore "C:\Alkane\trusted.certs" -storetype JKS -storepass "alkanestorepass" "C:\Alkane\DeploymentRuleSet.jar" "alkanealias"

DeploymentRuleset.jar Testing

Now that everything is signed and verified successfully, we need to test it.

I’m not entirely sure this step is necessary for Active Directory certificates (since the root and intermediate certificates are already in the relevant trusted stores) but it’s probably relevant for externally purchased certificates.  In any event, at the moment our Java environment is fairly locked down, and we have deployment.config, deployment.properties and exception.sites located in C:\Windows\SUN\Java\Deployment.

I needed to add a reference to our keystore, so I added an entry to deployment.properties like so:

deployment.system.security.trusted.certs=C\:/WINDOWS/Sun/Java/Deployment/trusted.certs

and then I lumped our new trusted.certs file into the same folder.  We use the ‘system’ context trusted.certs (deployment.system) instead of the ‘user’ context (deployment.user) because users may have already trusted their own specific sites and we don’t want to overwrite these.

To test the DeploymentRuleSet.jar file it’s as easy as placing it into this same location (C:\WINDOWS\Sun\Java\Deployment).  Then launch the Java control panel applet and head over to the security tab (this may differ for later versions of Java).

There should be a blue link towards the bottom called ‘View the active Deployment Ruleset’.  Click it and you should see the contents of your ruleset.xml file and a verification that it is valid and timestamped.  Click ‘View Certificate Details’ to verify the certificate.

Of course if you were testing browser redirection specifically (assuming you have JRE 1.7.0_51 and JRE 1.6.0_31 installed locally), you could add a rule like this:

<rule>
		<id location="https://*.java.com" />
		<action permission="run" version="1.6.0_31" force="true" />
</rule>

By default Java in the browser should use the latest version installed (1.7.0_51 in our case) but the rule above forces it to load any applets on java.com with version 1.6.0_31.  Once this rule is in place, head over to the Java verify page and ensure it shows the ‘older’ version of Java!

 

 

Error 16389 for .Net Framework 4.6.2 as SCCM Application

In brief, when configuring .Net 4.6.2 as an SCCM 2012 application we get an error 16389.  Even when I checked the ‘Run installation and uninstall program as 32-bit process on 64-bit clients’ (a ‘fix’ provided here) i still had the issue.  It appears it’s a memory issue when SCCM extracts the compressed .Net installer.  So here’s how I fixed it:

So, first download the offline installer from here.

Now, using 7-Zip, extract the contents of NDP462-KB3151800-x86-x64-AllOS-ENU.exe to a folder of your choice.

Now select all of the extracted content (hold down shift or ctrl to select multiple), right-click, 7-Zip and Add to Archive.

Then use settings similar to the following – ensure you check ‘Create SFX archive’ and name the archive as an executable:

Add To Archive

Click OK and it will create a self extracting executable.  By default the extraction method isn’t silent – it shows a progress bar in a GUI.  But we can make it run silently by using the following VBScript file to perform the extraction and installation:

Option Explicit

Dim objShell : Set objShell = CreateObject("Wscript.Shell")

Dim strPath : strPath = Wscript.ScriptFullName

Dim objFSO : Set objFSO = CreateObject("Scripting.FileSystemObject")

Dim objFile : Set objFile = objFSO.GetFile(strPath)

Dim strFolder : strFolder = objFSO.GetParentFolderName(objFile) 
Dim strExecutable : strExecutable = strFolder & "\W7_Microsoft_DotNet_462_EN_001_F.exe"

Dim strTempFolder : strTempFolder = objFSO.GetSpecialFolder(2)
Dim strTempExtractionFolder : strTempExtractionFolder = strTempFolder & "\netfw"

Dim strExtractionCommandLine : strExtractionCommandLine = chr(34) & strExecutable & chr(34) &  " -o" & chr(34) & strTempExtractionFolder & chr(34) & " -y"

'extract installation files to temp folder
objShell.Run strExtractionCommandLine,0,true

Dim strInstallCommandLine
Dim strInstallCommandLineExitCode : strInstallCommandLineExitCode = 0

'install dot net
If Is32BitOS() Then
	strInstallCommandLine = chr(34) & strTempExtractionFolder & "\setup.exe" & chr(34) & " /q /norestart /ChainingPackage " & chr(34) & "ADMINDEPLOYMENT" & chr(34) & " /log " & chr(34) & strTempFolder & "\netframework" & chr(34) & " /x86"
	strInstallCommandLineExitCode = objShell.Run (strInstallCommandLine,0,true)
Else
	strInstallCommandLine = chr(34) & strTempExtractionFolder & "\setup.exe" & chr(34) & " /q /norestart /ChainingPackage " & chr(34) & "ADMINDEPLOYMENT" & chr(34) & " /log " & chr(34) & strTempFolder & "\netframework" & chr(34) & " /x64"
	strInstallCommandLineExitCode = objShell.Run (strInstallCommandLine,0,true)
End If

'delete extracted folder
objFSO.DeleteFolder strTempExtractionFolder, true

'close objects
Set objFile = Nothing
Set objFSO = Nothing
Set objShell = Nothing

'quit with exit code
WScript.Quit(strInstallCommandLineExitCode)

'function to see if OS is x86 or not
Function Is32BitOS()
    Const Path = "winmgmts:root\cimv2:Win32_Processor='cpu0'"
    Is32BitOS = (GetObject(Path).AddressWidth = 32)
End Function

The default .Net executable is heavily compressed.  When extracted it extracts to over 1gb!  So I would suggest it’s worthwhile compressing it like above using 7-Zip (which doesn’t compress it quite as well, admittedly.)

Finally, the detection method:

.Net Detection Method

You can find the relevant release number for your version of .Net from here.

Suppress Opt-In and First Run dialogs when launching Office 2013

So a bit of background first.  I’d been given an Office 2013 App-V application to fix.  Each time it was launched the user was prompted with ‘First Run’, ‘Opt-In’ and licensing dialogs.  I needed to suppress these prompts.  This could probably be done via GPO or initially when the application was configured and captured, via the Office Customization Tool.  But since I didn’t want to start all over again I detected the registry settings.  Some I Googled, and some I found myself.  Here they are:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\FirstRun]
"BootedRTM"=dword:00000001
"disablemovie"=dword:00000001

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\General]
"shownfirstrunoptin"=dword:00000001

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\PTWatson]
"PTWOptIn"=dword:00000000

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common]
"qmenable"=dword:00000000

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Registration]
"AcceptAllEulas"=dword:00000001

[HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\General]
"ShownFileFmtPrompt"=dword:00000001

 

Google Chrome 49.0.2623.112

I’ve been configuring an old version of Google Chrome recently (googlechromestandaloneenterprise.msi), for a legacy Windows XP environment! (gasp).

Anyway, here’s my recipe.

We have manually created a master.preferences file:

{
"session": {
    "restore_on_startup": 4,
    "startup_urls": [
    "http://www.yourhomepage.co.uk/"
    ]
},
"browser": {
    "show_home_button": true,
    "check_default_browser": false
},
"distribution": {
    "show_welcome_page": false,
    "skip_first_run_ui": true,
    "import_bookmarks": false,
    "import_history": false,
    "import_home_page": false,
    "import_search_engine": false,
    "suppress_first_run_bubble": true,
    "do_not_create_desktop_shortcut": true,
    "do_not_create_quick_launch_shortcut": true,
    "do_not_create_taskbar_shortcut": true,
    "do_not_launch_chrome": true,
    "do_not_register_for_update_launch": true,
    "make_chrome_default": false,
    "make_chrome_default_for_user": false,
    "require_eula": false,
    "suppress_first_run_default_browser_prompt": true,
    "system_level": true,
    "verbose_logging": true
},
"first_run_tabs": [
    "http://www.yourhomepage.co.uk/"
],
"homepage": "http://www.yourhomepage.co.uk/",
"homepage_is_newtabpage": false,
"sync_promo": {
    "show_on_first_run_allowed": false
}
}

Most of the entries are self explanatory.  However Google Chrome doesn’t open on the homepage when you launch the browser – instead it opens on the Startup URL, so we configured this to point to the ‘homepage’ too.

A guide for preferences can be found here:

http://www.chromium.org/administrators/configuring-other-preferences

The content of this file is in JSON format. Once you have finished editing and tweaking, ensure the JSON validates successfully – you can do this here: http://jsonlint.com/

Now we will add these ‘master preferences’ to our installer.  To do this we URL Encode the content of master.preferences file. You can do this in any URL Encoding/Decoding website such as here: http://meyerweb.com/eric/tools/dencoder/

Create a transform (MST)

Create a Property called MASTER_PREFERENCES. Paste the URL encoded master_preferences into the value field.

Find the Custom Action called BuildInstallCommand.   Look in the Target column and set: installerdata=[MASTER_PREFERENCES]

This will generate your master.preferences file at install time so that any new user profile created when Google Chrome is launched will use these settings (existing profiles will not use this preferences file). When Google Chrome is launched for the first time, user data is created here:

[LocalAppDataFolder]\Google\Chrome\User Data

I’ve also added a VBScript custom action to run towards the end of the InstallExecuteSequence (Deferred, just before InstallFinalize) to delete C:\Program Files\Google\Update and delete the services gupdate and gupdatem. This will suppress Chrome from updating.  The condition for this custom action is NOT REMOVE~=”ALL”.

On Error Resume Next

dim wshShell : Set wshShell = CreateObject("WScript.Shell")
dim fso : Set fso = CreateObject("Scripting.FileSystemObject")

dim pFiles : pFiles = wshShell.ExpandEnvironmentStrings("%PROGRAMFILES%")
dim folderToDelete : folderToDelete = pFiles & "\Google\Update"

'kill update service
wshShell.run "SC DELETE gupdate",0,true
wshShell.run "SC DELETE gupdatem",0,true

'kill update process if exists (otherwise we cannot delete Updates folder)
dim processArray  : processArray = Array("GoogleUpdate.exe")
dim process
For Each process In processArray  
	wshShell.run "TASKKILL /im " & chr(34) & process & chr(34) &  " /f /t", 0, true
Next

'have 10 attempts at deleting the file - just in case of timing issues!
Dim loopCount : loopCount = 0
Do While fso.FolderExists(folderToDelete) And loopCount < 10 
       fso.DeleteFolder folderToDelete, true 
       sleep1 1 
       loopCount = loopCount + 1 
Loop Set 

wshShell = Nothing 
Set fso = Nothing 

Sub sleep1(strSeconds) 
       Dim dteWait : dteWait = DateAdd("s", strSeconds, Now()) 
       Do Until (Now() > dteWait)
       Loop
End Sub

Note that to sleep from within a custom action, we use one of the appraches mentioned here.

I’ve also added a clean up script after uninstall since it sometimes leaves a folder called CrashReports behind:

On Error Resume Next

dim wshShell : Set wshShell = CreateObject("WScript.Shell")
dim fso : Set fso = CreateObject("Scripting.FileSystemObject")

dim pFiles : pFiles = wshShell.ExpandEnvironmentStrings("%PROGRAMFILES%")
dim folderToDelete : folderToDelete = pFiles & "\Google\CrashReports"

If fso.FolderExists(folderToDelete) Then 
	fso.DeleteFolder folderToDelete, true
End If 

'Delete Google folder if empty

folderToDelete = pFiles & "\Google"

If fso.FolderExists(folderToDelete) Then
 
	Dim objFolder : Set objFolder = fso.GetFolder(folderToDelete)
	 
	If objFolder.Files.Count = 0 And objFolder.SubFolders.Count = 0 Then
		objFolder.Delete(true)
	End If
 
End If
 
Set wshShell = Nothing
Set fso = Nothing