2004-12-20 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 bool checkExecutionRights;
49                 private static bool securityEnabled;
50                 private static object _lockObject;
51                 private static ArrayList _hierarchy;
52                 private static PermissionSet _fullTrust; // for [AllowPartiallyTrustedCallers]
53                 private static Hashtable _declsecCache;
54
55                 static SecurityManager () 
56                 {
57                         // lock(this) is bad
58                         // http://msdn.microsoft.com/library/en-us/dnaskdr/html/askgui06032003.asp?frame=true
59                         _lockObject = new object ();
60                         securityEnabled = true;
61 //                      checkExecutionRights = true;
62                 }
63
64                 private SecurityManager ()
65                 {
66                 }
67
68                 // properties
69
70                 public static bool CheckExecutionRights {
71                         get { return checkExecutionRights; }
72
73                         [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
74                         set {
75                                 // throw a SecurityException if we don't have ControlPolicy permission
76                                 checkExecutionRights = value; 
77                         }
78                 }
79
80                 public static bool SecurityEnabled {
81                         get { return securityEnabled; }
82
83                         [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
84                         set {
85                                 // throw a SecurityException if we don't have ControlPolicy permission
86                                 securityEnabled = value; 
87                         }
88                 }
89
90                 // methods
91
92 #if NET_2_0
93                 [MonoTODO]
94                 [StrongNameIdentityPermission (SecurityAction.LinkDemand, PublicKey = "0x00000000000000000400000000000000")]
95                 public static void GetZoneAndOrigin (out ArrayList zone, out ArrayList origin) 
96                 {
97                         zone = null;
98                         origin = null;
99                 }
100 #endif
101
102                 public static bool IsGranted (IPermission perm)
103                 {
104                         if (perm == null)
105                                 return true;
106                         if (!securityEnabled)
107                                 return true;
108
109                         // - Policy driven
110                         // - Only check the caller (no stack walk required)
111                         // - Not affected by overrides (like Assert, Deny and PermitOnly)
112                         // - calls IsSubsetOf even for non CAS permissions
113                         //   (i.e. it does call Demand so any code there won't be executed)
114                         return IsGranted (Assembly.GetCallingAssembly (), perm);
115                 }
116
117                 internal static bool IsGranted (Assembly a, IPermission perm)
118                 {
119                         CodeAccessPermission grant = null;
120
121                         if (a.GrantedPermissionSet != null)
122                                 grant = (CodeAccessPermission) a.GrantedPermissionSet.GetPermission (perm.GetType ());
123
124                         if ((grant == null) || !perm.IsSubsetOf (grant)) {
125                                 if (a.DeniedPermissionSet != null) {
126                                         CodeAccessPermission refuse = (CodeAccessPermission) a.DeniedPermissionSet.GetPermission (perm.GetType ());
127                                         if ((refuse != null) && perm.IsSubsetOf (refuse))
128                                                 return false;
129                                 }
130                                 return true;
131                         }
132                         return false;
133                 }
134
135                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
136                 public static PolicyLevel LoadPolicyLevelFromFile (string path, PolicyLevelType type)
137                 {
138                         // throw a SecurityException if we don't have ControlPolicy permission
139                         if (path == null)
140                                 throw new ArgumentNullException ("path");
141
142                         PolicyLevel pl = null;
143                         try {
144                                 pl = new PolicyLevel (type.ToString (), PolicyLevelType.AppDomain);
145                                 pl.LoadFromFile (path);
146                         }
147                         catch (Exception e) {
148                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
149                         }
150                         return pl;
151                 }
152
153                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
154                 public static PolicyLevel LoadPolicyLevelFromString (string str, PolicyLevelType type)
155                 {
156                         // throw a SecurityException if we don't have ControlPolicy permission
157                         if (null == str)
158                                 throw new ArgumentNullException ("str");
159
160                         PolicyLevel pl = null;
161                         try {
162                                 pl = new PolicyLevel (type.ToString (), PolicyLevelType.AppDomain);
163                                 pl.LoadFromString (str);
164                         }
165                         catch (Exception e) {
166                                 throw new ArgumentException (Locale.GetText ("Invalid policy XML"), e);
167                         }
168                         return pl;
169                 }
170
171                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
172                 public static IEnumerator PolicyHierarchy ()
173                 {
174                         // throw a SecurityException if we don't have ControlPolicy permission
175                         return Hierarchy;
176                 }
177
178                 public static PermissionSet ResolvePolicy (Evidence evidence)
179                 {
180                         // no evidence, no permission
181                         if (evidence == null)
182                                 return new PermissionSet (PermissionState.None);
183
184                         PermissionSet ps = null;
185                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
186                         IEnumerator ple = Hierarchy;
187                         while (ple.MoveNext ()) {
188                                 PolicyLevel pl = (PolicyLevel) ple.Current;
189                                 PolicyStatement pst = pl.Resolve (evidence);
190                                 if (pst != null) {
191                                         if (ps == null)
192                                                 ps = pst.PermissionSet; // for first time only
193                                         else
194                                                 ps = ps.Intersect (pst.PermissionSet);
195
196                                         // some permissions returns null, other returns an empty set
197                                         // sadly we must adjust for every variations :(
198                                         if (ps == null)
199                                                 ps = new PermissionSet (PermissionState.None);
200
201                                         if ((pst.Attributes & PolicyStatementAttribute.LevelFinal) == PolicyStatementAttribute.LevelFinal)
202                                                 break;
203                                 }
204                         }
205
206                         // Only host evidence are used for policy resolution
207                         IEnumerator ee = evidence.GetHostEnumerator ();
208                         while (ee.MoveNext ()) {
209                                 IIdentityPermissionFactory ipf = (ee.Current as IIdentityPermissionFactory);
210                                 if (ipf != null) {
211                                         IPermission p = ipf.CreateIdentityPermission (evidence);
212                                         ps.AddPermission (p);
213                                 }
214                         }
215
216                         return ps;
217                 }
218
219 #if NET_2_0
220                 public static PermissionSet ResolvePolicy (Evidence[] evidences)
221                 {
222                         if (evidences == null)
223                                 return new PermissionSet (PermissionState.None);
224
225                         // probably not optimal
226                         PermissionSet ps = null;
227                         foreach (Evidence evidence in evidences) {
228                                 if (ps == null)
229                                         ps = ResolvePolicy (evidence);
230                                 else
231                                         ps = ps.Intersect (ResolvePolicy (evidence));
232                         }
233                         return ps;
234                 }
235 #endif
236
237                 static private SecurityPermission _execution = new SecurityPermission (SecurityPermissionFlag.Execution);
238
239                 [MonoTODO()]
240                 public static PermissionSet ResolvePolicy (Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, out PermissionSet denied)
241                 {
242                         PermissionSet resolved = ResolvePolicy (evidence);
243                         // do we have the minimal permission requested by the assembly ?
244                         if ((reqdPset != null) && !reqdPset.IsSubsetOf (resolved)) {
245                                 throw new PolicyException (Locale.GetText (
246                                         "Policy doesn't grant the minimal permissions required to execute the assembly."));
247                         }
248                         // do we have the right to execute ?
249                         if (checkExecutionRights) {
250                                 // unless we have "Full Trust"...
251                                 if (!resolved.IsUnrestricted ()) {
252                                         // ... we need to find a SecurityPermission
253                                         IPermission security = resolved.GetPermission (typeof (SecurityPermission));
254                                         if (!_execution.IsSubsetOf (security)) {
255                                                 throw new PolicyException (Locale.GetText (
256                                                         "Policy doesn't grant the right to execute to the assembly."));
257                                         }
258                                 }
259                         }
260
261                         denied = denyPset;
262                         return resolved;
263                 }
264
265                 public static IEnumerator ResolvePolicyGroups (Evidence evidence)
266                 {
267                         if (evidence == null)
268                                 throw new ArgumentNullException ("evidence");
269
270                         ArrayList al = new ArrayList ();
271                         // Note: can't call PolicyHierarchy since ControlPolicy isn't required to resolve policies
272                         IEnumerator ple = Hierarchy;
273                         while (ple.MoveNext ()) {
274                                 PolicyLevel pl = (PolicyLevel) ple.Current;
275                                 CodeGroup cg = pl.ResolveMatchingCodeGroups (evidence);
276                                 al.Add (cg);
277                         }
278                         return al.GetEnumerator ();
279                 }
280
281                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
282                 public static void SavePolicy () 
283                 {
284                         // throw a SecurityException if we don't have ControlPolicy permission
285                         IEnumerator e = Hierarchy;
286                         while (e.MoveNext ()) {
287                                 PolicyLevel level = (e.Current as PolicyLevel);
288                                 level.Save ();
289                         }
290                 }
291
292                 [SecurityPermission (SecurityAction.Demand, Flags=SecurityPermissionFlag.ControlPolicy)]
293                 public static void SavePolicyLevel (PolicyLevel level) 
294                 {
295                         // Yes this will throw a NullReferenceException, just like MS (see FDBK13121)
296                         level.Save ();
297                 }
298
299                 // private/internal stuff
300
301                 private static IEnumerator Hierarchy {
302                         get {
303                                 // double-lock pattern
304                                 if (_hierarchy == null) {
305                                         lock (_lockObject) {
306                                                 if (_hierarchy == null)
307                                                         InitializePolicyHierarchy ();
308                                         }
309                                 }
310                                 return _hierarchy.GetEnumerator ();
311                         }
312                 }
313
314                 private static void InitializePolicyHierarchy ()
315                 {
316                         string machinePolicyPath = Path.GetDirectoryName (Environment.GetMachineConfigPath ());
317                         string userPolicyPath = Path.Combine (Environment.GetFolderPath (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 PermissionSet Decode (IntPtr permissions, int length)
333                 {
334                         // Permission sets from the runtime (declarative security) can be cached
335                         // for performance as they can never change (i.e. they are read-only).
336
337                         if (_declsecCache == null) {
338                                 lock (_lockObject) {
339                                         if (_declsecCache == null) {
340                                                 _declsecCache = new Hashtable ();
341                                         }
342                                 }
343                         }
344
345                         PermissionSet ps = null;
346                         lock (_lockObject) {
347                                 object key = (object) (int) permissions;
348                                 ps = (PermissionSet) _declsecCache [key];
349                                 if (ps == null) {
350                                         // create permissionset and add it to the cache
351                                         byte[] data = new byte [length];
352                                         Marshal.Copy (permissions, data, 0, length);
353                                         ps = Decode (data);
354                                         ps.DeclarativeSecurity = true;
355                                         _declsecCache.Add (key, ps);
356                                 }
357                         }
358                         return ps;
359                 }
360
361                 internal static PermissionSet Decode (byte[] encodedPermissions)
362                 {
363                         switch (encodedPermissions [0]) {
364                         case 60:
365                                 // Fx 1.0/1.1 declarative security permissions metadata is in Unicode-encoded XML
366                                 string xml = Encoding.Unicode.GetString (encodedPermissions);
367                                 return new PermissionSet (xml);
368                         case 0x2E:
369                                 // TODO: Fx 2.0
370                                 throw new SecurityException ("Unsupported 2.0 metadata format.");
371                         default:
372                                 throw new SecurityException ("Unknown metadata format.");
373                         }
374                 }
375
376                 private static PermissionSet Union (byte[] classPermissions, byte[] methodPermissions)
377                 {
378                         if (classPermissions != null) {
379                                 PermissionSet ps = Decode (classPermissions);
380                                 if (methodPermissions != null) {
381                                         ps = ps.Union (Decode (methodPermissions));
382                                 }
383                                 return ps;
384                         }
385
386                         return Decode (methodPermissions);
387                 }
388
389                 // internal - get called at JIT time
390
391                 unsafe private static void LinkDemand (
392                         IntPtr casClassPermission, int casClassLength,
393                         IntPtr nonCasClassPermission, int nonCasClassLength,
394                         IntPtr casMethodPermission, int casMethodLength,
395                         IntPtr nonCasMethodPermission, int nonCasMethodLength,
396                         bool requiresFullTrust)
397                 {
398                         PermissionSet ps = null;
399
400                         if (casClassLength > 0) {
401                                 ps = Decode (casClassPermission, casClassLength);
402                                 ps.ImmediateCallerDemand ();
403                         }
404                         if (nonCasClassLength > 0) {
405                                 ps = Decode (nonCasClassPermission, nonCasClassLength);
406                                 ps.ImmediateCallerNonCasDemand ();
407                         }
408
409                         if (casMethodLength > 0) {
410                                 ps = Decode (casMethodPermission, casMethodLength);
411                                 ps.ImmediateCallerDemand ();
412                         }
413                         if (nonCasMethodLength > 0) {
414                                 ps = Decode (nonCasMethodPermission, nonCasMethodLength);
415                                 ps.ImmediateCallerNonCasDemand ();
416                         }
417
418                         if (requiresFullTrust) {
419                                 // double-lock pattern
420                                 if (_fullTrust == null) {
421                                         lock (_lockObject) {
422                                                 if (_fullTrust == null)
423                                                         _fullTrust = new NamedPermissionSet ("FullTrust");
424                                         }
425                                 }
426                                 // FIXME: to be optimized with a flag
427                                 _fullTrust.ImmediateCallerDemand ();
428                         }
429                 }
430
431                 // internal - get called by the class loader
432
433                 // Called when
434                 // - class inheritance
435                 // - method overrides
436                 private static void InheritanceDemand (byte[] permissions, byte[] nonCasPermissions)
437                 {
438                         if (permissions != null) {
439                                 PermissionSet ps = Decode (permissions);
440                                 if (ps != null)
441                                         ps.ImmediateCallerDemand ();
442                         }
443                         if (nonCasPermissions != null) {
444                                 PermissionSet ps = Decode (nonCasPermissions);
445                                 if (ps != null)
446                                         ps.ImmediateCallerNonCasDemand ();
447                         }
448                 }
449
450                 // internal - get called by JIT generated code
451
452                 unsafe private static void InternalDemand (IntPtr permissions, int length)
453                 {
454                         PermissionSet ps = Decode (permissions, length);
455                         ps.Demand ();
456                 }
457
458                 unsafe private static void InternalDemandChoice (IntPtr permissions, int length)
459                 {
460 #if NET_2_0
461                         PermissionSet ps = Decode (permissions, length);
462                         // TODO
463 #else
464                         throw new SecurityException ("SecurityAction.DemandChoice is only possible in 2.0");
465 #endif
466                 }
467         }
468 }