2004-09-01 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Security / PermissionSet.cs
1 //
2 // System.Security.PermissionSet.cs
3 //
4 // Authors:
5 //      Nick Drochak(ndrochak@gol.com)
6 //      Sebastien Pouliot  <sebastien@ximian.com>
7 //
8 // (C) Nick Drochak
9 // Portions (C) 2003, 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.Diagnostics;
34 using System.Reflection;
35 using System.Runtime.Serialization;
36 using System.Security.Permissions;
37 using System.Security.Policy;
38
39 namespace System.Security {
40
41         [Serializable]
42         public class PermissionSet: ISecurityEncodable, ICollection, IEnumerable, IStackWalk, IDeserializationCallback {
43
44                 private static string tagName = "PermissionSet";
45                 private const int version = 1;
46
47                 private PermissionState state;
48                 private ArrayList list;
49                 private int _hashcode;
50                 private PolicyLevel _policyLevel;
51
52                 // constructors
53
54                 // for PolicyLevel (to avoid validation duplication)
55                 internal PermissionSet () 
56                 {
57                         list = new ArrayList ();
58                 }
59
60                 public PermissionSet (PermissionState state) : this ()
61                 {
62                         if (!Enum.IsDefined (typeof (PermissionState), state))
63                                 throw new System.ArgumentException ("state");
64                         this.state = state;
65                 }
66
67                 public PermissionSet (PermissionSet permSet) : this ()
68                 {
69                         // LAMESPEC: This would be handled by the compiler.  No way permSet is not a PermissionSet.
70                         //if (!(permSet is PermissionSet))
71                         //      throw new System.ArgumentException(); // permSet is not an instance of System.Security.PermissionSet.
72                         if (permSet == null)
73                                 state = PermissionState.Unrestricted;
74                         else {
75                                 state = permSet.state;
76                                 foreach (IPermission p in permSet.list)
77                                         list.Add (p);
78                         }
79                 }
80
81                 // methods
82
83                 public virtual IPermission AddPermission (IPermission perm)
84                 {
85                         if (perm == null)
86                                 return null;
87
88                         // we don't add to an unrestricted permission set unless...
89                         if (state == PermissionState.Unrestricted) {
90                                 // we're adding identity permission as they don't support unrestricted
91                                 if (perm is IUnrestrictedPermission) {
92                                         // we return the union of the permission with unrestricted
93                                         // which results in a permission of the same type initialized 
94                                         // with PermissionState.Unrestricted
95                                         object[] args = new object [1] { PermissionState.Unrestricted };
96                                         return (IPermission) Activator.CreateInstance (perm.GetType (), args);
97                                 }
98                         }
99
100                         // we can't add two permissions of the same type in a set
101                         // so we remove an existing one, union with it and add it back
102                         IPermission existing = RemovePermission (perm.GetType ());
103                         if (existing != null) {
104                                 perm = perm.Union (existing);
105                         }
106
107                         list.Add (perm);
108                         return perm;
109                 }
110
111                 [MonoTODO()]
112                 public virtual void Assert ()
113                 {
114                 }
115
116                 internal void Clear () 
117                 {
118                         list.Clear ();
119                 }
120
121                 public virtual PermissionSet Copy ()
122                 {
123                         return new PermissionSet (this);
124                 }
125
126                 public virtual void CopyTo (Array array, int index)
127                 {
128                         if (null == array)
129                                 throw new ArgumentNullException ("array");
130
131                         if (list.Count > 0) {
132                                 if (array.Rank > 1) {
133                                         throw new ArgumentException (Locale.GetText (
134                                                 "Array has more than one dimension"));
135                                 }
136                                 if (index < 0 || index >= array.Length) {
137                                         throw new IndexOutOfRangeException ("index");
138                                 }
139
140                                 list.CopyTo (array, index);
141                         }
142                 }
143
144                 [MonoTODO ("Assert, Deny and PermitOnly aren't yet supported")]
145                 public virtual void Demand ()
146                 {
147                         if (!SecurityManager.SecurityEnabled)
148                                 return;
149
150                         // non CAS permissions (e.g. PrincipalPermission) do not requires a stack walk
151                         PermissionSet cas = this.Copy ();
152                         foreach (IPermission p in list) {
153                                 Type t = p.GetType ();
154                                 if (!t.IsSubclassOf (typeof (CodeAccessPermission))) {
155                                         p.Demand ();
156                                         // we wont have to process this one in the stack walk
157                                         cas.RemovePermission (t);
158                                 }
159                         }
160                         // don't start the walk if the permission set only contains non CAS permissions
161                         if (cas.Count == 0)
162                                 return;
163
164                         Assembly a = null;
165                         StackTrace st = new StackTrace (1); // skip ourself
166                         StackFrame[] frames = st.GetFrames ();
167                         foreach (StackFrame sf in frames) {
168                                 MethodBase mb = sf.GetMethod ();
169                                 // declarative security checks, when present, must be checked
170                                 // for each stack frame
171                                 if ((MethodAttributes.HasSecurity & mb.Attributes) == MethodAttributes.HasSecurity) {
172                                         // TODO
173                                 }
174                                 // however the "final" grant set is resolved by assembly, so
175                                 // there's no need to check it every time (just when we're 
176                                 // changing assemblies between frames).
177                                 Assembly af = mb.ReflectedType.Assembly;
178                                 if (a != af) {
179                                         a = af;
180                                         if (!a.Demand (cas)) {
181                                                 // TODO add more details
182                                                 throw new SecurityException ("Demand failed");
183                                         }
184                                 }
185                         }
186                 }
187
188                 [MonoTODO()]
189                 public virtual void Deny ()
190                 {
191                 }
192
193                 [MonoTODO ("adjust class version with current runtime - unification")]
194                 public virtual void FromXml (SecurityElement et)
195                 {
196                         if (et == null)
197                                 throw new ArgumentNullException ("et");
198                         if (et.Tag != tagName) {
199                                 string msg = String.Format ("Invalid tag {0} expected {1}", et.Tag, tagName);
200                                 throw new ArgumentException (msg, "et");
201                         }
202
203                         if (CodeAccessPermission.IsUnrestricted (et))
204                                 state = PermissionState.Unrestricted;
205                         else
206                                 state = PermissionState.None;
207
208                         list.Clear ();
209                         if (et.Children != null) {
210                                 foreach (SecurityElement se in et.Children) {
211                                         string className = se.Attribute ("class");
212                                         if (className == null) {
213                                                 throw new ArgumentException (Locale.GetText (
214                                                         "No permission class is specified."));
215                                         }
216                                         if (Resolver != null) {
217                                                 // policy class names do not have to be fully qualified
218                                                 className = Resolver.ResolveClassName (className);
219                                         }
220                                         // TODO: adjust class version with current runtime (unification)
221                                         // http://blogs.msdn.com/shawnfa/archive/2004/08/05/209320.aspx
222                                         Type classType = Type.GetType (className);
223                                         if (classType != null) {
224                                                 object [] psNone = new object [1] { PermissionState.None };
225                                                 IPermission p = (IPermission) Activator.CreateInstance (classType, psNone);
226                                                 p.FromXml (se);
227                                                 list.Add (p);
228                                         }
229                                 }
230                         }
231                 }
232
233                 public virtual IEnumerator GetEnumerator ()
234                 {
235                         return list.GetEnumerator ();
236                 }
237
238                 public virtual bool IsSubsetOf (PermissionSet target)
239                 {
240                         // if target is empty we must be empty too
241                         if ((target == null) || (target.IsEmpty ()))
242                                 return this.IsEmpty ();
243
244                         // TODO - non CAS permissions must be evaluated for unrestricted
245
246                         // if target is unrestricted then we are a subset
247                         if (!this.IsUnrestricted () && target.IsUnrestricted ())
248                                 return true;
249                         // else target isn't unrestricted.
250                         // so if we are unrestricted, the we can't be a subset
251                         if (this.IsUnrestricted () && !target.IsUnrestricted ())
252                                 return false;
253
254                         // if each of our permission is (a) present and (b) a subset of target
255                         foreach (IPermission p in list) {
256                                 // for every type in both list
257                                 IPermission i = target.GetPermission (p.GetType ());
258                                 if (i == null)
259                                         return false; // not present (condition a)
260                                 if (!p.IsSubsetOf (i))
261                                         return false; // not a subset (condition b)
262                         }
263                         return true;
264                 }
265
266                 [MonoTODO()]
267                 public virtual void PermitOnly ()
268                 {
269                 }
270
271                 public bool ContainsNonCodeAccessPermissions () 
272                 {
273                         foreach (IPermission p in list) {
274                                 if (! p.GetType ().IsSubclassOf (typeof (CodeAccessPermission)))
275                                         return true;
276                         }
277                         return false;
278                 }
279
280                 [MonoTODO ("little documentation in Fx 2.0 beta 1")]
281                 public static byte[] ConvertPermissionSet (string inFormat, byte[] inData, string outFormat) 
282                 {
283                         if (inFormat == null)
284                                 throw new ArgumentNullException ("inFormat");
285                         if (outFormat == null)
286                                 throw new ArgumentNullException ("outFormat");
287                         if (inData == null)
288                                 return null;
289
290                         if (inFormat == outFormat)
291                                 return inData;
292
293                         if (inFormat == "BINARY") {
294                                 if (outFormat.StartsWith ("XML")) {
295                                         // TODO - convert from binary format
296                                         return inData;
297                                 }
298                         }
299                         else if (inFormat.StartsWith ("XML")) {
300                                 if (outFormat == "BINARY") {
301                                         // TODO - convert to binary format
302                                         return inData;
303                                 }
304                                 else if (outFormat.StartsWith ("XML")) {
305                                         string msg = String.Format (Locale.GetText ("Can't convert from {0} to {1}"), inFormat, outFormat);
306                                         throw new XmlSyntaxException (msg);
307                                 }
308                         }
309                         else {
310                                 // unknown inFormat, returns null
311                                 return null;
312                         }
313                         // unknown outFormat, throw
314                         throw new SerializationException (String.Format (Locale.GetText ("Unknown output format {0}."), outFormat));
315                 }
316
317                 public virtual IPermission GetPermission (Type permClass) 
318                 {
319                         foreach (object o in list) {
320                                 if (o.GetType ().Equals (permClass))
321                                         return (IPermission) o;
322                         }
323                         // it's normal to return null for unrestricted sets
324                         return null;
325                 }
326
327                 public virtual PermissionSet Intersect (PermissionSet other) 
328                 {
329                         // no intersection possible
330                         if ((other == null) || (other.IsEmpty ()) || (this.IsEmpty ()))
331                                 return null;
332
333                         PermissionState state = PermissionState.None;
334                         if (this.IsUnrestricted () && other.IsUnrestricted ())
335                                 state = PermissionState.Unrestricted;
336
337                         PermissionSet interSet = new PermissionSet (state);
338                         if (state == PermissionState.Unrestricted) {
339                                 InternalIntersect (interSet, this, other, true);
340                                 InternalIntersect (interSet, other, this, true);
341                         }
342                         else if (this.IsUnrestricted ()) {
343                                 InternalIntersect (interSet, this, other, true);
344                         }
345                         else if (other.IsUnrestricted ()) {
346                                 InternalIntersect (interSet, other, this, true);
347                         }
348                         else {
349                                 InternalIntersect (interSet, this, other, false);
350                         }
351                         return interSet;
352                 }
353
354                 internal void InternalIntersect (PermissionSet intersect, PermissionSet a, PermissionSet b, bool unrestricted)
355                 {
356                         foreach (IPermission p in b.list) {
357                                 // for every type in both list
358                                 IPermission i = a.GetPermission (p.GetType ());
359                                 if (i != null) {
360                                         // add intersection for this type
361                                         intersect.AddPermission (p.Intersect (i));
362                                 }
363                                 else if (unrestricted && (p is IUnrestrictedPermission)) {
364                                         intersect.AddPermission (p);
365                                 }
366                                 // or reject!
367                         }
368                 }
369
370                 public virtual bool IsEmpty () 
371                 {
372                         // note: Unrestricted isn't empty
373                         if (state == PermissionState.Unrestricted)
374                                 return false;
375                         if ((list == null) || (list.Count == 0))
376                                 return true;
377                         // the set may include some empty permissions
378                         foreach (IPermission p in list) {
379                                 // an empty permission only has a class and/or version attributes
380                                 SecurityElement se = p.ToXml ();
381                                 int n = se.Attributes.Count;
382                                 if (n <= 2) {
383                                         if (se.Attribute ("class") != null)
384                                                 n--;
385                                         if (se.Attribute ("version") != null)
386                                                 n--;
387                                         if (n > 0)
388                                                 return false;   // not class or version - then not empty
389                                 }
390                                 else {
391                                         // too much attributes - then not empty
392                                         return false;
393                                 }
394                         }
395                         return true;
396                 }
397
398                 public virtual bool IsUnrestricted () 
399                 {
400                         return (state == PermissionState.Unrestricted);
401                 }
402
403                 public virtual IPermission RemovePermission (Type permClass) 
404                 {
405                         if (permClass == null)
406                                 return null;
407
408                         foreach (object o in list) {
409                                 if (o.GetType ().Equals (permClass)) {
410                                         list.Remove (o);
411                                         return (IPermission) o;
412                                 }
413                         }
414                         return null;
415                 }
416
417                 public virtual IPermission SetPermission (IPermission perm) 
418                 {
419                         if (perm == null)
420                                 return null;
421                         if (perm is IUnrestrictedPermission)
422                                 state = PermissionState.None;
423                         RemovePermission (perm.GetType ());
424                         list.Add (perm);
425                         return perm;
426                 }
427
428                 public override string ToString ()
429                 {
430                         return ToXml ().ToString ();
431                 }
432
433                 public virtual SecurityElement ToXml ()
434                 {
435                         SecurityElement se = new SecurityElement (tagName);
436                         se.AddAttribute ("class", GetType ().FullName);
437                         se.AddAttribute ("version", version.ToString ());
438                         if (state == PermissionState.Unrestricted)
439                                 se.AddAttribute ("Unrestricted", "true");
440
441                         // required for permissions that do not implement IUnrestrictedPermission
442                         foreach (IPermission p in list) {
443                                 se.AddChild (p.ToXml ());
444                         }
445                         return se;
446                 }
447
448                 public virtual PermissionSet Union (PermissionSet other)
449                 {
450                         if (other == null)
451                                 return this.Copy ();
452
453                         PermissionSet copy = this.Copy ();
454                         if (this.IsUnrestricted () || other.IsUnrestricted ()) {
455                                 // so we keep the "right" type
456                                 copy.Clear ();
457                                 copy.state = PermissionState.Unrestricted;
458                                 // copy all permissions that do not implement IUnrestrictedPermission
459                                 foreach (IPermission p in this.list) {
460                                         if (!(p is IUnrestrictedPermission))
461                                                 copy.AddPermission (p);
462                                 }
463                                 foreach (IPermission p in other.list) {
464                                         if (!(p is IUnrestrictedPermission))
465                                                 copy.AddPermission (p);
466                                 }
467                         }
468                         else {
469                                 // PermissionState.None -> copy all permissions
470                                 foreach (IPermission p in other.list) {
471                                         copy.AddPermission (p);
472                                 }
473                         }
474                         return copy;
475                 }
476
477                 public virtual int Count {
478                         get { return list.Count; }
479                 }
480
481                 public virtual bool IsSynchronized {
482                         get { return list.IsSynchronized; }
483                 }
484
485                 public virtual bool IsReadOnly {
486                         get { return false; } // always false
487                 }
488
489                 public virtual object SyncRoot {
490                         get { return this; }
491                 }
492
493                 [MonoTODO()]
494                 void IDeserializationCallback.OnDeserialization (object sender) 
495                 {
496                 }
497
498 #if NET_2_0
499                 public override bool Equals (object obj)
500                 {
501                         if (obj == null)
502                                 return false;
503                         PermissionSet ps = (obj as PermissionSet);
504                         if (ps == null)
505                                 return false;
506                         if (list.Count != ps.Count)
507                                 return false;
508
509                         for (int i=0; i < list.Count; i++) {
510                                 bool found = false;
511                                 for (int j=0; i < ps.list.Count; j++) {
512                                         if (list [i].Equals (ps.list [j])) {
513                                                 found = true;
514                                                 break;
515                                         }
516                                 }
517                                 if (!found)
518                                         return false;
519                         }
520                         return true;
521                 }
522
523                 public override int GetHashCode ()
524                 {
525                         if (list.Count == 0)
526                                 return (int) state;
527
528                         if (_hashcode == 0) {
529                                 _hashcode = state.GetHashCode ();
530                                 foreach (IPermission p in list) {
531                                         _hashcode ^= p.GetHashCode ();
532                                 }
533                         }
534                         return _hashcode;
535                 }
536
537                 [MonoTODO ("what's it doing here?")]
538                 static public void RevertAssert ()
539                 {
540                 }
541 #endif
542
543                 // internal
544
545                 internal PolicyLevel Resolver {
546                         get { return _policyLevel; }
547                         set { _policyLevel = value; }
548                 }
549         }
550 }