Tutorial: How to Use the MsiLockPermissionsEx Table (and CSI_GetSDDLFromObject Helper Script)

Windows Installer 5.0 (Windows 7) introduced several new capabilities for setting permissions. However, it is difficult to find samples of how to configure the new table and even more difficult to learn the permissions syntax (SDDLText) required to configure permissions. The information in this article and a new CSI toolkit utility make this challenging chore into an easy one.

Background

MsiLockPermissionsEx was introduced in the MSI 5.0 release which was the release for Windows 7. Previous versions of MSI simply ignore this new table.

The detection and use of the MsiLockPermissionsEx table is automatic if it exists – there is no need to add a Standard Action or for the .MSI file database version to be set as “5.0”

Converting to MsiLockPermissionsEx & One Package for XP and Win7

If, however, a package that runs on Windows 7 has BOTH the legacy table (LockPermissions) and the new one (MsiLockPermissionsEx), an error is generated and the package has a hard failure. Conditions cannot be used to avoid this behavior as the check does not happen ONLY IF the MsiLockPermissionsEx table needs to be used – even a blank MsiLockPermissionsEx table causes the error.

This is an unfortunate design as it creates significant problems for designing a single package (that sets permissions) to run on Windows XP through Windows 7. I think it would have been a much more flexible approach to simply have Windows 7 ignore the legacy table when both are present and note this fact in the log when it occurs. Another approach would have been to only generate the error if rows in MsiLockPermissionsEx targeted objects that were also targeted by LockPermissions – that may have allowed both tables to be processed when just a few new capabilities (like setting service permissions) were needed by a package that is otherwise working fine with its legacy LockPermissions table.

As it is, converting an existing LockPermissions table to an MsILockPermissionsEx implementation could be costly since it involves converting all existing rows and, if the package needs to target XP as well.

There are two primary approaches for supporting both LockPermissions and MsiLockPermissionsEx in one package.

Just keep using LockPermissions and any custom actions you are using until you no longer have to support XP. LockPermissions is still supported on Windows 7.

Implement MsiLockPermissionsEx in a transform. The transform would contain your MsiLockPermissionsEx table as well as an entry to delete (Drop) the LockPermissions table. We have tested this approach and it effectively avoids the error about having both tables in the package. For administrative packagers who have control over the deployment command line, this approach is not difficult. If you are a commercial software developer packager, you would need to make sure you have wrapper logic that detects Windows 7 and implements the transform.

MsiLockPermissionsEx Table

Here are some notes about the MsiLockPermissionsEx table and how each column should be used:

MsiLockPermissionsEx – The primary key to the table. Similar to the Registry table this value can be arbitrarily made up as long as it is unique within this column in the current .MSI file.

LockObject – A link to one of four other tables, which indicates which type of object the permissions are being set on. This column can link to the File table, Registry table, CreateFolder table or ServiceInstall table. Each of these tables has a “primary key” column which is also arbitrarily assigned when the package is built (either by the packaging tool used to build the package or the package developer who built the package). You must locate a row in one of these tables that contains the target object you wish to set permission on and then enter its primary key value into this column.

Table – indicate the table name in which the target object is specified (value values: “File”, “Registry”, “CreateFolder”, “ServiceInstall”) References to tables in SQL are case sensitive – so always preserve the case.

Condition – a standard formatted MSI condition statement (optional).

SDDLFormattedText – a permissions text string that follows the SDDLText syntax. This is the heart of the new permissions mechanism.

What is SDDLText ?

SDDL stands for “Security Descriptor Definition Language”. It is a method for specifying permissions on the Windows OS. Instead of using permissions bitmasks (like the LockPermissions table and most of the Win32 API) it uses a text string with a specific syntax to indicate what permissions to apply to objects. Although it is a big improvement over calculating permission bitmasks, it ends up being a fairly complex “language” in order to support the many permissions available on Windows.

SDDLText vs. Windows Installer FormattedSDDLText

MSI “enhances” the standard SDDLText strings so that they can use the similar types of variable references available in LockPermissions (with the exception of using a property). Any substitutions must be surrounded by angle brackets “<>”.

Any references to a user or group can use:

Standard SDDL Support:

SDDL Aliases for well know groups.

SIDS

MSI SDDLFormattedText Support:

Literal Text – for example: “<Domainname\UserORGroup>”

Unqualified User or Group Name “<Users>” – this searches for the user or group – first on the local machine, then the user’s domain then the workstation’s domain (If different than user domain).

Environment Variable in MSI Formatted Text Format – for example: “<[%USERDOMAIN]\[%USERNAME]>. This method is most useful when you have a standard group across multiple user account domains in a diverse organization. Keep in mind the USERDOMAIN environment variable is for whomever started the package - this will normally be your Software Distribution System’s “Installation User” profile.

The square brackets with a leading percent is the standard MSI “FormattedText” reference for retrieving an environment variable. DO NOT use a trailing percent.

IMPORTANT: Curiously a property cannot be used for either the domain or user.

(Please ensure that the confirmation email clears your spam filter so that you will see future mailings.)

With Windows security, anywhere a “Domain” can be used, a workstation name can be used for referencing local groups and anywhere a “UserID” can be used, a group name can be used as well.

If the domain is not the local machine, then when Windows Installer requests the permissions to be set by the Windows Operating System, the OS will need to be able to contact a domain controller for the domain during the install or else the install will fail.

CSI_GetSDDLFromObject.vbs Features and Benefits

As of this writing, your authoring tool may not yet support a GUI for setting permissions in this new table. I had to ask myself “Why can’t I just use my familiar old tools to set the permissions?” That question led to CSI_GetSDDLFromObject.vbs. This script simply exacts the existing permissions and formats them as an SDDLText string for easy copying of it (or portions of it) to the MsiLockPermissionsEx table.

Here is a feature overview:

Supported Objects - It can extract SDDL from every object type that MsiLockPermissionsEx supports (Files, Folders, Registry Keys and Services).

Flexible Registry Syntax - For the registry it takes both long and short hive references (e.g. “HKLM” or “HKEY_LOCAL_MACHINE”).

Flexible Services Syntax - For services it takes both the long service name (seen in services control panel) and the registry name. (e.g. “Adaptive Brightness” or “SensrSvc”)

Command line or Interactive – the script can run from the command line which will output the SDDL to the command window. It can also be run with wscript.exe which makes it very easy to copy the SDDLText with CTRL-C. When using wscript.exe, if the target object is not provided on the command line, you are prompted for it.

Elevated Admin Rights on Windows 7 – services and certain file / registry permission retrievals require administrator rights. To keep things simple the script requires elevated admin rights to run. To use it interactively, start an elevated command prompt and run the script name or precede it with “wscript.exe”

Converting From Secedit Security Templates

Some packagers use Secedit security templates to apply permissions to their packages. These templates actually use SDDL – so conversion from these solutions should be fairly easy. Obviously you lose the advantage of being able to run audit reports and reapply permissions with secedit.exe long after a package has been installed.

Cautions and Tips

Folder Permissions – if you are setting permissions on a folder, it must have an entry in the CreateFolders table to explicitly create the folder. The CreateFolders table is processed by the CreateFolders standard action which may also need to be added to your package. This standard action should be between IntallInitialize and InstallFinalize (before InstallFiles and CopyFiles if they exist in your package). This is the same requirement as the legacy LockPermissions table and is noted in the MSI subsection of the SDK under “Securing Resources” – but it is easy to miss if you are simply adding permissions to a package that has never had the standard LockPermissions table configured.

Avoid Using Domain Accounts When Possible – When the Windows OS (not Windows Installer) sets permissions on objects using domain based accounts, it must contact the domain. This creates a dependency in your package on being network connected with connectivity to a domain controller for the domain in question.

Overwrites Existing Permissions – MsiLockPermissionsEx can preserve (or block or copy) inherited permissions. This is a big upgrade from LockPermissions which overwrote all permissions. However, it does not technically do an “insert” of permissions when upgrading / overwriting existing resources. So if custom permissions exist on an object (say a service) from another package or a previous run of your package, they will be overwritten by MsiLockPermissionsEx entries in the latest MSI to run against the object.

Does Not Support Properties – formatted data types in MSI usually allow properties to be specified (in addition to literal text and environment variables). This was how it worked with LockPermissions. When testing for this article I found that I could not user a PUBLIC or Private property (Set by Custom Action or Property Table) for the User or domain name. Literal text and environment variables work.

Use An Unqualified User ID for Multiple Domain Support – When an unqualified user name is used in Windows security, Windows searches for the “security principal” – first the local machine, then the user’s domain, then the domain the computer is a member of (if different than the User’s domain). As long as the group does not exist on any of your workstations, it should be found on the user’s domain. As usual, only use domain based groups in the LockPermissions or MsiLockPermissionsEx table as this makes successful installation of the package dependent on network and domain controller access at the time of install.

When Using One MSI Package That Uses LockPermissions for XP and MsiLockPermissionsEx for Windows 7, Implement MsiLockPermissionsEx in a Transform – As long as you can dynamically specify the command line this will allow you to drop the LockPermissions table and add in the MsiLockPermissionsEx table.

Permissions Inheritance Depends on Your SDDLText String – Windows Installer does not do anything special to ensure that your permissions are inheritable, it relies on the coding of your SDDLText string to indicate whether inheritance is desired or not. CSI_GetSDDLFromObject.vbs will take the inheritance settings that are specified when Explorer or Regedit to set permissions on the target object.

Permissions On Empty Folders Under Program Files Do Not Inherit – I have a customer report that if a folder is created under Program Files with SDDL that indicates inheritance should be on and that folder does not have any files copied to it (say it is simply an root folder for other folders that DO contain files), the permissions do not inherit. If you have many subfolders directly under an empty folder, the easiest solution is to copy a dummy file to the folder to avoid having to set permissions directly on each subfolder. This customer report indicated that the problem is isolated to folders created in a folder tree under Program Files.

(Please ensure that the confirmation email clears your spam filter so that you will see future mailings.)

What Can Go Wrong?

Here are some of the most common errors and resolutions relating the new permissions support in MSI 5.

Log file: Error 1941. Both LockPermissions and MsiLockPermissionsEx tables were found in the package. Only one of them should be present. This is a problem with the package.

This error only shows up on MSI 5 (currently Windows 7) and later – so it is possible some package authors working primarily on XP could create the new table to support Windows 7 and forget to remove the old one.

If you are using CSI_GetSDDLFromObject, the most likely cause of this message is that the username is specified incorrectly. Another possibility is that the object you copied the SDDL from contains permissions from local users or groups that do not exist on the machine where the permissions is attempting to be applied.

Logging of Successful Permission Application

To audit the successful application of permissions from MsiLockPermissionsEx, search the log for the keyword “SDDLText” The SDDLText= will be appended to the “Executing op” log line of the respective operation depending on what resource the permissions are set on. File permissions have a new Op called “FileCopySDDL”.

Tutorial: How to Add MsiLockPermissionsEx Support to a Package

This tutorial uses Orca. Your authoring tool may not yet (or ever) have a GUI for setting these permissions – so you will need to use it’s table editor which will be very similar to Orca. Even if your authoring tool does not yet support this MSI 5 table, it should allow you to create this table as a custom table in your project file (.WSI for Wise and .ISM for InstallShield).

ATTENTION: You must use Orca version 5 which ships with the latest SDK so that the schema will include the description of the MsiLockPermissionsEx table.

Step 1 – Remove Legacy LockPermissions Table.

If both LockPermissions and MsiLockPermissionsEx are in the same package – the package will fail with an error when run on Windows 7 or later.

Step 2 – Add the MsiLockPermissionsEx Table.

From the main menu bar select Table and then Add Table. Select MsiLockPermissionsEx and click OK. (MsiLockPermissionsEx will only appear in this list for Orca 5 and later.)

Step 3 – Install the Software.

In order to use the standard Windows tools to set permissions on application objects, the application must be installed.

Step 4 – Use Regedit to Set Registry Permissions.

Select the desired application key and edit the permissions. By default the permissions you apply will apply to the registry key and and will be inherited by sub-keys created by Windows Installer during the install.

Step 4 – Granting Authenticated Users Full Control.

In this example we will simply give Authenticated Users full permissions.

(Please ensure that the confirmation email clears your spam filter so that you will see future mailings.)

Step 5 – Copy the Key name Before Leaving Regedit.

Step 6 – From An Elevated CMD Prompt Run CSI_GetSDDLFromObject.vbs

Step 7 – At The Prompt, Paste in the Registry Key Name and Click OK.

Step 8 – When the SDDL Displays, Press CTRL-C to Copy It.

Leave the script display window up as you may need to copy the SDDL again.

Step 6 – In Orca Click the Registry Table and Locate a Reference To the Key.

For the located row, write down the value in the "Registry” column. IMPORTANT: This value is case sensitive.

If there are a lot of registry keys, select Edit > Find and type in the name of the key WITHOUT the hive (first part). If there is more than one value being set in that key, there will be more than one reference to it in the Registry table. You can pick any of them, however, be careful to reference the SAME Registry table row in future updates.

If it is not, add a row and for the Action column type “CreateFolders” (no quotes, case sensitive)

Set the Sequence column to a number that will come after InstallInitialize and any actions that remove the resources you are setting permissions on (Unregister*, Remove*) but before any standard actions that copy or move files onto the system (MoveFiles, InstallFiles, PatchFiles).

Step 14 – Add a MsiLockPermissionsEx Table Row

For LockObject specify the folder property used in the CreateFolder table.

For Table specify “CreateFolder” (NOT the Directory table).

Copy and paste the SDDL string from the earlier script run.

By default the permissions you apply will apply to the folder and all files within it and will be inherited by all files and folders created by Windows Installer during the install.