Assemblies, Configuration and Security
Lesson 1 - Assemblies and Resources
Assemblies
-
project that compiles to .EXE or .DLL
-
primary deployment unit
-
collection of types and resources bound into logical unit of functionality
-
self describing - contain all info runtime requires to interpret contents and configure itself for execution
-
.EXE and .DLL resemble predecessors externally
-
Different internal structure of four parts
- Manifest (or metadata) contains info that runtime and CLR extract
- Type metadata details types contained in assembly
- Intermediate language for assembly
- Resource files
-
Manifest contains
- Identity - name and version number
- Types and resources - list of types exposed to CLR as well as info about how types can be accessed
- Files - list of files in assembly and dependency info for those files
- Security permissions - permissions required by assembly, if conflict with local security permission then assembly fails to execute
-
Manifest created automatically, developer must set identity information themselves
-
Identity info contained in AssemblyInfo.cs - can set by right clicking AssemblyInfo icon and choosing View Code
[assembly: AssemblyTitle("")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
...
Class Library Assemblies
- Represent sets of types that can be referenced and used in other assemblies
- Not executable, referenced by executable apps
- Create using templates provided by .NET
- From File menu choose New, then Project
- Select C# Projects and then Class Library or Windows Control Library
- Write code
- Set required identity information
- Build assembly
Resources and Resource Assemblies
-
.NET includes sample app called ResEditor for creating Text and Image resources
- must be compiled prior to use
- specify type and name of resources
- supply values (strings or images)
- save as .resources binary file or .resx XML file
-
Embedding resources into assembly increases portability and reduces dependencies on additional files
-
Add .resources or .resx files to Project by selecting Project\Add Existing Item
-
Can create assemblies only containing resources - useful where likely to have to update resource data but not rest of application
-
Add resource files to Empty Project (from New Project menu)
Satellite Assemblies
-
Permit different sets of resources to be loaded at runtime based on CurrentUICulture setting
-
To create, incorporate alternative sets of appropriately named resources into app
-
To be incorporated into satellite the resource file must be specially named
- Invariant culture resource file may be called MyResource.resx
- Alternative natural German would be called MyResource.de.resx
- Luxembourg specific would be called MyResource.de-LU.resx
-
Visual Studio .NET compiles resources into separate satellite assemblies within an appropriate directory structure
Retrieving Resources
- Use ResourceManager class to retrieve embedded resources
- Create ResourceManager specifying base name of embedded resource file and containing assembly - resulting instance dedicated to embedded resource file
- Base name = namespace containing file and filename without extensions, e.g. myResources.de-DE.resx in Namespace1 has base name of Namespace1.myResources
// Obtain resources from same assembly
ResourceManager myManager = new ResourceManager("myNamesapce.myResources", this.GetType().Assembly);
// Obtain resources from another assembly
System.Reflection.Assembly myResources;
myResources = System.Reflection.Assembly.Load("ResAssembly");
ResourceManager myManager = new ResourceManager("myNamesapce.myResources", myResources);
- To retrieve string use ResourceManager.GetString()
- To retrieve objects and images use ResourceManager.GetObject() converting returned item to correct type
- When culture specific satellite assemblies exist the ResourceManager automatically loads correct resources based on CurrentUICulture setting
Shared Assemblies
-
Private assembly
- used by one app
- most common type
- trouble free to create
- integral part of app (packaged within it)
- no versioning or identity issues
-
Note - can create assemblies used in multiple projects (e.g. DLL containing custom controls), but these are still private as each assembly using this DLL has its own private copy of the DLL residing in its project folder
-
Normally use private, only use shared assemblies where valid reason to do so
-
Shared assembly - used by multiple apps
-
Only one copy of DLL on machine, stored in GAC (Global Assembly Cache)
-
Why install to GAC
- Shared location
- Security - GAC located in C:\WINNT folder so given high level
- Side-by-side versioning - can install multiple versions of same assembly to GAC and apps locate and use appropriate version
-
Require strong naming - guarantees unique identity
- Identity of assembly - name, version and culture
- Public key or public/private pair
-
To sign assembly with strong name
- Generate key pair using sn.exe
sn -k myKey.snk
- Open Assembly Info file for project and verify version number is correct
- Set AssemblyKeyFileAttribute to file generated by sn.exe
Assembly: AssemblyKeyFile("..\\..\\myKey.snk");
- To install to GAC
- Sign assembly with string Nme
- Run gacutil.exe with /i to specify assembly to install
gacutil /i myAssembly.dll
Lesson 2 - Configuration and Optimisation
The Configuration File
- XML file containing info on app configuration
- Named
<name>.<extension>.config
where<name>
= app name and<extension>
= app extension (e.g. .exe) - Located in same folder as assembly it configures
- No required content for config file - all elements optional
- High level config file schema elements
<startup>
- contains<requiredRuntime>
that specifies version of CLR to use<runtime>
- assembly binding and garbage collection behaviour<system.runtime.remoting>
- configuration of channels and remote objects<system.net>
- info for Internet apps<mscorlib>
- contains<cryptogtraphySettings>
element that controls how app uses cryptography<configSections>
- custom config settings<system.diagnostics>
- config Trace and Debug classes
Dynamic Properties
- Allow configuration of startup values for objects in app
- Map object properties to entries in config file
- Retrieve values from file at runtime
- Useful for properties likely to change during apps lifetime (e.g. DB connection string)
- Can only store string types (or values that can be explicitly converted to string)
Using Properties Window To Configure Properties
- Use Properties windows to set properties of UI elements to be configurable
- Add desired properties to controls DynamicProperties node
- Associate key with desired property. This key corresponds to appropriate value to return from config file
- Default format is
<control><propertyname>
e.g.
- Default format is
<add key="Button1.Text" value="Button1">
appears in config file to configure Text attribute of Button1 control.
Setting / Retrieving Dynamic Properties Manually
- Useful for non-UI elements
- Access using AppSettingsReader class
//Create AppSettingsReader
System.Configuration.AppSettingsReader myReader = new System.Configuration.AppSettingsReader();
// Create a widget
Widget myWidget = new Widget();
// Retrieve dynamic property
myWidget.Text = myReader.GetValue("DynamicWidget.Text", typeof(System.String).ToString());
- Attempt to read key not present in config file causes InvalidOperationException
- Key data pairs held in
<add>
elements within<appSettings>
<appSettings>
<add key="Widget.Visible" value="True"/>
<add key="Widget.Text" value="Hello, world"/>
</appSettings>
Optimise performance
-
Begins in development
-
Avoid late binding
- avoid use of object data types
- unnecessary conversions resource expensive and slow
-
Avoid global variables
- Locals allocated in memory region that is easier for code to access
- Only use globals for truly global needs
- Constants used fro frequently used values improves efficiency
-
-
Be wary of loops
- Most operation-intensive regions of app - merit special attention - Design loops so fewest ops required -
Iterative process
- Measure performance data
- Identify bottlenecks
- Tune code
- Repeat
Measuring performance
- Use perfmon.exe
- Use Trace statements
Compiler Optimisations
- Does not take place of careful coding
- Optimisations drop down menu in Configuration Properties folder
- On dialog set Optimize Code to True
- Can make apps difficult to debug (IL is rearranged)
Lesson 3 - Security
-
Overall system security policy set by administrator
- what kind of code machine allowed to execute
- whether particular assembly is trusted, if so what kind of trust
-
Security policy set by administrator cannot be overridden by code
-
Use security to further protect app within bounds set by administrator
- Role based to authorise users
- Code access security to protect code from unauthorised users
-
Authorisation either
- Imperative - permission to execute demanded at runtime
- Declarative - permissions required by assembly specified in manifest
Permissions
-
Primary security objects
-
Represents
- User
- Identity
- Code resource
-
Implements IPermission interface
- Copy - Creates and returns identical copy of permission
- Demand - Walks call stack throwing SecurityException if all callers in call stack have not been granted the permission
- Intersect - Creates and returns permission that is intersection of two permissions
- IsSubsetOf - Determines if current permission is subset of specified permission
- Union - Creates and returns permission that is union of two permissions
Role Based Authorisation
-
Grants access to app or resources based on identity and role of user
-
Authenticated users represented by Principal object
- Contains info on identity and role
-
Validate Principal object against PrincipalPermission object to protect sensitive parts of app from unauthorised users
-
Can use Windows built-in security to verify identity and role of user by associating WindowsPrincipal object (the currently logged on user) with the apps principal policy
AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrinciplal);
- WindowsPrincipal contains reference to WindowsIdentity object (the current user)
- Obtain info on current user via WindowsIdentity
- WindowsIdentity returned by WindowsPrincipal as IIdentity interface - must explicitly convert to WindowsIdentity
WindowsPrincipla myPrincipal;
myPrincipal = (WindowsPrincipal)
System.Threading.Thread.CurrentPrincipal;
WindowsIdentity myIdentity;
myIdentity = (WindowsIdentity) myPrincipal.Identity;
MessageBox.Show(myIdentity.Name);
Imperative Security
- Use PrincipalPermission to perform imperative security checks
- Specifies and identity and role
- Demands current user match name and role specified
// Create principal permission for manager named megan
PrincipalPermission myPermission = new PrincipalPermission("Megan", "Manager");
// Ensure current user is manager named megan
myPermission.Demand();
- Use Union to combine permissions
// Create principal permission for manager named megan
PrincipalPermission Permission1 = new PrincipalPermission("Megan", "Manager");
// Create principal permission for group manager named
// ann
PrincipalPermission Permission2 = new PrincipalPermission("Ann", "Group Manager");
// Create union
PrincipalPermission Permission3 = (PrincipalPermission) Permission2.Union(Permission1);
// Ensure current user is manager named megan and
// group manager named ann
Permission3.Demand();
- Can specify null for name or role - validate only name of role of permission
// Create permission to check user is a manager
PrincipalPermission myPermission = new PrinicpalPermission(null, "Manager");
- Can authenticate membership of Windows built-in roles such as Administrators - precede role with BUILTIN\
// Create permission to check user is an Administrator
PrincipalPermission myPermission = new PrinicpalPermission(null, "BUILTIN\\Administrators");
Declarative Security
- Every Permission object has corresponding attribute
- Attach attributes to classes and members to control access
- Attributes emitted into metadata - enables administrators to decide if assembly allowed to execute
- Permission attribute constructor requires SecurityAction - usually a Demand action
[PrincipalPermission(SecurityAction.Demand, Name="Joe", Role="Clerk")]
public void MyMethod()
{
}
Code Access Security
-
Prevent code being used by unauthorised callers
-
Communicate security requirements to system administrator
-
Based on permissions - represent system resources and their access
- e.g. app writes to file, ensure unauthorised callers do not have access to that resource to prevent damage to file system, protect access to file with FileIOPermission object
-
Some of the available code access permissions
- DirectoryServicesPermission - access to Active Directory
- EnvironmentPermission - access to environment variables
- EventLogPermission - access to event logs
- FileDialogPermission - access to files and folders through file dialog box
- FileIOPermission - access to file system
- OleDbPermission - access to OleDb database
- PrintingPermission - access to printer
- ReflectionPermission - access to System.Reflection class
- RegistryPermission - access to registry
- SecurityPermission - ability to execute code, manipulate threads and principals and call unmanaged code
- SQLClientPermission - access to SQL Server database
- UIPermission - access to user interface
-
Every code access permission has different set of overloaded constructors to configure resource it protects
FileIOPermission myPermission = new FileIOPermission( FileIOPermissionAccess.Write, "C:\\myFile.txt");
- Unrestricted access provided by PermissionState.Unrestricted flag
- Use PermissionState.None to create permission with no access
ReflectionPermission myPermission = new ReflectionPermission(PermissionState.Unrestricted);
UIPermission anotherPermission = new UIPermission(PermissionState.None);
- Can be used either imperatively or declaratively
Imperative Security
- Primary method to enforce security is Demand()
- Permission to access resources granted by CLR by checking security policy for assembly as set by system administrator
- Demand() walks stack ensuring every caller has been granted permission to access resource represented by permission object
// Create permission object representing unrestricted
// access to file system
FileIOPermission myPermission = new FileIOPermission(Permissionstate.Unrestricted);
// Verify all callers to code have unrestricted access
// to file system
myPermission.Demand();
- Use Deny() to deny callers permission to access protected resources, even if they granted permission by the CLR
// Create permission object representing unrestricted
// access to file system
FileIOPermission myPermission = new FileIOPermission(Permissionstate.Unrestricted);
// Denies access to the file system from this method
myPermission.Deny();
- PermitOnly() restricts access to the specified resource
// Create permission object representing only write
// access to c:\myFile.txt
FileIOPermission myPermission = new FileIOPermission(FileIOPermission.Write, "C:\\myFilt.txt");
// Only allow write access to c:\myFile.txt, deny all
// else
myPermission.PermitOnly();
- Use Assert() to declare method has permission to access resource - Demand() stack traversal will halt with satisfaction when Assert() reached
- Use Assert() with care - potentially allows untrusted code to access resources
- Assert() can bypass system security policy - assembly must be given appropriate permission before issuing Assert()
// Create permission object representing unrestricted
// access to file system
FileIOPermission myPermission = new FileIOPermission(Permissionstate.Unrestricted);
// Assert that this method has unrestricted access to
// file system
myPermission.Assert();
- Use static Revert() to remove any previously granted Deny, Assert or PermitOnly calls
Declarative Security
- Similar to declarative code access, instead of specifying role you must specify SecurityAction represented by attribute
// Deny fileIOPermission to class
[FileIOPermission(SecurityAction.Deny)]
public class aClass
{
}
-
SecurityAction.Demand, SecurityAction.Deny, SecurityAction.Assert, SecurityAction.PermitOnly correspond to Demand, Deny, Assert and PermitOnly of relevant permission
-
SecurityAccess.LinkDemand only requires immediate caller to have been granted appropriate permission
-
SecurityAccess.InheritanceDemand requires any inherited class or overriding method must have appropriate permission
-
Can use permission attributes against entire assembly
- SecurityAction.RequestMinimum - makes request to CLR to be granted requested permission, if not granted assembly does not run
- SecurityAction.RequestOptional - makes request to CLR to be granted requested permission, if not granted assembly still runs
- SecurityAction.RequestRefuse - requests assembly be denied specified permission
[assembly: FileIOPermission(SecurityAction.RequestMinimum)]
- Can set properties for permission attribute
[FileIOPermission(SecurityAction.Assert, Write="C:\\myFile.txt")]
public void WriteFile()
{
}
Exception Handling and Imperative Security
- Security failure throws SecurityException
- Imperative security should be wrapped in exception handling code permitting app to degrade gracefully
- Foreseeable exceptions should not go unhandled