2 // System.Security.PermissionSet.cs
5 // Nick Drochak(ndrochak@gol.com)
6 // Sebastien Pouliot <sebastien@ximian.com>
9 // Portions (C) 2003, 2004 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004-2005 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.Diagnostics;
35 using System.Reflection;
36 using System.Runtime.InteropServices;
37 using System.Runtime.Serialization;
38 using System.Runtime.Serialization.Formatters.Binary;
39 using System.Security.Permissions;
40 using System.Security.Policy;
42 using System.Threading;
44 namespace System.Security {
47 public class PermissionSet: ISecurityEncodable, ICollection, IEnumerable, IStackWalk, IDeserializationCallback {
49 private static string tagName = "PermissionSet";
50 private const int version = 1;
51 private static object[] psNone = new object [1] { PermissionState.None };
53 private PermissionState state;
54 private ArrayList list;
55 private int _hashcode;
56 private PolicyLevel _policyLevel;
57 private bool _declsec;
61 // for PolicyLevel (to avoid validation duplication)
62 internal PermissionSet ()
64 list = new ArrayList ();
67 public PermissionSet (PermissionState state) : this ()
69 if (!Enum.IsDefined (typeof (PermissionState), state))
70 throw new System.ArgumentException ("state");
74 public PermissionSet (PermissionSet permSet) : this ()
76 // LAMESPEC: This would be handled by the compiler. No way permSet is not a PermissionSet.
77 //if (!(permSet is PermissionSet))
78 // throw new System.ArgumentException(); // permSet is not an instance of System.Security.PermissionSet.
80 state = PermissionState.Unrestricted;
82 state = permSet.state;
83 foreach (IPermission p in permSet.list)
88 internal PermissionSet (string xml)
91 state = PermissionState.None;
93 SecurityElement se = SecurityElement.FromString (xml);
98 // Light version for creating a (non unrestricted) PermissionSet with
99 // a single permission. This allows to relax most validations.
100 internal PermissionSet (IPermission perm)
104 // note: we do not copy IPermission like AddPermission
111 public virtual IPermission AddPermission (IPermission perm)
116 // we don't add to an unrestricted permission set unless...
117 if (state == PermissionState.Unrestricted) {
118 // we're adding identity permission as they don't support unrestricted
119 if (perm is IUnrestrictedPermission) {
120 // we return the union of the permission with unrestricted
121 // which results in a permission of the same type initialized
122 // with PermissionState.Unrestricted
123 object[] args = new object [1] { PermissionState.Unrestricted };
124 return (IPermission) Activator.CreateInstance (perm.GetType (), args);
128 // we can't add two permissions of the same type in a set
129 // so we remove an existing one, union with it and add it back
130 IPermission existing = RemovePermission (perm.GetType ());
131 if (existing != null) {
132 perm = perm.Union (existing);
135 // note: Add doesn't copy
140 [MonoTODO ("Imperative mode isn't supported")]
141 public virtual void Assert ()
143 new SecurityPermission (SecurityPermissionFlag.Assertion).Demand ();
145 int count = this.Count;
147 // we (current frame) must have the permission to assert it to others
148 // otherwise we don't assert (but we don't throw an exception)
149 foreach (IPermission p in list) {
150 // note: we ignore non-CAS permissions
151 if (p is IStackWalk) {
152 if (!SecurityManager.IsGranted (p)) {
159 // note: we must ignore the stack modifiers for the non-CAS permissions
161 throw new NotSupportedException ("Currently only declarative Assert are supported.");
164 internal void Clear ()
169 public virtual PermissionSet Copy ()
171 return new PermissionSet (this);
174 public virtual void CopyTo (Array array, int index)
177 throw new ArgumentNullException ("array");
179 if (list.Count > 0) {
180 if (array.Rank > 1) {
181 throw new ArgumentException (Locale.GetText (
182 "Array has more than one dimension"));
184 if (index < 0 || index >= array.Length) {
185 throw new IndexOutOfRangeException ("index");
188 list.CopyTo (array, index);
192 [MonoTODO ("Imperative Assert, Deny and PermitOnly aren't yet supported")]
193 public virtual void Demand ()
195 // Note: SecurityEnabled only applies to CAS permissions
196 // so we're not checking for it (yet)
200 PermissionSet cas = this;
201 // avoid copy (if possible)
202 if (ContainsNonCodeAccessPermissions ()) {
203 // non CAS permissions (e.g. PrincipalPermission) do not requires a stack walk
205 foreach (IPermission p in list) {
206 Type t = p.GetType ();
207 if (!t.IsSubclassOf (typeof (CodeAccessPermission))) {
209 // we wont have to process this one in the stack walk
210 cas.RemovePermission (t);
215 // don't start the stack walk if
216 // - the permission set only contains non CAS permissions; or
217 // - security isn't enabled (applis only to CAS!)
218 if ((cas.Count > 0) && SecurityManager.SecurityEnabled)
219 CasOnlyDemand (_declsec ? 3 : 2);
222 // The number of frames to skip depends on who's calling
223 // - CodeAccessPermission.Demand (imperative)
224 // - PermissionSet.Demand (imperative)
225 // - SecurityManager.InternalDemand (declarative)
226 internal void CasOnlyDemand (int skip)
228 Assembly current = null;
230 // skip ourself, Demand and other security runtime methods
231 foreach (SecurityFrame sf in SecurityFrame.GetStack (skip)) {
232 if (ProcessFrame (sf, ref current))
233 return; // reached Assert
236 // Is there a CompressedStack to handle ?
237 CompressedStack stack = Thread.CurrentThread.GetCompressedStack ();
238 if ((stack != null) && !stack.IsEmpty ()) {
239 foreach (SecurityFrame frame in stack.List) {
240 if (ProcessFrame (frame, ref current))
241 return; // reached Assert
246 [MonoTODO ("Imperative mode isn't supported")]
247 public virtual void Deny ()
249 foreach (IPermission p in list) {
250 // note: we ignore non-CAS permissions
251 if (p is IStackWalk) {
252 throw new NotSupportedException ("Currently only declarative Deny are supported.");
257 [MonoTODO ("adjust class version with current runtime - unification")]
258 public virtual void FromXml (SecurityElement et)
261 throw new ArgumentNullException ("et");
262 if (et.Tag != tagName) {
263 string msg = String.Format ("Invalid tag {0} expected {1}", et.Tag, tagName);
264 throw new ArgumentException (msg, "et");
267 if (CodeAccessPermission.IsUnrestricted (et))
268 state = PermissionState.Unrestricted;
270 state = PermissionState.None;
273 if (et.Children != null) {
274 foreach (SecurityElement se in et.Children) {
275 string className = se.Attribute ("class");
276 if (className == null) {
277 throw new ArgumentException (Locale.GetText (
278 "No permission class is specified."));
280 if (Resolver != null) {
281 // policy class names do not have to be fully qualified
282 className = Resolver.ResolveClassName (className);
284 // TODO: adjust class version with current runtime (unification)
285 // http://blogs.msdn.com/shawnfa/archive/2004/08/05/209320.aspx
286 Type classType = Type.GetType (className);
287 if (classType != null) {
288 IPermission p = (IPermission) Activator.CreateInstance (classType, psNone);
294 string msg = Locale.GetText ("Can't create an instance of permission class {0}.");
295 throw new ArgumentException (String.Format (msg, se.Attribute ("class")));
302 public virtual IEnumerator GetEnumerator ()
304 return list.GetEnumerator ();
307 public virtual bool IsSubsetOf (PermissionSet target)
309 // if target is empty we must be empty too
310 if ((target == null) || (target.IsEmpty ()))
311 return this.IsEmpty ();
313 // TODO - non CAS permissions must be evaluated for unrestricted
315 // if target is unrestricted then we are a subset
316 if (!this.IsUnrestricted () && target.IsUnrestricted ())
318 // else target isn't unrestricted.
319 // so if we are unrestricted, the we can't be a subset
320 if (this.IsUnrestricted () && !target.IsUnrestricted ())
323 // if each of our permission is (a) present and (b) a subset of target
324 foreach (IPermission p in list) {
325 // for every type in both list
326 IPermission i = target.GetPermission (p.GetType ());
328 return false; // not present (condition a)
329 if (!p.IsSubsetOf (i))
330 return false; // not a subset (condition b)
335 [MonoTODO ("Imperative mode isn't supported")]
336 public virtual void PermitOnly ()
338 foreach (IPermission p in list) {
339 // note: we ignore non-CAS permissions
340 if (p is IStackWalk) {
341 throw new NotSupportedException ("Currently only declarative Deny are supported.");
346 public bool ContainsNonCodeAccessPermissions ()
348 foreach (IPermission p in list) {
349 if (! p.GetType ().IsSubclassOf (typeof (CodeAccessPermission)))
355 [MonoTODO ("little documentation in Fx 2.0 beta 1")]
356 public static byte[] ConvertPermissionSet (string inFormat, byte[] inData, string outFormat)
358 if (inFormat == null)
359 throw new ArgumentNullException ("inFormat");
360 if (outFormat == null)
361 throw new ArgumentNullException ("outFormat");
365 if (inFormat == outFormat)
368 PermissionSet ps = null;
370 if (inFormat == "BINARY") {
371 if (outFormat.StartsWith ("XML")) {
372 using (MemoryStream ms = new MemoryStream (inData)) {
373 BinaryFormatter formatter = new BinaryFormatter ();
374 ps = (PermissionSet) formatter.Deserialize (ms);
377 string xml = ps.ToString ();
381 return Encoding.ASCII.GetBytes (xml);
383 return Encoding.Unicode.GetBytes (xml);
387 else if (inFormat.StartsWith ("XML")) {
388 if (outFormat == "BINARY") {
393 xml = Encoding.ASCII.GetString (inData);
396 xml = Encoding.Unicode.GetString (inData);
400 ps = new PermissionSet (PermissionState.None);
401 ps.FromXml (SecurityElement.FromString (xml));
403 MemoryStream ms = new MemoryStream ();
404 BinaryFormatter formatter = new BinaryFormatter ();
405 formatter.Serialize (ms, ps);
407 return ms.ToArray ();
410 else if (outFormat.StartsWith ("XML")) {
411 string msg = String.Format (Locale.GetText ("Can't convert from {0} to {1}"), inFormat, outFormat);
413 throw new XmlSyntaxException (msg);
415 throw new ArgumentException (msg);
420 // unknown inFormat, returns null
423 // unknown outFormat, throw
424 throw new SerializationException (String.Format (Locale.GetText ("Unknown output format {0}."), outFormat));
427 public virtual IPermission GetPermission (Type permClass)
429 foreach (object o in list) {
430 if (o.GetType ().Equals (permClass))
431 return (IPermission) o;
433 // it's normal to return null for unrestricted sets
437 public virtual PermissionSet Intersect (PermissionSet other)
439 // no intersection possible
440 if ((other == null) || (other.IsEmpty ()) || (this.IsEmpty ()))
443 PermissionState state = PermissionState.None;
444 if (this.IsUnrestricted () && other.IsUnrestricted ())
445 state = PermissionState.Unrestricted;
447 PermissionSet interSet = new PermissionSet (state);
448 if (state == PermissionState.Unrestricted) {
449 InternalIntersect (interSet, this, other, true);
450 InternalIntersect (interSet, other, this, true);
452 else if (this.IsUnrestricted ()) {
453 InternalIntersect (interSet, this, other, true);
455 else if (other.IsUnrestricted ()) {
456 InternalIntersect (interSet, other, this, true);
459 InternalIntersect (interSet, this, other, false);
464 internal void InternalIntersect (PermissionSet intersect, PermissionSet a, PermissionSet b, bool unrestricted)
466 foreach (IPermission p in b.list) {
467 // for every type in both list
468 IPermission i = a.GetPermission (p.GetType ());
470 // add intersection for this type
471 intersect.AddPermission (p.Intersect (i));
473 else if (unrestricted && (p is IUnrestrictedPermission)) {
474 intersect.AddPermission (p);
480 public virtual bool IsEmpty ()
482 // note: Unrestricted isn't empty
483 if (state == PermissionState.Unrestricted)
485 if ((list == null) || (list.Count == 0))
487 // the set may include some empty permissions
488 foreach (IPermission p in list) {
489 // empty == fully restricted == IsSubsetOg(null) == true
490 if (!p.IsSubsetOf (null))
496 public virtual bool IsUnrestricted ()
498 return (state == PermissionState.Unrestricted);
501 public virtual IPermission RemovePermission (Type permClass)
503 if (permClass == null)
506 foreach (object o in list) {
507 if (o.GetType ().Equals (permClass)) {
509 return (IPermission) o;
515 public virtual IPermission SetPermission (IPermission perm)
519 if (perm is IUnrestrictedPermission)
520 state = PermissionState.None;
521 RemovePermission (perm.GetType ());
526 public override string ToString ()
528 return ToXml ().ToString ();
531 public virtual SecurityElement ToXml ()
533 SecurityElement se = new SecurityElement (tagName);
534 se.AddAttribute ("class", GetType ().FullName);
535 se.AddAttribute ("version", version.ToString ());
536 if (state == PermissionState.Unrestricted)
537 se.AddAttribute ("Unrestricted", "true");
539 // required for permissions that do not implement IUnrestrictedPermission
540 foreach (IPermission p in list) {
541 se.AddChild (p.ToXml ());
546 public virtual PermissionSet Union (PermissionSet other)
551 PermissionSet copy = this.Copy ();
552 if (this.IsUnrestricted () || other.IsUnrestricted ()) {
553 // so we keep the "right" type
555 copy.state = PermissionState.Unrestricted;
556 // copy all permissions that do not implement IUnrestrictedPermission
557 foreach (IPermission p in this.list) {
558 if (!(p is IUnrestrictedPermission))
559 copy.AddPermission (p);
561 foreach (IPermission p in other.list) {
562 if (!(p is IUnrestrictedPermission))
563 copy.AddPermission (p);
567 // PermissionState.None -> copy all permissions
568 foreach (IPermission p in other.list) {
569 copy.AddPermission (p);
575 public virtual int Count {
576 get { return list.Count; }
579 public virtual bool IsSynchronized {
580 get { return list.IsSynchronized; }
583 public virtual bool IsReadOnly {
584 get { return false; } // always false
587 public virtual object SyncRoot {
591 internal bool DeclarativeSecurity {
592 get { return _declsec; }
593 set { _declsec = value; }
597 void IDeserializationCallback.OnDeserialization (object sender)
603 public override bool Equals (object obj)
607 PermissionSet ps = (obj as PermissionSet);
610 if (list.Count != ps.Count)
613 for (int i=0; i < list.Count; i++) {
615 for (int j=0; i < ps.list.Count; j++) {
616 if (list [i].Equals (ps.list [j])) {
628 public override int GetHashCode ()
630 return (list.Count == 0) ? (int) state : base.GetHashCode ();
633 [MonoTODO ("what's it doing here?")]
634 static public void RevertAssert ()
636 // FIXME: There's probably a reason this was added here ?
637 CodeAccessPermission.RevertAssert ();
643 internal PolicyLevel Resolver {
644 get { return _policyLevel; }
645 set { _policyLevel = value; }
649 internal void ImmediateCallerDemand ()
655 SecurityFrame sf = new SecurityFrame (1); // FIXME skip
656 foreach (IPermission p in list) {
657 // note: this may contains non CAS permissions
658 if (p is CodeAccessPermission) {
659 if (SecurityManager.SecurityEnabled)
660 SecurityManager.IsGranted (sf.Assembly, p);
667 // Note: Non-CAS demands aren't affected by SecurityManager.SecurityEnabled
668 internal void ImmediateCallerNonCasDemand ()
673 // non CAS permissions (e.g. PrincipalPermission) requires direct call to Demand
674 foreach (IPermission p in list) {
679 internal bool ProcessFrame (SecurityFrame frame, ref Assembly current)
681 foreach (CodeAccessPermission cap in list) {
682 if (cap.ProcessFrame (frame, ref current))
683 return true; // Assert reached - abort stack walk!