2 // System.Security.SecurityManager.cs
5 // Nick Drochak(ndrochak@gol.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
9 // Portions (C) 2004 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Collections;
33 using System.Globalization;
35 using System.Reflection;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
38 using System.Security.Permissions;
39 using System.Security.Policy;
44 namespace System.Security {
46 public sealed class SecurityManager {
48 private static object _lockObject;
49 private static ArrayList _hierarchy;
50 private static PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
51 private static Hashtable _declsecCache;
53 static SecurityManager ()
56 // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
57 _lockObject = new object ();
60 private SecurityManager ()
66 extern public static bool CheckExecutionRights {
67 [MethodImplAttribute (MethodImplOptions.InternalCall)]
70 [MethodImplAttribute (MethodImplOptions.InternalCall)]
71 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
75 extern public static bool SecurityEnabled {
76 [MethodImplAttribute (MethodImplOptions.InternalCall)]
79 [MethodImplAttribute (MethodImplOptions.InternalCall)]
80 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
88 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
89 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin)
91 zone = new ArrayList ();
92 origin = new ArrayList ();
96 public static bool IsGranted (IPermission perm)
100 if (!SecurityEnabled)
104 // - Only check the caller (no stack walk required)
105 // - Not affected by overrides (like Assert, Deny and PermitOnly)
106 // - calls IsSubsetOf even for non CAS permissions
107 // (i.e. it does call Demand so any code there won't be executed)
108 return IsGranted (Assembly.GetCallingAssembly (), perm);
111 internal static bool IsGranted (Assembly a, IPermission perm)
113 CodeAccessPermission grant = null;
115 if (a.GrantedPermissionSet != null) {
116 grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
118 if (!a.GrantedPermissionSet.IsUnrestricted () || !(perm is IUnrestrictedPermission)) {
121 } else if (!perm.IsSubsetOf (grant)) {
126 if (a.DeniedPermissionSet != null) {
127 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
128 if ((refuse != null) && perm.IsSubsetOf (refuse))
134 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
135 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
137 // throw a SecurityException if we don't have ControlPolicy permission
139 throw new ArgumentNullException ("path");
141 PolicyLevel pl = null;
143 pl = new PolicyLevel (type.ToString (), type);
144 pl.LoadFromFile (path);
146 catch (Exception e) {
147 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
152 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
153 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
155 // throw a SecurityException if we don't have ControlPolicy permission
157 throw new ArgumentNullException ("str");
159 PolicyLevel pl = null;
161 pl = new PolicyLevel (type.ToString (), type);
162 pl.LoadFromString (str);
164 catch (Exception e) {
165 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
170 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
171 public static IEnumerator PolicyHierarchy ()
173 // throw a SecurityException if we don't have ControlPolicy permission
177 public static PermissionSet ResolvePolicy (Evidence evidence)
179 // no evidence, no permission
180 if (evidence == null)
181 return new PermissionSet (PermissionState.None);
183 PermissionSet ps = null;
184 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
185 IEnumerator ple = Hierarchy;
186 while (ple.MoveNext ()) {
187 PolicyLevel pl = (PolicyLevel) ple.Current;
188 if (ResolvePolicyLevel (ref ps, pl, evidence)) {
189 break; // i.e. PolicyStatementAttribute.LevelFinal
193 ResolveIdentityPermissions (ps, evidence);
198 [MonoTODO ("more tests are needed")]
199 public static PermissionSet ResolvePolicy (Evidence[] evidences)
201 if ((evidences == null) || (evidences.Length == 0) ||
202 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
203 return new PermissionSet (PermissionState.None);
206 // probably not optimal
207 PermissionSet ps = ResolvePolicy (evidences [0]);
208 for (int i=1; i < evidences.Length; i++) {
209 ps = ps.Intersect (ResolvePolicy (evidences [i]));
214 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
216 // no evidence, no permission
217 if (evidence == null)
218 return new PermissionSet (PermissionState.None);
220 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
221 PermissionSet ps = null;
222 IEnumerator ple = Hierarchy;
223 while (ple.MoveNext ()) {
224 PolicyLevel pl = (PolicyLevel) ple.Current;
225 if (pl.Type == PolicyLevelType.AppDomain)
227 if (ResolvePolicyLevel (ref ps, pl, evidence))
228 break; // i.e. PolicyStatementAttribute.LevelFinal
231 ResolveIdentityPermissions (ps, evidence);
236 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
239 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
241 PermissionSet resolved = ResolvePolicy (evidence);
242 // do we have the minimal permission requested by the assembly ?
243 if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
244 throw new PolicyException (Locale.GetText (
245 "Policy doesn't grant the minimal permissions required to execute the assembly."));
247 // do we have the right to execute ?
248 if (CheckExecutionRights) {
249 // unless we have "Full Trust"...
250 if (!resolved.IsUnrestricted ()) {
251 // ... we need to find a SecurityPermission
252 IPermission security = resolved.GetPermission (typeof (SecurityPermission));
253 if (!_execution.IsSubsetOf (security)) {
254 throw new PolicyException (Locale.GetText (
255 "Policy doesn't grant the right to execute to the assembly."));
264 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
266 if (evidence == null)
267 throw new ArgumentNullException ("evidence");
269 ArrayList al = new ArrayList ();
270 // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
271 IEnumerator ple = Hierarchy;
272 while (ple.MoveNext ()) {
273 PolicyLevel pl = (PolicyLevel) ple.Current;
274 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
277 return al.GetEnumerator ();
280 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
281 public static void SavePolicy ()
283 // throw a SecurityException if we don't have ControlPolicy permission
284 IEnumerator e = Hierarchy;
285 while (e.MoveNext ()) {
286 PolicyLevel level = (e.Current as PolicyLevel);
291 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
292 public static void SavePolicyLevel (PolicyLevel level)
294 // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
298 // private/internal stuff
300 private static IEnumerator Hierarchy {
302 // double-lock pattern
303 if (_hierarchy == null) {
305 if (_hierarchy == null)
306 InitializePolicyHierarchy ();
309 return _hierarchy.GetEnumerator ();
313 private static void InitializePolicyHierarchy ()
315 string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
316 // note: use InternalGetFolderPath to avoid recursive policy initialization
317 string userPolicyPath = Path.Combine (Environment.InternalGetFolderPath (Environment.SpecialFolder.ApplicationData), "mono");
319 ArrayList al = new ArrayList ();
320 al.Add (new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise,
321 Path.Combine (machinePolicyPath, "enterprisesec.config")));
323 al.Add (new PolicyLevel ("Machine", PolicyLevelType.Machine,
324 Path.Combine (machinePolicyPath, "security.config")));
326 al.Add (new PolicyLevel ("User", PolicyLevelType.User,
327 Path.Combine (userPolicyPath, "security.config")));
329 _hierarchy = ArrayList.Synchronized (al);
332 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
334 PolicyStatement pst = pl.Resolve (evidence);
337 // only for initial (first) policy level processed
338 ps = pst.PermissionSet;
340 ps = ps.Intersect (pst.PermissionSet);
342 // null is equals to None - exist that null can throw NullReferenceException ;-)
343 ps = new PermissionSet (PermissionState.None);
347 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
353 // TODO: this changes in 2.0 as identity permissions can now be unrestricted
354 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
356 // Only host evidence are used for policy resolution
357 IEnumerator ee = evidence.GetHostEnumerator ();
358 while (ee.MoveNext ()) {
359 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
361 IPermission p = ipf.CreateIdentityPermission (evidence);
362 ps.AddPermission (p);
367 internal static PermissionSet Decode (IntPtr permissions, int length)
369 // Permission sets from the runtime (declarative security) can be cached
370 // for performance as they can never change (i.e. they are read-only).
372 if (_declsecCache == null) {
374 if (_declsecCache == null) {
375 _declsecCache = new Hashtable ();
380 PermissionSet ps = null;
382 object key = (object) (int) permissions;
383 ps = (PermissionSet) _declsecCache [key];
385 // create permissionset and add it to the cache
386 byte[] data = new byte [length];
387 Marshal.Copy (permissions, data, 0, length);
389 ps.DeclarativeSecurity = true;
390 _declsecCache.Add (key, ps);
396 internal static PermissionSet Decode (byte[] encodedPermissions)
398 switch (encodedPermissions [0]) {
400 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
401 string xml = Encoding.Unicode.GetString (encodedPermissions);
402 return new PermissionSet (xml);
405 throw new SecurityException ("Unsupported 2.0 metadata format.");
407 throw new SecurityException ("Unknown metadata format.");
411 private static PermissionSet Union (byte[] classPermissions, byte[] methodPermissions)
413 if (classPermissions != null) {
414 PermissionSet ps = Decode (classPermissions);
415 if (methodPermissions != null) {
416 ps = ps.Union (Decode (methodPermissions));
421 return Decode (methodPermissions);
424 // internal - get called at JIT time
426 unsafe private static void LinkDemand (
427 IntPtr casClassPermission, int casClassLength,
428 IntPtr nonCasClassPermission, int nonCasClassLength,
429 IntPtr casMethodPermission, int casMethodLength,
430 IntPtr nonCasMethodPermission, int nonCasMethodLength,
431 bool requiresFullTrust)
433 PermissionSet ps = null;
435 if (casClassLength > 0) {
436 ps = Decode (casClassPermission, casClassLength);
437 ps.ImmediateCallerDemand ();
439 if (nonCasClassLength > 0) {
440 ps = Decode (nonCasClassPermission, nonCasClassLength);
441 ps.ImmediateCallerNonCasDemand ();
444 if (casMethodLength > 0) {
445 ps = Decode (casMethodPermission, casMethodLength);
446 ps.ImmediateCallerDemand ();
448 if (nonCasMethodLength > 0) {
449 ps = Decode (nonCasMethodPermission, nonCasMethodLength);
450 ps.ImmediateCallerNonCasDemand ();
453 if (requiresFullTrust) {
454 // double-lock pattern
455 if (_fullTrust == null) {
457 if (_fullTrust == null)
458 _fullTrust = new NamedPermissionSet ("FullTrust");
461 // FIXME: to be optimized with a flag
462 _fullTrust.ImmediateCallerDemand ();
466 // internal - get called by the class loader
469 // - class inheritance
470 // - method overrides
471 private static void InheritanceDemand (byte[] permissions, byte[] nonCasPermissions)
473 if (permissions != null) {
474 PermissionSet ps = Decode (permissions);
476 ps.ImmediateCallerDemand ();
478 if (nonCasPermissions != null) {
479 PermissionSet ps = Decode (nonCasPermissions);
481 ps.ImmediateCallerNonCasDemand ();
485 // internal - get called by JIT generated code
487 unsafe private static void InternalDemand (IntPtr permissions, int length)
489 PermissionSet ps = Decode (permissions, length);
493 unsafe private static void InternalDemandChoice (IntPtr permissions, int length)
496 PermissionSet ps = Decode (permissions, length);
499 throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");