Re: Creating MSI for installing .NET security policies



just create a installer project - add a class that derives from Installer - override Install/Uninstall - where's the hack??

[RunInstaller(true)]
public class PolicyInstaller : Installer
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

string permissionSetName = "Acme Permissions";
string permissionSetDesc = "This is the set of permissions needed by ..NET applications for Acme corporation";
string codeGroupName = "Acme Code Group";
string codeGroupDesc = "Grants a few extra permissions to any assembly signed with the Acme strong name";

public PolicyInstaller()
{
// This call is required by the Designer.
InitializeComponent();
}

#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion

// this code will run when the MSI file is installed
public override void Install(IDictionary stateSaver)
{

// first need to find the machine policy,
// which is where we'll make our changes
PolicyLevel machinePolicy = _findPolicyLevel(PolicyLevelType.Machine);

if (null == machinePolicy)
{
// sanity check - this should never happen
throw new ApplicationException("Failed to find the machine policy in the PolicyHierarchy");
}

// we need to add a named permission set
// that includes whatever permissions we're granting
NamedPermissionSet nps = new NamedPermissionSet(permissionSetName, PermissionState.None);
nps.Description = permissionSetDesc;

// TODO: add some permissions
nps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, @"c:\acme\expenses"));
nps.AddPermission(new EnvironmentPermission(EnvironmentPermissionAccess.Read, "EXPENSE"));
nps.AddPermission(new SqlClientPermission(PermissionState.Unrestricted));
nps.AddPermission(new DataProtectionPermission(PermissionState.Unrestricted));

// add our named permission set to the machine policy level
// note that nothing is saved yet (we'll save at the end)
try
{
machinePolicy.AddNamedPermissionSet(nps);
}
catch
{
// duplicate name - update the existing one with the same name
machinePolicy.ChangeNamedPermissionSet(nps.Name, nps);
}

// create code group
CodeGroup cg = new UnionCodeGroup(
new StrongNameMembershipCondition(
new StrongNamePublicKeyBlob(acmePublicKey),
null, // match regardless of assembly's simple name
null), // match regardless of assembly's version
new PolicyStatement(nps,
PolicyStatementAttribute.Nothing) // no LevelFinal or Exclusive attribute on this code group
);
cg.Name = codeGroupName;
cg.Description = codeGroupDesc;

// code groups with duplicate names are legal, but messy and confusing,
// so we make sure to first remove any existing code groups with our name
_removeCodeGroupsByName(machinePolicy.RootCodeGroup, cg.Name);

// add our new code group (note we've not saved yet).
machinePolicy.RootCodeGroup.AddChild(cg);

// finally, save all changes atomically.
SecurityManager.SavePolicyLevel(machinePolicy);
}

// this code will run when our MSI is uninstalled
public override void Uninstall(IDictionary savedState)
{
// first need to find the machine policy,
// which is where we'll make our changes
PolicyLevel machinePolicy = _findPolicyLevel(PolicyLevelType.Machine);

if (null == machinePolicy)
{
// sanity check - this should never happen
throw new ApplicationException("Failed to find the machine policy in the PolicyHierarchy");
}

// remove our code group
_removeCodeGroupsByName(machinePolicy.RootCodeGroup, codeGroupName);

// remove our named permission set
try
{
machinePolicy.RemoveNamedPermissionSet(permissionSetName);
}
catch
{
// if it's not there, no biggie - just doing our best to clean up.
}

// finally, save all changes atomically.
SecurityManager.SavePolicyLevel(machinePolicy);
}

PolicyLevel _findPolicyLevel(PolicyLevelType policyLevel)
{
IEnumerator policyLevelEnumerator = SecurityManager.PolicyHierarchy();
PolicyLevel found = null;
while (policyLevelEnumerator.MoveNext())
{
PolicyLevel lvl = (PolicyLevel)policyLevelEnumerator.Current;
if (lvl.Type == policyLevel)
{
found = lvl;
}
}
return found;
}

void _removeCodeGroupsByName(CodeGroup parent, string childName)
{
// the implementation of CodeGroup.Children (viewed with Anakrino)
// gives a deep copy of the children, so technically this is overkill,
// but the documentation doesn't guarantee this, so we enumerate
// then delete in two separate steps as you would with any dynamic list
ArrayList codeGroupsToRemove = new ArrayList();
foreach (CodeGroup existingCodeGroup in parent.Children)
{
if (childName == existingCodeGroup.Name)
{
codeGroupsToRemove.Add(existingCodeGroup);
}
}
foreach (CodeGroup cg in codeGroupsToRemove)
{
parent.RemoveChild(cg);
}
}

// public key from AcmeExpense.exe using the secutil tool
byte[] acmePublicKey = new byte[]{ 0, 36, 0, 0, 4, 128, 0, 0, 148, 0, 0, 0,
6, 2, 0, 0, 0, 36, 0, 0, 82, 83, 65, 49, 0, 4, 0, 0, 1, 0, 1, 0, 85, 73, 181, 56, 224, 255, 198, 167, 151, 206, 111, 135,
63, 32, 172, 222, 77, 52, 224, 240, 213,
218, 202, 79, 18, 100, 175, 171, 17, 242,
83, 200, 243, 42, 105, 75, 231, 4, 81, 164, 11, 105, 39, 193, 38, 32, 161, 246,
131, 55, 86, 216, 252, 116, 204, 12, 60,
91, 125, 46, 230, 95, 108, 13, 161, 34, 69, 98, 191, 236, 147, 208, 59, 80, 172,
98, 1, 213, 167, 105, 180, 135, 22, 231,
69, 11, 55, 252, 224, 212, 219, 86, 112,
32, 92, 118, 40, 250, 51, 173, 23, 33, 77, 179, 133, 49, 197, 137, 174, 236, 57, 143, 37, 244, 251, 98, 178, 255, 175,
135, 107, 5, 168, 106, 175, 184, 116, 206 };
}


---
Dominick Baier, DevelopMentor
http://www.leastprivilege.com

The poblem with that is that doing .NET code inside an MSI via a
custom action is a little painful as of now. MSI doesn't have native
support for .NET custom actions, only script and native DLLs (or exe
files). There are many hacks to support this, but they are all hacks
for now and have a variety of shortcomings.

I agree that the .NET classes are better in theory.

Joe K.




.