3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 // <OWNER>Microsoft</OWNER>
10 // Representation for code groups used for the policy mechanism
13 namespace System.Security.Policy {
16 using System.Security.Util;
17 using System.Security;
18 using System.Collections;
19 using System.Globalization;
20 using System.Diagnostics.Contracts;
22 internal interface IUnionSemanticCodeGroup
24 PolicyStatement InternalResolve( Evidence evidence );
28 [System.Runtime.InteropServices.ComVisible(true)]
29 abstract public class CodeGroup
31 private IMembershipCondition m_membershipCondition;
32 private IList m_children;
33 private PolicyStatement m_policy;
34 private SecurityElement m_element;
35 private PolicyLevel m_parentLevel;
36 private String m_name;
37 private String m_description;
43 internal CodeGroup( IMembershipCondition membershipCondition, PermissionSet permSet )
45 Contract.Assert( membershipCondition != null, "membershipCondition != null" );
46 Contract.Assert( permSet != null, "permSet != null" );
48 m_membershipCondition = membershipCondition;
49 m_policy = new PolicyStatement();
50 m_policy.SetPermissionSetNoCopy( permSet );
51 m_children = ArrayList.Synchronized( new ArrayList() );
56 protected CodeGroup( IMembershipCondition membershipCondition, PolicyStatement policy )
58 if (membershipCondition == null)
59 throw new ArgumentNullException( "membershipCondition" );
60 Contract.EndContractBlock();
65 m_policy = policy.Copy();
67 m_membershipCondition = membershipCondition.Copy();
68 m_children = ArrayList.Synchronized( new ArrayList() );
73 [System.Security.SecuritySafeCritical] // auto-generated
74 public void AddChild( CodeGroup group )
77 throw new ArgumentNullException("group");
78 Contract.EndContractBlock();
80 if (m_children == null)
85 m_children.Add( group.Copy() );
89 [System.Security.SecurityCritical] // auto-generated
90 internal void AddChildInternal( CodeGroup group )
93 throw new ArgumentNullException("group");
94 Contract.EndContractBlock();
96 if (m_children == null)
101 m_children.Add( group );
105 [System.Security.SecuritySafeCritical] // auto-generated
106 public void RemoveChild( CodeGroup group )
111 if (m_children == null)
116 int index = m_children.IndexOf( group );
120 m_children.RemoveAt( index );
125 public IList Children
127 [System.Security.SecuritySafeCritical] // auto-generated
130 if (m_children == null)
135 IList newList = new ArrayList( m_children.Count );
137 IEnumerator enumerator = m_children.GetEnumerator();
139 while (enumerator.MoveNext())
141 newList.Add( ((CodeGroup)enumerator.Current).Copy() );
151 throw new ArgumentNullException( "Children" );
152 Contract.EndContractBlock();
154 ArrayList children = ArrayList.Synchronized( new ArrayList( value.Count ) );
156 IEnumerator enumerator = value.GetEnumerator();
158 while (enumerator.MoveNext())
160 CodeGroup group = enumerator.Current as CodeGroup;
163 throw new ArgumentException( Environment.GetResourceString( "Argument_CodeGroupChildrenMustBeCodeGroups" ) );
165 children.Add( group.Copy() );
168 m_children = children;
172 [System.Security.SecurityCritical] // auto-generated
173 internal IList GetChildrenInternal()
175 if (m_children == null)
181 public IMembershipCondition MembershipCondition
183 [System.Security.SecuritySafeCritical] // auto-generated
186 if (m_membershipCondition == null && m_element != null)
187 ParseMembershipCondition();
189 return m_membershipCondition.Copy();
195 throw new ArgumentNullException( "MembershipCondition" );
196 Contract.EndContractBlock();
198 m_membershipCondition = value.Copy();
202 public PolicyStatement PolicyStatement
206 if (m_policy == null && m_element != null)
209 if (m_policy != null)
210 return m_policy.Copy();
218 m_policy = value.Copy();
237 public String Description
241 return m_description;
246 m_description = value;
250 public abstract PolicyStatement Resolve( Evidence evidence );
252 public abstract CodeGroup ResolveMatchingCodeGroups( Evidence evidence );
254 public abstract CodeGroup Copy();
256 public virtual String PermissionSetName
260 if (m_policy == null && m_element != null)
263 if (m_policy == null)
266 NamedPermissionSet permSet = m_policy.GetPermissionSetNoCopy() as NamedPermissionSet;
279 public virtual String AttributeString
283 if (m_policy == null && m_element != null)
286 if (m_policy != null)
287 return m_policy.AttributeString;
293 public abstract String MergeLogic
299 public SecurityElement ToXml()
301 return ToXml( null );
304 public void FromXml( SecurityElement e )
309 [System.Security.SecuritySafeCritical] // auto-generated
310 public SecurityElement ToXml( PolicyLevel level )
312 return ToXml( level, GetTypeName() );
315 internal virtual String GetTypeName()
317 return this.GetType().FullName;
320 [System.Security.SecurityCritical] // auto-generated
321 internal SecurityElement ToXml( PolicyLevel level, String policyClassName )
323 if (m_membershipCondition == null && m_element != null)
324 ParseMembershipCondition();
326 if (m_children == null)
329 if (m_policy == null && m_element != null)
332 SecurityElement e = new SecurityElement( "CodeGroup" );
333 System.Security.Util.XMLUtil.AddClassAttribute( e, this.GetType(), policyClassName );
334 // If you hit this assert then most likely you are trying to change the name of this class.
335 // This is ok as long as you change the hard coded string above and change the assert below.
336 Contract.Assert( this.GetType().FullName.Equals( policyClassName ), "Incorrect class name passed in! Was: " + policyClassName + " Should be " + this.GetType().FullName);
338 e.AddAttribute( "version", "1" );
340 e.AddChild( m_membershipCondition.ToXml( level ) );
342 // Grab the inerts of the policy statement's xml and just stick it
343 // into the code group xml directly. We do this to hide the policy statement from
344 // users in the config file.
346 if (m_policy != null)
348 PermissionSet permSet = m_policy.GetPermissionSetNoCopy();
349 NamedPermissionSet namedPermSet = permSet as NamedPermissionSet;
351 if (namedPermSet != null && level != null && level.GetNamedPermissionSetInternal( namedPermSet.Name ) != null)
353 e.AddAttribute( "PermissionSetName", namedPermSet.Name );
357 if (!permSet.IsEmpty())
358 e.AddChild( permSet.ToXml() );
361 if (m_policy.Attributes != PolicyStatementAttribute.Nothing)
362 e.AddAttribute( "Attributes", XMLUtil.BitFieldEnumToString( typeof( PolicyStatementAttribute ), m_policy.Attributes ) );
365 if (m_children.Count > 0)
369 IEnumerator enumerator = m_children.GetEnumerator();
371 while (enumerator.MoveNext())
373 e.AddChild( ((CodeGroup)enumerator.Current).ToXml( level ) );
380 e.AddAttribute( "Name", SecurityElement.Escape( m_name ) );
383 if (m_description != null)
385 e.AddAttribute( "Description", SecurityElement.Escape( m_description ) );
388 CreateXml( e, level );
393 protected virtual void CreateXml( SecurityElement element, PolicyLevel level )
397 public void FromXml( SecurityElement e, PolicyLevel level )
400 throw new ArgumentNullException("e");
401 Contract.EndContractBlock();
406 m_parentLevel = level;
408 m_membershipCondition = null;
411 m_name = e.Attribute( "Name" );
412 m_description = e.Attribute( "Description" );
414 ParseXml( e, level );
418 protected virtual void ParseXml( SecurityElement e, PolicyLevel level )
422 [System.Security.SecurityCritical] // auto-generated
423 private bool ParseMembershipCondition( bool safeLoad )
427 IMembershipCondition membershipCondition = null;
428 SecurityElement elMembershipCondition = m_element.SearchForChildByTag( "IMembershipCondition" );
429 if (elMembershipCondition != null)
433 membershipCondition = System.Security.Util.XMLUtil.CreateMembershipCondition( elMembershipCondition );
435 if (membershipCondition == null)
440 throw new ArgumentException( Environment.GetResourceString( "Argument_MembershipConditionElement" ), ex );
444 // throw new ArgumentException( Environment.GetResourceString( "Argument_MembershipConditionElement" ) );
446 membershipCondition.FromXml( elMembershipCondition, m_parentLevel );
450 throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidXMLElement" ), "IMembershipCondition", this.GetType().FullName ) );
453 m_membershipCondition = membershipCondition;
458 [System.Security.SecurityCritical] // auto-generated
459 private void ParseMembershipCondition()
461 ParseMembershipCondition( false );
464 [System.Security.SecurityCritical] // auto-generated
465 internal void ParseChildren()
469 ArrayList childrenList = ArrayList.Synchronized( new ArrayList() );
471 if (m_element != null && m_element.InternalChildren != null)
473 // We set the elements childrenList to a list that contains no code groups that are in
474 // assemblies that have not yet been loaded. This guarantees that if
475 // we recurse while loading the child code groups or their membership conditions
476 // that we won't get back to this point to create an infinite recursion.
478 m_element.Children = (ArrayList)m_element.InternalChildren.Clone();
480 // We need to keep track of which children are not parsed and created in our
481 // first pass through the list of children, including what position they were
482 // at originally so that we can add them back in as we do parse them.
484 ArrayList unparsedChildren = ArrayList.Synchronized( new ArrayList() );
486 Evidence evidence = new Evidence();
488 int childCount = m_element.InternalChildren.Count;
490 while (i < childCount)
492 SecurityElement elGroup = (SecurityElement)m_element.Children[i];
494 if (elGroup.Tag.Equals( "CodeGroup" ))
496 // Using safe load here to guarantee that we don't load any assemblies that aren't
497 // already loaded. If we find a code group or membership condition that is defined
498 // in an assembly that is not yet loaded, we will remove the corresponding element
499 // from the list of child elements as to avoid the infinite recursion, and then
500 // add them back in later.
502 CodeGroup group = System.Security.Util.XMLUtil.CreateCodeGroup( elGroup );
506 group.FromXml( elGroup, m_parentLevel );
508 // We want to touch the membership condition to make sure it is loaded
509 // before we put the code group in a place where Resolve will touch it.
510 // This is critical in negotiating our recursive resolve scenario.
512 if (ParseMembershipCondition( true ))
514 // In addition, we need to touch several methods to make sure they are jitted previous
515 // to a Resolve happening with this code gropu in the hierarchy. We can run into
516 // recursive cases where if you have a method that touchs an assembly that does
517 // a resolve at load time (permission request, execution checking) that you recurse around
518 // and end up trying to jit the same method again.
520 group.Resolve( evidence );
521 group.MembershipCondition.Check( evidence );
523 // Now it should be ok to add the group to the hierarchy.
525 childrenList.Add( group );
527 // Increment the count since we are done with this child
533 // Assembly that holds the membership condition is not loaded, remove
534 // the child from the list.
536 m_element.InternalChildren.RemoveAt( i );
538 // Note: we do not increment the counter since the value at 'i' should
539 // now be what was at 'i+1' previous to the RemoveAt( i ) above. However,
540 // we do need to update the count of children in the list
542 childCount = m_element.InternalChildren.Count;
544 // Add this child to the unparsed child list.
546 unparsedChildren.Add( new CodeGroupPositionMarker( i, childrenList.Count, elGroup ) );
551 // Assembly that holds the code group is not loaded, remove
552 // the child from the list.
554 m_element.InternalChildren.RemoveAt( i );
556 // Note: we do not increment the counter since the value at 'i' should
557 // now be what was at 'i+1' previous to the RemoveAt( i ) above. However,
558 // we do need to update the count of children in the list
560 childCount = m_element.InternalChildren.Count;
562 // Add this child to the unparsed child list.
564 unparsedChildren.Add( new CodeGroupPositionMarker( i, childrenList.Count, elGroup ) );
569 // The current tag is not an <CodeGroup> tag, so we just skip it.
575 // Now we have parsed all the children that only use classes in already loaded
576 // assemblies. Now go through the process of loading the needed classes (and
577 // therefore assemblies) and building the objects in the order that they
578 // appear in the list of children (which is the same as they now appear in the
579 // list of unparsed children since we always added to the back of the list).
580 // As each is parsed, add that child back into the list of children since they
581 // can now be parsed without loading any additional assemblies.
583 IEnumerator enumerator = unparsedChildren.GetEnumerator();
585 while (enumerator.MoveNext())
587 CodeGroupPositionMarker marker = (CodeGroupPositionMarker)enumerator.Current;
589 CodeGroup group = System.Security.Util.XMLUtil.CreateCodeGroup( marker.element );
593 group.FromXml( marker.element, m_parentLevel );
595 // We want to touch the membership condition to make sure it is loaded
596 // before we put the code group in a place where Resolve will touch it.
597 // This is critical in negotiating our recursive resolve scenario.
599 group.Resolve( evidence );
600 group.MembershipCondition.Check( evidence );
602 // Now it should be ok to add the group to the hierarchy.
604 childrenList.Insert( marker.groupIndex, group );
606 // Add the element back into the child list in the proper spot.
608 m_element.InternalChildren.Insert( marker.elementIndex, marker.element );
612 throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_FailedCodeGroup" ), marker.element.Attribute( "class" ) ) );
617 m_children = childrenList;
622 private void ParsePolicy()
624 // There is a potential deadlock situation here
625 // since the PolicyStatement.FromXml method calls
626 // into PolicyLevel and we are holding this CodeGroup's lock.
627 // We solve this by releasing the lock for the duration of
628 // the FromXml call, but this leads us into some race conditions
629 // with other threads trying to alter the state of this object.
630 // The trickiest of these is the case from FromXml gets called on
631 // this object, in which case we will loop and try the decode again.
635 PolicyStatement policy = new PolicyStatement();
636 bool needToParse = false;
638 SecurityElement elPolicy = new SecurityElement( "PolicyStatement" );
639 elPolicy.AddAttribute( "version", "1" );
641 SecurityElement localRef = m_element;
646 // We create an xml representation of a policy statement from the
647 // xml for a code group. We do this to hide the policy statement from
648 // users in the config file.
650 if (m_element != null)
652 String permSetName = m_element.Attribute( "PermissionSetName" );
654 if (permSetName != null)
656 elPolicy.AddAttribute( "PermissionSetName", permSetName );
661 SecurityElement elPermSet = m_element.SearchForChildByTag( "PermissionSet" );
663 if (elPermSet != null)
665 elPolicy.AddChild( elPermSet );
670 elPolicy.AddChild( new PermissionSet( false ).ToXml() );
675 String attributes = m_element.Attribute( "Attributes" );
677 if (attributes != null)
679 elPolicy.AddAttribute( "Attributes", attributes );
686 policy.FromXml( elPolicy, m_parentLevel );
688 policy.PermissionSet = null;
692 if (localRef == m_element && m_policy == null)
697 else if (m_policy != null)
704 if (m_policy != null && m_children != null && m_membershipCondition != null)
707 //m_parentLevel = null;
712 [System.Security.SecuritySafeCritical] // auto-generated
713 public override bool Equals( Object o)
715 CodeGroup that = (o as CodeGroup);
717 if (that != null && this.GetType().Equals( that.GetType() ))
719 if (Equals( this.m_name, that.m_name ) &&
720 Equals( this.m_description, that.m_description ))
722 if (this.m_membershipCondition == null && this.m_element != null)
723 this.ParseMembershipCondition();
724 if (that.m_membershipCondition == null && that.m_element != null)
725 that.ParseMembershipCondition();
727 if (Equals( this.m_membershipCondition, that.m_membershipCondition ))
736 [System.Security.SecuritySafeCritical] // auto-generated
737 public bool Equals( CodeGroup cg, bool compareChildren)
739 if (!this.Equals( cg )) return false;
743 if (this.m_children == null)
744 this.ParseChildren();
745 if (cg.m_children == null)
748 ArrayList list1 = new ArrayList(this.m_children);
749 ArrayList list2 = new ArrayList(cg.m_children);
751 if (list1.Count != list2.Count) return false;
753 for (int i = 0; i < list1.Count; i++)
755 if (!((CodeGroup) list1[i]).Equals( (CodeGroup) list2[i], true ))
765 [System.Security.SecuritySafeCritical] // auto-generated
766 public override int GetHashCode()
768 if (m_membershipCondition == null && m_element != null)
769 ParseMembershipCondition();
771 if (m_name != null || m_membershipCondition != null)
773 return (m_name == null ? 0 : m_name.GetHashCode())
774 + (m_membershipCondition == null ? 0 : m_membershipCondition.GetHashCode());
778 return GetType().GetHashCode();
783 internal class CodeGroupPositionMarker
785 internal int elementIndex;
786 internal int groupIndex;
787 internal SecurityElement element;
789 internal CodeGroupPositionMarker( int elementIndex, int groupIndex, SecurityElement element )
791 this.elementIndex = elementIndex;
792 this.groupIndex = groupIndex;
793 this.element = element;