Wednesday, June 01, 2005

Code Access Security Notes

  • Licensing Classes Using LicFileLicenseProvider: 1) Add a LicenseProviderAttribute to the class (only classes are permitted) being protected by the file-based license:
    [System.ComponentModel.LicenseProviderAttribute(typeof(System.ComponentModel.LicFileLicenseProvider))]
    2) In the constructor of the class you want to protect, call the LicenseManager's Validate method:
    System.ComponentModel.LicenseManager.Validate(typeof(MyClass));
    3) Create a license file with the name of the format Namespace.Class.lic. Where Namespace.Class is the fully qualified class to be protected by the license. 4) In the license file, make sure the first line reads the following format: "Namespace.Class is a licensed component." (without the double-quotes). Care must be taken to ensure the exact sentence is written - even omitting the preiod at the end will cause the LicenseManager to throw an exception if it's not formatted perfectly.
  • SecurityAction.RequestOptional: isn't what it sounds like; requests the specified permission from the CLR and ALL others are implicitly refused; the specified permission is simply requested and not demanded (RequestMinimum). So, when running a Windows Forms application, [assembly: FileDialogPermission(SecurityAction.RequestOptional, Unrestricted=true)] will prevent the CLR from allowing the application to execute as ALL other permissions, including UIPermission, will be denied permission grants.
  • SecurityAction.RequestMinimum: demands that the CLR grant the specified permission to the application. If the run-time security policy disallows the specified permission, the application will not execute.
  • SecurityAction.RequestRefuse: the application lets the CLR know that the specified permission MUST NOT BE GRANTED ACCESS. If the CLR detected that the application is using the specified resource, it will now allow the application to execute.
  • SecurityAction.RequestOptional, SecurityAction.RequestMinimum and SecurityAction.RequestRefuse can ONLY be used at the assembly scope.
  • All permissions declaratively stated at the assembly scope are stored in the assembly's manifest.
  • Repeated Role-based Validation: Use the AppDomain.CurrentDomain's SetPrincipalPolicy method instead of the Thread's CurrentPrincipal property when consistent validation against the current principal is present. Supposedly, there's less overhead involved. The default is for the AppDomain to use the PrincipalPolicy.UnauthenticatedPrincipal policy.
    String strUsername = ""; AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // strUsername will be 'DomainName\Username' AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // A null reference exception will occur as no // principal object was automatically created AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal); strUserName = System.Threading.Thread.CurrentPrincipal.Identity.Name); // strUsername will be '' (empty) even though an // IPrincipal object was automatically created
  • Single-use Role-based Validation: When you're only going to validate a user one time, don't use the AppDomain's SetPrincipalPolicy method. Instead, set the thread's CurrentPrincipal to an instance of an IPrincipal object..
    GenericPrincipal gp = new GenericPrincipal(new GenericIdentity("mikeg"), new String[] {"Developer" }); System.Threading.Thread.CurrentPrincipal = gp;
  • Replacing the IPrincipal Object: You will not have a problem replacing the thread's current principal with trusted code. However, semi-trusted code (Internet, Intranet zones, etc.) will cause a problem. In such a cases, make it known to the run-time that this is the permission you need for the semi-trusted code (the need to change the principal object from unmanaged code) by implementing the SecurityPermissionAttribute:..
    [assembly: System.Security.Permissions.SecurityPermissionAttribute(SecurityAction.RequestMinimum, ControlPrincipal = true)]
  • Validating the Current User: Use the PrincipalPermission object to validate the current thread's IPrincipal object/user.:
    //Declaratively: [System.Security.Permissions.PrincipalPermission(SecurityAction.Demand, Name = "Username", Role = "Role1")] // Imperatively: System.Security.Permissions.PrincipalPermission pp = new System.Security.Permissions.PrincipalPermission("Username", "Role"); pp.Demand();
  • Combining PrincipalPermission Objects: You can do this by creating two PrincipalPermission objects and joining them by using of the object's Union methods. The CLR uses an OR condition to determine whether a test succeeds. For example,.:
    // Current user/principal must be part of either of // the following two roles/groups: PrincipalPermission pp1 = new PrincipalPermission(null, "Administrators"); PrincipalPermission pp2 = new PrincipalPermission(null, "Domain Admins"); // Set the current user: GenericPrincipal gp1 = new GenericPrincipal(new GenericIdentity("MikeG"), new String[] { "Administrators" }); System.Threading.Thread.CurrentPrincipal = gp1; // User must be part of the Administrators group: pp1.Union(pp2).Demand(); // The above statement will succeed as the current // principal is part of the Administrators group.

No comments: