all groups > dotnet security > october 2006 >
dotnet security :
Creating MSI for installing .NET security policies
Hi folks, I'm deploying my app to a network share and I need the security permissions installed on the client. AFAICT I cannot use ClickOnce because ClickOnce deployment doesnt allow user to specify install location i.e. Network share. So I have gone down the route of creating a Strong Name key and signing my assemblies with that key, creating a new Code Group in the Microsoft .NET configuration Tool which assigns full trust to my Strong Name key, and then finally creating a deployment package MSI to allow users to install the Security Policy easily. Now, this all works ok, but I find it a bit sas that the security policies are over written in the Code Groups with the new settings i.e. they are not merged with the existing groups. Luckily my clients have a clean slate of policies anyway so I wasnt overwriting anyone elses, but what if some other vendor had written an app for the client and had used the same approach, I would scrub there policies, or vice versa someone could quite easily scrub mine. Is this correct? Of course policies could be manually added in, but this might be a pain if there were a lot of users. Or is there another way I have missed? Thanks again,
Hi Joe, Thanks for the reply. That sounds like a good way of doing it. I'll give it a shot. Cheers very much, David [quoted text, click to view] "Joe Kaplan" wrote: > Probably the most reliable method would be to create a custom action that > calls caspol.exe with the proper arguments to add your policy to the current > policy. You'll probably want to use a search of some sort to find the > appropriate .NET framework directory to use to call caspol so that you > configure for the correct framework version. > > Joe K. > > -- > Joe Kaplan-MS MVP Directory Services Programming > Co-author of "The .NET Developer's Guide to Directory Services Programming" > http://www.directoryprogramming.net > -- > "David++" <David@discussions.microsoft.com> wrote in message > news:EA689966-00ED-4B4D-A577-38776F91A1C6@microsoft.com... > > Hi folks, > > > > I'm deploying my app to a network share and I need the security > > permissions > > installed on the client. AFAICT I cannot use ClickOnce because ClickOnce > > deployment doesnt allow user to specify install location i.e. Network > > share. > > So I have gone down the route of creating a Strong Name key and signing my > > assemblies with that key, creating a new Code Group in the Microsoft .NET > > configuration Tool which assigns full trust to my Strong Name key, and > > then > > finally creating a deployment package MSI to allow users to install the > > Security Policy easily. > > > > Now, this all works ok, but I find it a bit sas that the security policies > > are over written in the Code Groups with the new settings i.e. they are > > not > > merged with the existing groups. Luckily my clients have a clean slate of > > policies anyway so I wasnt overwriting anyone elses, but what if some > > other > > vendor had written an app for the client and had used the same approach, I > > would scrub there policies, or vice versa someone could quite easily scrub > > mine. Is this correct? > > > > Of course policies could be manually added in, but this might be a pain if > > there were a lot of users. > > > > Or is there another way I have missed? > > > > Thanks again, > > David > >
Hi Dominick, Sounds good, any code snippets you can summon would be very useful, I will also turn to the mighty google for this one also. Best, David [quoted text, click to view] "Dominick Baier" wrote: > Hi, > > no thats the default behavior. You should write an MSI installer with custom > install/uninstall actions to programmatically create the stuff for you. > > System.Security.Policy has all the classes you need. > > I can dig out some code if you need it. > > --- > Dominick Baier, DevelopMentor > http://www.leastprivilege.com > > > Hi folks, > > > > I'm deploying my app to a network share and I need the security > > permissions installed on the client. AFAICT I cannot use ClickOnce > > because ClickOnce deployment doesnt allow user to specify install > > location i.e. Network share. So I have gone down the route of creating > > a Strong Name key and signing my assemblies with that key, creating a > > new Code Group in the Microsoft .NET configuration Tool which assigns > > full trust to my Strong Name key, and then finally creating a > > deployment package MSI to allow users to install the Security Policy > > easily. > > > > Now, this all works ok, but I find it a bit sas that the security > > policies are over written in the Code Groups with the new settings > > i.e. they are not merged with the existing groups. Luckily my clients > > have a clean slate of policies anyway so I wasnt overwriting anyone > > elses, but what if some other vendor had written an app for the client > > and had used the same approach, I would scrub there policies, or vice > > versa someone could quite easily scrub mine. Is this correct? > > > > Of course policies could be manually added in, but this might be a > > pain if there were a lot of users. > > > > Or is there another way I have missed? > > > > Thanks again, > > David > >
Probably the most reliable method would be to create a custom action that calls caspol.exe with the proper arguments to add your policy to the current policy. You'll probably want to use a search of some sort to find the appropriate .NET framework directory to use to call caspol so that you configure for the correct framework version. Joe K. -- Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net -- [quoted text, click to view] "David++" <David@discussions.microsoft.com> wrote in message news:EA689966-00ED-4B4D-A577-38776F91A1C6@microsoft.com... > Hi folks, > > I'm deploying my app to a network share and I need the security > permissions > installed on the client. AFAICT I cannot use ClickOnce because ClickOnce > deployment doesnt allow user to specify install location i.e. Network > share. > So I have gone down the route of creating a Strong Name key and signing my > assemblies with that key, creating a new Code Group in the Microsoft .NET > configuration Tool which assigns full trust to my Strong Name key, and > then > finally creating a deployment package MSI to allow users to install the > Security Policy easily. > > Now, this all works ok, but I find it a bit sas that the security policies > are over written in the Code Groups with the new settings i.e. they are > not > merged with the existing groups. Luckily my clients have a clean slate of > policies anyway so I wasnt overwriting anyone elses, but what if some > other > vendor had written an app for the client and had used the same approach, I > would scrub there policies, or vice versa someone could quite easily scrub > mine. Is this correct? > > Of course policies could be manually added in, but this might be a pain if > there were a lot of users. > > Or is there another way I have missed? > > Thanks again, > David
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. -- Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net -- "Dominick Baier" <dbaier@pleasepleasenospam_leastprivilege.com> wrote in message news:4580be63196448c8bc30df997b80@news.microsoft.com... [quoted text, click to view] > ouch... > > use the API. Thats the most robust way. > > --- > Dominick Baier, DevelopMentor > http://www.leastprivilege.com > >> Probably the most reliable method would be to create a custom action >> that calls caspol.exe with the proper arguments to add your policy to >> the current policy. You'll probably want to use a search of some sort >> to find the appropriate .NET framework directory to use to call caspol >> so that you configure for the correct framework version. >> >> Joe K. >> > >
Only MSI packages created in VS.NET have native support for Installer classes via custom actions, and that is actually implemented via a hackfest shim that the VS team put into their code base. Installer classes are generally regarded as "the devil" by professional setup authors, as they violate some key MSI principles and have really poor integration with MSI in general. They are regarded as "toys for devs", but not for serious setup authors. The fact that the VS team continues to push Installer classes as the way to do things is a source of tension with the Windows Installer guys at MS. I know all of this stuff as I've been dragged into the world of doing more "pro" setup development for a project after hitting the wall with the capabilities of VS.NET deployment projects and found out about the dark side of Installer classes. Joe K. -- Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net -- "Dominick Baier" <dbaier@pleasepleasenospam_leastprivilege.com> wrote in message news:4580be63196508c8bc36e25f27d0@news.microsoft.com... [quoted text, click to view] > 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
Hi, no thats the default behavior. You should write an MSI installer with custom install/uninstall actions to programmatically create the stuff for you. System.Security.Policy has all the classes you need. I can dig out some code if you need it. --- Dominick Baier, DevelopMentor http://www.leastprivilege.com [quoted text, click to view] > Hi folks, > > I'm deploying my app to a network share and I need the security > permissions installed on the client. AFAICT I cannot use ClickOnce > because ClickOnce deployment doesnt allow user to specify install > location i.e. Network share. So I have gone down the route of creating > a Strong Name key and signing my assemblies with that key, creating a > new Code Group in the Microsoft .NET configuration Tool which assigns > full trust to my Strong Name key, and then finally creating a > deployment package MSI to allow users to install the Security Policy > easily. > > Now, this all works ok, but I find it a bit sas that the security > policies are over written in the Code Groups with the new settings > i.e. they are not merged with the existing groups. Luckily my clients > have a clean slate of policies anyway so I wasnt overwriting anyone > elses, but what if some other vendor had written an app for the client > and had used the same approach, I would scrub there policies, or vice > versa someone could quite easily scrub mine. Is this correct? > > Of course policies could be manually added in, but this might be a > pain if there were a lot of users. > > Or is there another way I have missed? > > Thanks again, > David
ouch... use the API. Thats the most robust way. --- Dominick Baier, DevelopMentor http://www.leastprivilege.com [quoted text, click to view] > Probably the most reliable method would be to create a custom action > that calls caspol.exe with the proper arguments to add your policy to > the current policy. You'll probably want to use a search of some sort > to find the appropriate .NET framework directory to use to call caspol > so that you configure for the correct framework version. > > Joe K. >
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,
Yep, it is really all about the audience in this case. They are great for devs and not so great for people doing professional deployment packaging. Ideally, MS would fix this mess and create a solution that worked well for both. The current implementation is not well-conceived and leads to this tension. It could be a much better for both audiences. Joe K. -- Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net -- "Dominick Baier" <dbaier@pleasepleasenospam_leastprivilege.com> wrote in message news:4580be63196598c8bc4646b1f770@news.microsoft.com... [quoted text, click to view] > OK :) > > but for the purpose of having a robust way to install/uninstall policies > they are perfect (for me). > > --- > Dominick Baier, DevelopMentor > http://www.leastprivilege.com > >> Only MSI packages created in VS.NET have native support for Installer >> classes via custom actions, and that is actually implemented via a >> hackfest shim that the VS team put into their code base. Installer >> classes are generally regarded as "the devil" by professional setup >> authors, as they violate some key MSI principles and have really poor >> integration with MSI in general. They are regarded as "toys for >> devs", but not for serious setup authors. >> >> The fact that the VS team continues to push Installer classes as the >> way to do things is a source of tension with the Windows Installer >> guys at MS. >> >> I know all of this stuff as I've been dragged into the world of doing >> more "pro" setup development for a project after hitting the wall with >> the capabilities of VS.NET deployment projects and found out about the >> dark side of Installer classes. >> >> Joe K. >> > >
OK :) but for the purpose of having a robust way to install/uninstall policies they are perfect (for me). --- Dominick Baier, DevelopMentor http://www.leastprivilege.com [quoted text, click to view] > Only MSI packages created in VS.NET have native support for Installer > classes via custom actions, and that is actually implemented via a > hackfest shim that the VS team put into their code base. Installer > classes are generally regarded as "the devil" by professional setup > authors, as they violate some key MSI principles and have really poor > integration with MSI in general. They are regarded as "toys for > devs", but not for serious setup authors. > > The fact that the VS team continues to push Installer classes as the > way to do things is a source of tension with the Windows Installer > guys at MS. > > I know all of this stuff as I've been dragged into the world of doing > more "pro" setup development for a project after hitting the wall with > the capabilities of VS.NET deployment projects and found out about the > dark side of Installer classes. > > Joe K. >
Don't see what you're looking for? Try a search.
|
|
|