Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / policy / codegroup.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 //  CodeGroup.cs
7 // 
8 // <OWNER>Microsoft</OWNER>
9 //
10 //  Representation for code groups used for the policy mechanism
11 //
12
13 namespace System.Security.Policy {
14     
15     using System;
16     using System.Security.Util;
17     using System.Security;
18     using System.Collections;
19     using System.Globalization;
20     using System.Diagnostics.Contracts;
21     
22     internal interface IUnionSemanticCodeGroup
23     {
24         PolicyStatement InternalResolve( Evidence evidence );
25     }
26
27     [Serializable]
28     [System.Runtime.InteropServices.ComVisible(true)]
29     abstract public class CodeGroup
30     {
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;
38         
39         internal CodeGroup()
40         {
41         }
42         
43         internal CodeGroup( IMembershipCondition membershipCondition, PermissionSet permSet )
44         {
45             Contract.Assert( membershipCondition != null, "membershipCondition != null" );
46             Contract.Assert( permSet != null, "permSet != null" );
47
48             m_membershipCondition = membershipCondition;
49             m_policy = new PolicyStatement();
50             m_policy.SetPermissionSetNoCopy( permSet );
51             m_children = ArrayList.Synchronized( new ArrayList() );
52             m_element = null;
53             m_parentLevel = null;
54         }
55         
56         protected CodeGroup( IMembershipCondition membershipCondition, PolicyStatement policy )
57         {
58             if (membershipCondition == null)
59                 throw new ArgumentNullException( "membershipCondition" );
60             Contract.EndContractBlock();
61
62             if (policy == null)
63                 m_policy = null;
64             else
65                 m_policy = policy.Copy();
66         
67             m_membershipCondition = membershipCondition.Copy();
68             m_children = ArrayList.Synchronized( new ArrayList() );
69             m_element = null;
70             m_parentLevel = null;
71         }
72         
73         [System.Security.SecuritySafeCritical]  // auto-generated
74         public void AddChild( CodeGroup group )
75         {
76             if (group == null)
77                 throw new ArgumentNullException("group");
78             Contract.EndContractBlock();
79                 
80             if (m_children == null)
81                 ParseChildren();
82             
83             lock (this)
84             {
85                 m_children.Add( group.Copy() );
86             }
87         }
88
89         [System.Security.SecurityCritical]  // auto-generated
90         internal void AddChildInternal( CodeGroup group )
91         {
92             if (group == null)
93                 throw new ArgumentNullException("group");
94             Contract.EndContractBlock();
95                 
96             if (m_children == null)
97                 ParseChildren();
98
99             lock (this)
100             {
101                 m_children.Add( group );
102             }
103         }            
104         
105         [System.Security.SecuritySafeCritical]  // auto-generated
106         public void RemoveChild( CodeGroup group )
107         {
108             if (group == null)
109                 return;
110         
111             if (m_children == null)
112                 ParseChildren();
113             
114             lock (this )
115             {
116                 int index = m_children.IndexOf( group );
117             
118                 if (index != -1)
119                 {
120                     m_children.RemoveAt( index );
121                 }
122             }
123         }        
124         
125         public IList Children
126         {
127             [System.Security.SecuritySafeCritical]  // auto-generated
128             get
129             {
130                 if (m_children == null)
131                     ParseChildren();
132         
133                 lock (this)
134                 {
135                     IList newList = new ArrayList( m_children.Count );
136
137                     IEnumerator enumerator = m_children.GetEnumerator();
138
139                     while (enumerator.MoveNext())
140                     {
141                         newList.Add( ((CodeGroup)enumerator.Current).Copy() );
142                     }
143
144                     return newList;
145                 }
146             }
147
148             set
149             {
150                 if (value == null)
151                     throw new ArgumentNullException( "Children" );
152                 Contract.EndContractBlock();
153
154                 ArrayList children = ArrayList.Synchronized( new ArrayList( value.Count ) );
155
156                 IEnumerator enumerator = value.GetEnumerator();
157
158                 while (enumerator.MoveNext())
159                 {
160                     CodeGroup group = enumerator.Current as CodeGroup;
161
162                     if (group == null)
163                         throw new ArgumentException( Environment.GetResourceString( "Argument_CodeGroupChildrenMustBeCodeGroups" ) );
164
165                     children.Add( group.Copy() );
166                 }
167
168                 m_children = children;
169             }
170         }
171         
172         [System.Security.SecurityCritical]  // auto-generated
173         internal IList GetChildrenInternal()
174         {
175             if (m_children == null)
176                 ParseChildren();
177         
178             return m_children;
179         }
180                 
181         public IMembershipCondition MembershipCondition
182         {
183             [System.Security.SecuritySafeCritical]  // auto-generated
184             get
185             {
186                 if (m_membershipCondition == null && m_element != null)
187                     ParseMembershipCondition();
188         
189                 return m_membershipCondition.Copy();
190             }
191             
192             set
193             {
194                 if (value == null)
195                     throw new ArgumentNullException( "MembershipCondition" );
196                 Contract.EndContractBlock();
197
198                 m_membershipCondition = value.Copy();
199             }
200         }
201         
202         public PolicyStatement PolicyStatement
203         {
204             get
205             {
206                 if (m_policy == null && m_element != null)
207                     ParsePolicy();
208
209                 if (m_policy != null)
210                     return m_policy.Copy();
211                 else
212                     return null;
213             }
214             
215             set
216             {
217                 if (value != null)
218                     m_policy = value.Copy();
219                 else
220                     m_policy = null;
221             }
222         }
223         
224         public String Name
225         {
226             get
227             {
228                 return m_name;
229             }
230
231             set
232             {
233                 m_name = value;
234             }
235         }
236
237         public String Description
238         {
239             get
240             {
241                 return m_description;
242             }
243
244             set
245             {
246                 m_description = value;
247             }
248         }
249         
250         public abstract PolicyStatement Resolve( Evidence evidence );
251
252         public abstract CodeGroup ResolveMatchingCodeGroups( Evidence evidence );
253         
254         public abstract CodeGroup Copy();
255         
256         public virtual String PermissionSetName
257         {
258             get
259             {
260                 if (m_policy == null && m_element != null)
261                     ParsePolicy();
262
263                 if (m_policy == null)
264                     return null;
265                     
266                 NamedPermissionSet permSet = m_policy.GetPermissionSetNoCopy() as NamedPermissionSet;
267             
268                 if (permSet != null)
269                 {
270                     return permSet.Name;
271                 }
272                 else
273                 {
274                     return null;
275                 }
276             }
277         }
278         
279         public virtual String AttributeString
280         {
281             get
282             {
283                 if (m_policy == null && m_element != null)
284                     ParsePolicy();
285                     
286                 if (m_policy != null)
287                     return m_policy.AttributeString;
288                 else
289                     return null;
290             }
291         }  
292         
293         public abstract String MergeLogic
294         {
295             get;
296         }
297               
298
299         public SecurityElement ToXml()
300         {
301             return ToXml( null );
302         }
303
304         public void FromXml( SecurityElement e )
305         {
306             FromXml( e, null );
307         }
308
309         [System.Security.SecuritySafeCritical]  // auto-generated
310         public SecurityElement ToXml( PolicyLevel level )
311         {
312             return ToXml( level, GetTypeName() );
313         }
314
315         internal virtual String GetTypeName()
316         {
317             return this.GetType().FullName;
318         }
319     
320         [System.Security.SecurityCritical]  // auto-generated
321         internal SecurityElement ToXml( PolicyLevel level, String policyClassName )
322         {
323             if (m_membershipCondition == null && m_element != null)
324                 ParseMembershipCondition();
325                 
326             if (m_children == null)
327                 ParseChildren();
328                 
329             if (m_policy == null && m_element != null)
330                 ParsePolicy();
331         
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);
337         
338             e.AddAttribute( "version", "1" );
339
340             e.AddChild( m_membershipCondition.ToXml( level ) );
341
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.
345             
346             if (m_policy != null)
347             {
348                 PermissionSet permSet = m_policy.GetPermissionSetNoCopy();
349                 NamedPermissionSet namedPermSet = permSet as NamedPermissionSet;
350
351                 if (namedPermSet != null && level != null && level.GetNamedPermissionSetInternal( namedPermSet.Name ) != null)
352                 {
353                     e.AddAttribute( "PermissionSetName", namedPermSet.Name );
354                 }
355                 else
356                 {
357                     if (!permSet.IsEmpty())
358                         e.AddChild( permSet.ToXml() );
359                 }
360
361                 if (m_policy.Attributes != PolicyStatementAttribute.Nothing)
362                     e.AddAttribute( "Attributes", XMLUtil.BitFieldEnumToString( typeof( PolicyStatementAttribute ), m_policy.Attributes ) );
363                 }
364             
365                 if (m_children.Count > 0)
366                 {
367                     lock (this)
368                     {
369                         IEnumerator enumerator = m_children.GetEnumerator();
370             
371                         while (enumerator.MoveNext())
372                         {
373                             e.AddChild( ((CodeGroup)enumerator.Current).ToXml( level ) );
374                         }
375                     }
376                 }
377
378             if (m_name != null)
379             {
380                 e.AddAttribute( "Name", SecurityElement.Escape( m_name ) );
381             }
382
383             if (m_description != null)
384             {
385                 e.AddAttribute( "Description", SecurityElement.Escape( m_description ) );
386             }
387
388             CreateXml( e, level );
389             
390             return e;
391         }
392
393         protected virtual void CreateXml( SecurityElement element, PolicyLevel level )
394         {
395         }
396         
397         public void FromXml( SecurityElement e, PolicyLevel level )
398         {
399             if (e == null)
400                 throw new ArgumentNullException("e");
401             Contract.EndContractBlock();
402
403             lock (this)
404             {
405                 m_element = e;
406                 m_parentLevel = level;
407                 m_children = null;
408                 m_membershipCondition = null;
409                 m_policy = null;
410
411                 m_name = e.Attribute( "Name" );
412                 m_description = e.Attribute( "Description" );
413
414                 ParseXml( e, level );
415             }
416         }
417         
418         protected virtual void ParseXml( SecurityElement e, PolicyLevel level )
419         {
420         } 
421             
422         [System.Security.SecurityCritical]  // auto-generated
423         private bool ParseMembershipCondition( bool safeLoad )
424         {
425             lock (this)
426             {
427                 IMembershipCondition membershipCondition = null;
428                 SecurityElement elMembershipCondition = m_element.SearchForChildByTag( "IMembershipCondition" );
429                 if (elMembershipCondition != null)
430                 {
431                     try
432                     {
433                         membershipCondition = System.Security.Util.XMLUtil.CreateMembershipCondition( elMembershipCondition );
434
435                         if (membershipCondition == null)
436                             return false;
437                     }
438                     catch (Exception ex)
439                     {
440                         throw new ArgumentException( Environment.GetResourceString( "Argument_MembershipConditionElement" ), ex );
441                     }
442 //                    catch
443 //                    {
444 //                        throw new ArgumentException( Environment.GetResourceString( "Argument_MembershipConditionElement" ) );
445 //                    }
446                     membershipCondition.FromXml( elMembershipCondition, m_parentLevel );
447                 }
448                 else
449                 {
450                     throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_InvalidXMLElement" ),  "IMembershipCondition", this.GetType().FullName ) );
451                 }
452                 
453                 m_membershipCondition = membershipCondition;
454                 return true;
455             }
456         }
457
458         [System.Security.SecurityCritical]  // auto-generated
459         private void ParseMembershipCondition()
460         {
461             ParseMembershipCondition( false );
462         }
463         
464         [System.Security.SecurityCritical]  // auto-generated
465         internal void ParseChildren()
466         {
467             lock (this)
468             {
469                 ArrayList childrenList = ArrayList.Synchronized( new ArrayList() );
470         
471                 if (m_element != null && m_element.InternalChildren != null)
472                 {    
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.
477
478                     m_element.Children = (ArrayList)m_element.InternalChildren.Clone();
479
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.
483
484                     ArrayList unparsedChildren = ArrayList.Synchronized( new ArrayList() );
485
486                     Evidence evidence = new Evidence();
487
488                     int childCount = m_element.InternalChildren.Count;
489                     int i = 0;
490                     while (i < childCount)
491                     {
492                         SecurityElement elGroup = (SecurityElement)m_element.Children[i];
493             
494                         if (elGroup.Tag.Equals( "CodeGroup" ))
495                         {
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.
501
502                             CodeGroup group = System.Security.Util.XMLUtil.CreateCodeGroup( elGroup );
503                 
504                             if (group != null)
505                             {
506                                 group.FromXml( elGroup, m_parentLevel );
507
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.
511
512                                 if (ParseMembershipCondition( true ))
513                                 {
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.
519                             
520                                     group.Resolve( evidence );
521                                     group.MembershipCondition.Check( evidence );
522
523                                     // Now it should be ok to add the group to the hierarchy.
524
525                                     childrenList.Add( group );
526
527                                     // Increment the count since we are done with this child
528
529                                     ++i;
530                                 }
531                                 else
532                                 {
533                                     // Assembly that holds the membership condition is not loaded, remove
534                                     // the child from the list.
535
536                                     m_element.InternalChildren.RemoveAt( i );
537
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
541
542                                     childCount = m_element.InternalChildren.Count;
543
544                                     // Add this child to the unparsed child list.
545
546                                     unparsedChildren.Add( new CodeGroupPositionMarker( i, childrenList.Count, elGroup ) );
547                                 }
548                             }
549                             else
550                             {
551                                 // Assembly that holds the code group is not loaded, remove
552                                 // the child from the list.
553
554                                 m_element.InternalChildren.RemoveAt( i );
555
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
559
560                                 childCount = m_element.InternalChildren.Count;
561
562                                 // Add this child to the unparsed child list.
563
564                                 unparsedChildren.Add( new CodeGroupPositionMarker( i, childrenList.Count, elGroup ) );
565                             }
566                         }
567                         else
568                         {
569                             // The current tag is not an <CodeGroup> tag, so we just skip it.
570
571                             ++i;
572                         }
573                     }
574
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.
582
583                     IEnumerator enumerator = unparsedChildren.GetEnumerator();
584
585                     while (enumerator.MoveNext())
586                     {
587                         CodeGroupPositionMarker marker = (CodeGroupPositionMarker)enumerator.Current;
588
589                         CodeGroup group = System.Security.Util.XMLUtil.CreateCodeGroup( marker.element );
590             
591                         if (group != null)
592                         {
593                             group.FromXml( marker.element, m_parentLevel );
594
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.
598
599                             group.Resolve( evidence );
600                             group.MembershipCondition.Check( evidence );
601
602                             // Now it should be ok to add the group to the hierarchy.
603
604                             childrenList.Insert( marker.groupIndex, group );
605
606                             // Add the element back into the child list in the proper spot.
607
608                             m_element.InternalChildren.Insert( marker.elementIndex, marker.element );
609                         }
610                         else
611                         {
612                             throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, Environment.GetResourceString( "Argument_FailedCodeGroup" ), marker.element.Attribute( "class" ) ) );
613                         }
614                     }
615
616                 }
617                 m_children = childrenList;
618             }
619
620         }
621         
622         private void ParsePolicy()
623         {
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.
632
633             while (true)
634             {
635                 PolicyStatement policy = new PolicyStatement();
636                 bool needToParse = false;
637
638                 SecurityElement elPolicy = new SecurityElement( "PolicyStatement" );
639                 elPolicy.AddAttribute( "version", "1" );
640
641                 SecurityElement localRef = m_element;
642
643                 lock (this)
644                 {
645
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.
649
650                     if (m_element != null)
651                     {
652                         String permSetName = m_element.Attribute( "PermissionSetName" );
653
654                         if (permSetName != null)
655                         {
656                             elPolicy.AddAttribute( "PermissionSetName", permSetName );
657                             needToParse = true;
658                         }
659                         else
660                         {
661                             SecurityElement elPermSet = m_element.SearchForChildByTag( "PermissionSet" );
662
663                             if (elPermSet != null)
664                             {
665                                 elPolicy.AddChild( elPermSet );
666                                 needToParse = true;
667                             }
668                             else
669                             {
670                                 elPolicy.AddChild( new PermissionSet( false ).ToXml() );
671                                 needToParse = true;
672                             }
673                         }
674
675                         String attributes = m_element.Attribute( "Attributes" );
676
677                         if (attributes != null)
678                         {
679                             elPolicy.AddAttribute( "Attributes", attributes );
680                             needToParse = true;
681                         }
682                     }
683                 }
684
685                 if (needToParse)
686                     policy.FromXml( elPolicy, m_parentLevel );
687                 else
688                     policy.PermissionSet = null;
689
690                 lock (this)
691                 {
692                     if (localRef == m_element && m_policy == null)
693                     {
694                         m_policy = policy;
695                         break;
696                     }
697                     else if (m_policy != null)
698                     {
699                         break;
700                     }
701                 }
702             }
703
704             if (m_policy != null && m_children != null && m_membershipCondition != null)
705             {
706                 //m_element = null;
707                 //m_parentLevel = null;
708             }
709
710         }
711
712         [System.Security.SecuritySafeCritical]  // auto-generated
713         public override bool Equals( Object o)
714         {
715             CodeGroup that = (o as CodeGroup);
716
717             if (that != null && this.GetType().Equals( that.GetType() ))
718             {
719                 if (Equals( this.m_name, that.m_name ) &&
720                     Equals( this.m_description, that.m_description ))
721                 {
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();
726
727                     if (Equals( this.m_membershipCondition, that.m_membershipCondition ))
728                     {
729                         return true;
730                     }
731                 }
732             }
733             return false;
734         }
735         
736         [System.Security.SecuritySafeCritical]  // auto-generated
737         public bool Equals( CodeGroup cg, bool compareChildren)
738         {
739             if (!this.Equals( cg )) return false;
740             
741             if (compareChildren)
742             {
743                 if (this.m_children == null)
744                     this.ParseChildren();
745                 if (cg.m_children == null)
746                     cg.ParseChildren();
747
748                 ArrayList list1 = new ArrayList(this.m_children);
749                 ArrayList list2 = new ArrayList(cg.m_children);
750                 
751                 if (list1.Count != list2.Count) return false;
752                 
753                 for (int i = 0; i < list1.Count; i++)
754                 {
755                     if (!((CodeGroup) list1[i]).Equals( (CodeGroup) list2[i], true ))
756                     {
757                         return false;
758                     }
759                 }
760             }
761             
762             return true;
763         }
764         
765         [System.Security.SecuritySafeCritical]  // auto-generated
766         public override int GetHashCode()
767         {
768             if (m_membershipCondition == null && m_element != null)
769                 ParseMembershipCondition();
770
771             if (m_name != null || m_membershipCondition != null)
772             {
773                 return (m_name == null ? 0 : m_name.GetHashCode())
774                      + (m_membershipCondition == null ? 0 : m_membershipCondition.GetHashCode());
775             }
776             else
777             {
778                 return GetType().GetHashCode();
779             }
780         }
781     }
782
783     internal class CodeGroupPositionMarker
784     {
785         internal int elementIndex;
786         internal int groupIndex;
787         internal SecurityElement element;
788
789         internal CodeGroupPositionMarker( int elementIndex, int groupIndex, SecurityElement element )
790         {
791             this.elementIndex = elementIndex;
792             this.groupIndex = groupIndex;
793             this.element = element;
794         }
795     }
796 }