2005-01-25 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Security / SecurityManager.cs
1 //
2 // System.Security.SecurityManager.cs
3 //
4 // Authors:
5 //      Nick Drochak(ndrochak@gol.com)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // (C) Nick Drochak
9 // Portions (C) 2004 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 //
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:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
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.
30 //
31
32 using System.Collections;
33 using System.Globalization;
34 using System.IO;
35 using System.Reflection;
36 using System.Runtime.CompilerServices;
37 using System.Runtime.InteropServices;
38 using System.Security.Permissions;
39 using System.Security.Policy;
40 using System.Text;
41
42 using Mono.Xml;
43
44 namespace System.Security {
45
46         public sealed class SecurityManager {
47
48                 private static object _lockObject;
49                 private static ArrayList _hierarchy;
50                 private static PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
51                 private static Hashtable _declsecCache;
52
53                 static SecurityManager () 
54                 {
55                         // lock(this) is bad
56                         // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
57                         _lockObject = new object ();
58                 }
59
60                 private SecurityManager ()
61                 {
62                 }
63
64                 // properties
65
66                 extern public static bool CheckExecutionRights {
67                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
68                         get;
69
70                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
71                         [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
72                         set;
73                 }
74
75                 extern public static bool SecurityEnabled {
76                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
77                         get;
78
79                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
80                         [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
81                         set;
82                 }
83
84                 // methods
85
86 #if NET_2_0
87                 [MonoTODO]
88                 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
89                 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin) 
90                 {
91                         zone = new ArrayList ();
92                         origin = new ArrayList ();
93                 }
94 #endif
95
96                 public static bool IsGranted (IPermission perm)
97                 {
98                         if (perm == null)
99                                 return true;
100                         if (!SecurityEnabled)
101                                 return true;
102
103                         // - Policy driven
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);
109                 }
110
111                 internal static bool IsGranted (Assembly a, IPermission perm)
112                 {
113                         CodeAccessPermission grant = null;
114
115                         if (a.GrantedPermissionSet != null) {
116                                 grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
117                                 if (grant == null) {
118                                         if (!a.GrantedPermissionSet.IsUnrestricted () || !(perm is IUnrestrictedPermission)) {
119                                                 return false;
120                                         }
121                                 } else if (!perm.IsSubsetOf (grant)) {
122                                         return false;
123                                 }
124                         }
125
126                         if (a.DeniedPermissionSet != null) {
127                                 CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
128                                 if ((refuse != null) && perm.IsSubsetOf (refuse))
129                                         return false;
130                         }
131                         return true;
132                 }
133
134                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
135                 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
136                 {
137                         // throw a SecurityException if we don't have ControlPolicy permission
138                         if (path == null)
139                                 throw new ArgumentNullException ("path");
140
141                         PolicyLevel pl = null;
142                         try {
143                                 pl = new PolicyLevel (type.ToString (), type);
144                                 pl.LoadFromFile (path);
145                         }
146                         catch (Exception e) {
147                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
148                         }
149                         return pl;
150                 }
151
152                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
153                 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
154                 {
155                         // throw a SecurityException if we don't have ControlPolicy permission
156                         if (null == str)
157                                 throw new ArgumentNullException ("str");
158
159                         PolicyLevel pl = null;
160                         try {
161                                 pl = new PolicyLevel (type.ToString (), type);
162                                 pl.LoadFromString (str);
163                         }
164                         catch (Exception e) {
165                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
166                         }
167                         return pl;
168                 }
169
170                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
171                 public static IEnumerator PolicyHierarchy ()
172                 {
173                         // throw a SecurityException if we don't have ControlPolicy permission
174                         return Hierarchy;
175                 }
176
177                 public static PermissionSet ResolvePolicy (Evidence evidence)
178                 {
179                         // no evidence, no permission
180                         if (evidence == null)
181                                 return new PermissionSet (PermissionState.None);
182
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
190                                 }
191                         }
192
193                         ResolveIdentityPermissions (ps, evidence);
194                         return ps;
195                 }
196
197 #if NET_2_0
198                 [MonoTODO ("more tests are needed")]
199                 public static PermissionSet ResolvePolicy (Evidence[] evidences)
200                 {
201                         if ((evidences == null) || (evidences.Length == 0) ||
202                                 ((evidences.Length == 1) && (evidences [0].Count == 0))) {
203                                 return new PermissionSet (PermissionState.None);
204                         }
205
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]));
210                         }
211                         return ps;
212                 }
213
214                 public static PermissionSet ResolveSystemPolicy (Evidence evidence)
215                 {
216                         // no evidence, no permission
217                         if (evidence == null)
218                                 return new PermissionSet (PermissionState.None);
219
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)
226                                         break;
227                                 if (ResolvePolicyLevel (ref ps, pl, evidence))
228                                         break;  // i.e. PolicyStatementAttribute.LevelFinal
229                         }
230
231                         ResolveIdentityPermissions (ps, evidence);
232                         return ps;
233                 }
234 #endif
235
236                 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
237
238                 [MonoTODO()]
239                 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
240                 {
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."));
246                         }
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."));
256                                         }
257                                 }
258                         }
259
260                         denied = denyPset;
261                         return resolved;
262                 }
263
264                 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
265                 {
266                         if (evidence == null)
267                                 throw new ArgumentNullException ("evidence");
268
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);
275                                 al.Add (cg);
276                         }
277                         return al.GetEnumerator ();
278                 }
279
280                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
281                 public static void SavePolicy () 
282                 {
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);
287                                 level.Save ();
288                         }
289                 }
290
291                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
292                 public static void SavePolicyLevel (PolicyLevel level) 
293                 {
294                         // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
295                         level.Save ();
296                 }
297
298                 // private/internal stuff
299
300                 private static IEnumerator Hierarchy {
301                         get {
302                                 // double-lock pattern
303                                 if (_hierarchy == null) {
304                                         lock (_lockObject) {
305                                                 if (_hierarchy == null)
306                                                         InitializePolicyHierarchy ();
307                                         }
308                                 }
309                                 return _hierarchy.GetEnumerator ();
310                         }
311                 }
312
313                 private static void InitializePolicyHierarchy ()
314                 {
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");
318
319                         ArrayList al = new ArrayList ();
320                         al.Add (new PolicyLevel ("Enterprise", PolicyLevelType.Enterprise,
321                                 Path.Combine (machinePolicyPath, "enterprisesec.config")));
322
323                         al.Add (new PolicyLevel ("Machine", PolicyLevelType.Machine,
324                                 Path.Combine (machinePolicyPath, "security.config")));
325
326                         al.Add (new PolicyLevel ("User", PolicyLevelType.User,
327                                 Path.Combine (userPolicyPath, "security.config")));
328
329                         _hierarchy = ArrayList.Synchronized (al);
330                 }
331
332                 internal static bool ResolvePolicyLevel (ref PermissionSet ps, PolicyLevel pl, Evidence evidence)
333                 {
334                         PolicyStatement pst = pl.Resolve (evidence);
335                         if (pst != null) {
336                                 if (ps == null) {
337                                         // only for initial (first) policy level processed
338                                         ps = pst.PermissionSet;
339                                 } else {
340                                         ps = ps.Intersect (pst.PermissionSet);
341                                         if (ps == null) {
342                                                 // null is equals to None - exist that null can throw NullReferenceException ;-)
343                                                 ps = new PermissionSet (PermissionState.None);
344                                         }
345                                 }
346
347                                 if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
348                                         return true;
349                         }
350                         return false;
351                 }
352
353                 // TODO: this changes in 2.0 as identity permissions can now be unrestricted
354                 internal static void ResolveIdentityPermissions (PermissionSet ps, Evidence evidence)
355                 {
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);
360                                 if (ipf != null) {
361                                         IPermission p = ipf.CreateIdentityPermission (evidence);
362                                         ps.AddPermission (p);
363                                 }
364                         }
365                 }
366
367                 internal static PermissionSet Decode (IntPtr permissions, int length)
368                 {
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).
371
372                         if (_declsecCache == null) {
373                                 lock (_lockObject) {
374                                         if (_declsecCache == null) {
375                                                 _declsecCache = new Hashtable ();
376                                         }
377                                 }
378                         }
379
380                         PermissionSet ps = null;
381                         lock (_lockObject) {
382                                 object key = (object) (int) permissions;
383                                 ps = (PermissionSet) _declsecCache [key];
384                                 if (ps == null) {
385                                         // create permissionset and add it to the cache
386                                         byte[] data = new byte [length];
387                                         Marshal.Copy (permissions, data, 0, length);
388                                         ps = Decode (data);
389                                         ps.DeclarativeSecurity = true;
390                                         _declsecCache.Add (key, ps);
391                                 }
392                         }
393                         return ps;
394                 }
395
396                 internal static PermissionSet Decode (byte[] encodedPermissions)
397                 {
398                         switch (encodedPermissions [0]) {
399                         case 60:
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);
403                         case 0x2E:
404                                 // TODO: Fx 2.0
405                                 throw new SecurityException ("Unsupported 2.0 metadata format.");
406                         default:
407                                 throw new SecurityException ("Unknown metadata format.");
408                         }
409                 }
410
411                 private static PermissionSet Union (byte[] classPermissions, byte[] methodPermissions)
412                 {
413                         if (classPermissions != null) {
414                                 PermissionSet ps = Decode (classPermissions);
415                                 if (methodPermissions != null) {
416                                         ps = ps.Union (Decode (methodPermissions));
417                                 }
418                                 return ps;
419                         }
420
421                         return Decode (methodPermissions);
422                 }
423
424                 // internal - get called at JIT time
425
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)
432                 {
433                         PermissionSet ps = null;
434
435                         if (casClassLength > 0) {
436                                 ps = Decode (casClassPermission, casClassLength);
437                                 ps.ImmediateCallerDemand ();
438                         }
439                         if (nonCasClassLength > 0) {
440                                 ps = Decode (nonCasClassPermission, nonCasClassLength);
441                                 ps.ImmediateCallerNonCasDemand ();
442                         }
443
444                         if (casMethodLength > 0) {
445                                 ps = Decode (casMethodPermission, casMethodLength);
446                                 ps.ImmediateCallerDemand ();
447                         }
448                         if (nonCasMethodLength > 0) {
449                                 ps = Decode (nonCasMethodPermission, nonCasMethodLength);
450                                 ps.ImmediateCallerNonCasDemand ();
451                         }
452
453                         if (requiresFullTrust) {
454                                 // double-lock pattern
455                                 if (_fullTrust == null) {
456                                         lock (_lockObject) {
457                                                 if (_fullTrust == null)
458                                                         _fullTrust = new NamedPermissionSet ("FullTrust");
459                                         }
460                                 }
461                                 // FIXME: to be optimized with a flag
462                                 _fullTrust.ImmediateCallerDemand ();
463                         }
464                 }
465
466                 // internal - get called by the class loader
467
468                 // Called when
469                 // - class inheritance
470                 // - method overrides
471                 private static void InheritanceDemand (byte[] permissions, byte[] nonCasPermissions)
472                 {
473                         if (permissions != null) {
474                                 PermissionSet ps = Decode (permissions);
475                                 if (ps != null)
476                                         ps.ImmediateCallerDemand ();
477                         }
478                         if (nonCasPermissions != null) {
479                                 PermissionSet ps = Decode (nonCasPermissions);
480                                 if (ps != null)
481                                         ps.ImmediateCallerNonCasDemand ();
482                         }
483                 }
484
485                 // internal - get called by JIT generated code
486
487                 unsafe private static void InternalDemand (IntPtr permissions, int length)
488                 {
489                         PermissionSet ps = Decode (permissions, length);
490                         ps.Demand ();
491                 }
492
493                 unsafe private static void InternalDemandChoice (IntPtr permissions, int length)
494                 {
495 #if NET_2_0
496                         PermissionSet ps = Decode (permissions, length);
497                         // TODO
498 #else
499                         throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");
500 #endif
501                 }
502         }
503 }