Adding Non-Active Directory Validation Logic to a ThinApp Package
These are instructions on how to script in validation logic to your ThinApp package using VBS scripts - since virtually, whatever you can script, you can ThinApp.
Background Information
ThinApp allows administrators to assign Active Directory groups to a ThinApp package to provide some level of validation. This is great if the package is going to be deployed to the local or wide are network (LAN or WAN) and admins don't want their ThinApp packages to find their way onto any of their user's home computers.
What about adding other types of validation such as only allowing the application to run from a certain IP range or location on the network, a specific drive letter, a drive with a specific serial number, only once on a system at any given point in time, or even a system with a specific screen resolution? For this, ThinApp supports Visual Basic Scripting and with a VBS script, any type validation one can think of can potentially be used.
Requirements
The following items and knowledge are required for use of this procedure:
- Familiarization with instructions on how to virtualize a software product via ThinApp (see, "How to Make a ThinApp Application" on the VMware ThinApp Blogs at http://blogs.vmware.com/thinapp/2008/10/how-to-make-a-t.html).
- Familiarization with Visual Basic Scripting.
NOTES:
Make sure you read through this entire document!
Script Examples
Getting into Good Practice First
In this section I'm going to define some good practices to get into as this will help explain the example validation scripts later on.
Declaring Variables:
Declaring all of the variables within your script is always a good thing to do as it assigns a spot in memory and only releases it once the script shuts down or when you manually clear the variable somewhere in the script.
You may also notice that I specify a value for a variable called "strComputer". I do this here as I need the variable defined prior to setting global variables.
NOTE: The below text may be wrapped – the DIM line is all one line.
' DECLARE VARIABLES
Dim WSHNetwork, WSHShell, objFSO, arraySearch, strSplit, objDrive, objWMIService, objProcess, colProcess, strComputer, strProcessKill, ExeName, ParentProcID, EPProcID, ParentProcIDTwo, EPProcIDTwo
strComputer = "."
Setting Global Use Variables:
Setting Global Use Variables is a good idea as you will likely utilize one of these calls at some point in your script.
Set WSHNetwork = CreateObject("WScript.Network")
Set WSHShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = CreateObject("Shell.Application")
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set objRegistry = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv")
Defining Global Script Environment Variables:
For script-wide variables which you will utilize time and again in your script, it is wise to set them once at the beginning of your script so that you may reuse them over and over without recalling the data to them (a longer process than just calling the value from memory).
' DEFINE SCRIPT ENVIRONMENT VARIABLES
Origin = GetEnvironmentVariable("TS_ORIGIN")
LastSlash = InStrRev(Origin, "\")
SourcePath = Left(Origin, LastSlash)
ExeName = Mid(Origin, LastSlash + 1, Len(Origin))
SandboxParent = GetBuildOption("SandboxPath")
SandboxName = GetBuildOption("SandboxName")
If SandboxParent = "." Then
SandboxPath = SourcePath & SandboxName
Else
SandboxPath = SandboxParent & Chr(92) & SandboxName
End If
Scripted Validation Examples
In this section I've coded some examples of different types of script validations that can be done. This is, by no means, all that you can do within a script!
Determine if Running from a UNC Path:
This code is useful in case you wish to ensure the application is only run from a drive letter and not a UNC path.
Origin = GetEnvironmentVariable("TS_ORIGIN")
Function OnFirstSandboxOwner
' -----------------------------------------
' Determine if running from a UNC path
' -----------------------------------------
IsUNC = InStr(Origin, "\\")
If IsUNC = 0 Then
' Find the drive letter which the ThinApp is running from
arraySearch = Split(Origin, "\", -1, 1)
strSplit = (arraySearch(0))
Set objDrive = objFSO.GetDrive(strSplit)
Else
MsgBox "Application must be run from a valid drive"
ExitProcess 0
End If
End Function
Note the use of the "Origin" ThinApp API call in the above code. This defines the path of where the ThinApp executable is actually running from.
The above code can also be expanded to determine the drive letter or path the ThinApp package is running from.
Determine Free Drive Space:
It may be that your application (packaged or otherwise) requires a certain amount of free disc space available to properly run. This code will check the free drive space by utilizing the File System Scripting Object.
Function OnFirstSandboxOwner
' -----------------------------------------
'Check Drive Free Space
' -----------------------------------------
Set objDrive = objFSO.GetDrive(strSplit)
If objDrive.FreeSpace > "1012903936" Then
MsgBox "FreeSpace " & objDrive.FreeSpace
Else
MsgBox "There is not enough free space, please contact Administrator"
ExitProcess 0
End If
End Function
It should be noted that the "Set objDrive = objFSO.GetDrive(strSplit)" line could actually exist in the Global Variable section of the script. Additionally, it is likely only needed once per script – so if the "UNC Path" code previously discussed is also in the script, then the "Set objDrive = objFSO.GetDrive(strSplit)" line is only needed once in the script.
Determine Drive (or Volume) Name:
The following code will check for the "<Valid Company C Drive>" name (or whatever you specify as a valid name) and run the ThinApp if it is correct.
Function OnFirstSandboxOwner
' -----------------------------------------
'Check Drive Name
' -----------------------------------------
If objDrive.VolumeName = "<Valid Company C Drive>" Then
MsgBox "Volume Name is Correct" & objDrive.VolumeName
Else
MsgBox "This system drive name is incorrect, please contact Administrator"
ExitProcess 0
End If
End Function
Obviously you'll want to ensure that each system which your ThinApp package runs on has the same volume name. This is probably a little more impractical as someone could easily circumvent this by setting their home computer to the same volume name – but this is just to provide some examples.
Ask for a Validation Code:
This code will check ask the user for a validation code and check it against the "valid code". This is another script that, in its current form, would be somewhat restrictive; but it is a good starting point and example of what validation options can be arranged within a script.
Function OnFirstSandboxOwner
' -----------------------------------------
'Ask the user for a Validation Code
' -----------------------------------------
ValidationCode = InputBox("Enter your code: ")
'Verify the Validation Code.
If Not ValidationCode = "1111" Then 'If correct, notify and run the ThinApp.
MsgBox "Invalid Validation Code: " & ValidationCode
ExitProcess 0
End If
End Function
Only Allow One Instance of a ThinApp Application to Run at a Time ONLY DURING VALIDATION:
WARNING! - This code is a bit more involved and requires additional modifications to the ThinApp package as well.
NOTE: This code utilizes the "Determine if Running from a UNC Path" code.
This piece of scripting will only allow one instance of a ThinApp packaged application to be executed at time during the validation phase. Once validation is successful, any number of instances of the ThinApp application can be launched. You may have just realized that using this code in conjunction with other validation logic is a must as this will prevent the circumvention of the OnFirstSandboxOwner validation logic code.
Explanation: When you utilize a single bit of validation code in the OnFirstSandboxOwner function of a ThinApp script, at the point of a prompt, the ThinApp executable can be launched a second time and will immediately bypass the validation stored in OnFirstSandboxOwner. This, actually, is by design because of the fact the four callback functions are timing points within ThinApp and not specifically for use with security functions and validation logic. Simply put, launching the entry point (any entry point into the same package) a second time while the first entry point is running will no longer execute the OnFirstSandboxOwner. Any validation logic in the OnFirstSandboxOwner callback function will not be executed for the second application (even if it's the same entry point).
To properly accommodate for this, we use the below script code and package modifications to prevent the launching of a second entry point (or relaunching of the same entry point) during the validation phase of the VB script. You'll also notice that similar code exists in both the OnFirstSandboxOwner and in the OnFirstParentStart ThinApp callback functions. This is because we're ensuring that similar processes occur for both the first launch of the application and subsequent and simultaneous launches of the application.
NOTE: The below script code assumes you have properly set and declared script variables prior to execution of this code. You may also wish to replace the "& SandboxName &" code with something such as a company name.
Function OnFirstSandboxOwner
' -------------------------------------------------------------------
' Search for HKCU registry keys to record process information and create it if not found in VOS
' ------------------------------------------------------------------------------------
objRegistry.GetStringValue &H80000001,"SOFTWARE\" & SandboxName,"ParentProcID",dwValue
If IsNull(dwValue) Then
objRegistry.CreateKey &H80000001,"SOFTWARE\" & SandboxName
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID", EPProcID, "REG_SZ"
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID", ParentProcID, "REG_SZ"
End If
' -------------------------------------------------------------------
' Search for process already running in background. If not found, write process id and parent process id to virtual registry
' -------------------------------------------------------------------
ParentProcID = WSHShell.RegRead("HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID")
EPProcID = WSHShell.RegRead("HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID")
' Fix up Numeric Values
If IsNumeric(ParentProcID) Then
ParentProcID = Abs(Fix(ParentProcID))
End If
If IsNumeric(ParentProcID) Then
EPProcID = Abs(Fix(EPProcID))
End If
Set colProcess = objWMIService.ExecQuery ("Select * from Win32_Process Where Name = " & "'" & ExeName & "'")
If ParentProcID = "" And EPProcID = "" Then
For Each objProcess In colProcess
ParentProcID = Abs(Fix(objProcess.ParentProcessId))
EPProcID = Abs(Fix(objProcess.ProcessId))
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID", EPProcID, "REG_SZ"
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID", ParentProcID, "REG_SZ"
Next
Else
For Each objProcess In colProcess
If ParentProcID <> Abs(Fix(objProcess.ParentProcessId)) Or EPProcID <> Abs(Fix(objProcess.ProcessId)) Then
MsgBox "This application '" & ExeName & "' is already running and cannot be ran more than once at a time during validation.",, ExeName & " is already running!"
ExitProcess 0
End If
Next
End If
' -------------------------------------------------------------------
' -----------------------------------------
' Determine if running from a UNC path
' -----------------------------------------
IsUNC = InStr(Origin, "\\")
If IsUNC = 0 Then
' Find the drive letter which the ThinApp is running from
arraySearch = Split(Origin, "\", -1, 1)
strSplit = (arraySearch(0))
Set objDrive = objFSO.GetDrive(strSplit)
Else
MsgBox "Application must be run from a valid drive"
ExitProcess 0
End If
End Function
Function OnFirstParentStart
' -------------------------------------------------------------------
' Search for process already running in background. If not found, write process id and parent process id to virtual registry
' -------------------------------------------------------------------
ParentProcID = WSHShell.RegRead("HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID")
EPProcID = WSHShell.RegRead("HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID")
' Fix up Numeric Values
If IsNumeric(ParentProcID) Then
ParentProcID = Abs(Fix(ParentProcID))
End If
If IsNumeric(ParentProcID) Then
EPProcID = Abs(Fix(EPProcID))
End If
Set colProcess = objWMIService.ExecQuery ("Select * from Win32_Process Where Name = " & "'" & ExeName & "'")
If ParentProcID = "" And EPProcID = "" Then
For Each objProcess In colProcess
ParentProcID = Abs(Fix(objProcess.ParentProcessId))
EPProcID = Abs(Fix(objProcess.ProcessId))
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID", EPProcID, "REG_SZ"
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID", ParentProcID, "REG_SZ"
Next
Else
For Each objProcess In colProcess
If ParentProcID <> Abs(Fix(objProcess.ParentProcessId)) Or EPProcID <> Abs(Fix(objProcess.ProcessId)) Then
MsgBox "This application '" & ExeName & "' is already running and cannot be ran more than once at a time during validation.",, ExeName & " is already running!"
ExitProcess 0
End If
Next
End If
' -------------------------------------------------------------------
' -----------------------------------------
' Determine if running from a UNC path
' -----------------------------------------
IsUNC = InStr(Origin, "\\")
If IsUNC = 0 Then
' Find the drive letter which the ThinApp is running from
arraySearch = Split(Origin, "\", -1, 1)
strSplit = (arraySearch(0))
Set objDrive = objFSO.GetDrive(strSplit)
Else
MsgBox "Application must be run from a valid drive"
ExitProcess 0
End If
End Function
Function OnLastProcessExit
' -------------------------------------------------------------------
' Clean up after duplicate process searching if RemoveSandboxOnExit=1 not set in PACKAGE.INI
' -------------------------------------------------------------------
ParentProcID = ""
EPProcID = ""
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\EPProcID", EPProcID, "REG_SZ"
WSHShell.RegWrite "HKEY_CURRENT_USER\SOFTWARE\" & SandboxName & "\ParentProcID", ParentProcID, "REG_SZ"
' -------------------------------------------------------------------
End Function
ATTENTION! – The above script code also requires modification of the package's HKEY_CURRENT_USER.TXT file and creation of the below lines. Also, replace "<SandboxName>" with a valid Sandbox Name.
Value=EPProcID
REG_SZ=#00
Value=ParentProcID
REG_SZ=#00
WARNING! – Ensure when modifying any ThinApp package registry TXT file that you only leave one blank line between entries.
Example:
Example VBS Script with above examples and suggestions:
Comments