Modifying Installation Directory Security with Windows Installer

I had a ill-behaved application for which I needed to construct an MSI installation package. The problem with this application is that it requires users to have modify permissions on the installation folder (and subfolders), since it writes application data there.

So the first step was to create a custom action. Create a C# project, then Add New Item and choose the Installer Class template.

Here is the beginning of this class:

    [RunInstaller(true)]
    public partial class ClientInstaller : System.Configuration.Install.Installer
    {
        public ClientInstaller()
        {
            InitializeComponent();
        }

        [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand)]
        public override void Install(IDictionary stateSaver)
        {
            base.Install(stateSaver);
            SetDirectorySecurity();
        }

Then, right-click on the setup project, choose View –> Custom actions to display the custom actions window. In the Install folder, add Primary output from the C# project.

The first problem was how to get hold of the installation folder. I used a context parameter, targetdir, for this.
string targetdir = Path.GetDirectoryName(Context.Parameters["targetdir"]);
This parameter needs to be set using the CustomActionData property. In the custom actions window, select primary output from the C# project, and set the CustomActionData property to
/targetdir="[TARGETDIR]\"
Yes, the double quotation marks and the backslash are needed.

The second problem was how to actually add the permissions. I found (part of) the answer in a forum post by “Jaso”: http://www.aspnet-answers.com/microsoft/NET-Security/30001760/how-to-change-group-permissions-on-existing-folder.aspx. The  problem with this code is that the statement “So just use ‘objectinherit and the child objects (even created at a later time in sub directories) inherit the permission correctly.” is false. You need to use both ObjectInherit and ContainerInherit to make sub folders inherit the same permission. Here is my complete SetDirectorySecurity:

        private void SetDirectorySecurity()
        {
            string targetdir = Path.GetDirectoryName(Context.Parameters["targetdir"]);
            Log("Adding modify rights for " + targetdir);
            // The following code was copied (and modified) from "Jaso" (http://www.aspnet-answers.com/microsoft/NET-Security/30001760/how-to-change-group-permissions-on-existing-folder.aspx).
            // Retrieve the Directory Security descriptor for the directory
            var dSecurity = Directory.GetAccessControl(targetdir, AccessControlSections.Access);
            // Build a temp domainSID using the Null SID passed in as a SDDL string.
            // The constructor will accept the traditional notation or the SDDL notation interchangeably.
            var domainSid = new SecurityIdentifier("S-1-0-0");
            // Create a security Identifier for the BuiltinUsers Group to be passed to the new accessrule
            var ident = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, domainSid);
            // Create a new Access Rule.
            // ContainerInherit AND ObjectInherit covers both target folder, child folder and child object.
            // However, when using both (combined with AND), the permissions aren't applied.
            // So use two rules.
            // Propagate.none means child and grandchild objects inherit.
            var accessRule1 = new FileSystemAccessRule(ident, FileSystemRights.Modify, InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow);
            var accessRule2 = new FileSystemAccessRule(ident, FileSystemRights.Modify, InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
            // Add the access rules to the Directory Security Descriptor
            dSecurity.AddAccessRule(accessRule1);
            dSecurity.AddAccessRule(accessRule2);
            // Persist the Directory Security Descriptor to the directory
            Directory.SetAccessControl(targetdir, dSecurity);
            Log("Rights added.");
        }

        private void Log(string message)
        {
            Context.LogMessage(message);
            System.Diagnostics.Trace.WriteLine(message, "1177 client installer");
        }
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s