Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / permissiontoken.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 namespace System.Security {
8     using System;
9     using System.Security.Util;
10     using System.Security.Permissions;
11     using System.Reflection;
12     using System.Collections;
13     using System.Threading;
14     using System.Globalization;
15     using System.Runtime.CompilerServices;
16     using System.Diagnostics.Contracts;
17
18     [Flags]
19     internal enum PermissionTokenType
20     {
21         Normal = 0x1,
22         IUnrestricted = 0x2,
23         DontKnow = 0x4,
24         BuiltIn = 0x8
25     }
26
27     [Serializable]
28     internal sealed class PermissionTokenKeyComparer : IEqualityComparer
29     {
30         private Comparer _caseSensitiveComparer;
31         private TextInfo _info;
32
33         public PermissionTokenKeyComparer()
34         {
35             _caseSensitiveComparer = new Comparer(CultureInfo.InvariantCulture);
36             _info = CultureInfo.InvariantCulture.TextInfo;
37         }
38
39         [System.Security.SecuritySafeCritical]  // auto-generated
40         public int Compare(Object a, Object b)
41         {
42             String strA = a as String;
43             String strB = b as String;
44
45             // if it's not a string then we just call the object comparer
46             if (strA == null || strB == null)
47                 return _caseSensitiveComparer.Compare(a, b);
48
49             int i = _caseSensitiveComparer.Compare(a,b);
50             if (i == 0)
51                 return 0;
52
53             if (SecurityManager.IsSameType(strA, strB))
54                 return 0;
55             
56             return i;
57         }
58
59         public new bool Equals( Object a, Object b )
60         {
61             if (a == b) return true;
62             if (a == null || b == null) return false;
63             return Compare( a, b ) == 0;
64         }
65
66         // The data structure consuming this will be responsible for dealing with null objects as keys.
67         public int GetHashCode(Object obj)
68         {            
69             if (obj == null) throw new ArgumentNullException("obj");
70             Contract.EndContractBlock();
71             
72             String str = obj as String;
73
74             if (str == null)
75                 return obj.GetHashCode();
76
77             int iComma = str.IndexOf( ',' );
78             if (iComma == -1)
79                 iComma = str.Length;
80
81             int accumulator = 0;
82             for (int i = 0; i < iComma; ++i)
83             {
84                 accumulator = (accumulator << 7) ^ str[i] ^ (accumulator >> 25);
85             }
86
87             return accumulator;
88         }
89     }
90
91     [Serializable]
92     internal sealed class PermissionToken : ISecurityEncodable
93     {
94         private static readonly PermissionTokenFactory s_theTokenFactory;
95 #if FEATURE_CAS_POLICY
96         private static volatile ReflectionPermission s_reflectPerm = null;
97 #endif // FEATURE_CAS_POLICY
98         private const string c_mscorlibName = "mscorlib";
99
100         internal int    m_index;
101         internal volatile PermissionTokenType m_type;
102 #if FEATURE_CAS_POLICY
103         internal String m_strTypeName;
104 #endif // FEATURE_CAS_POLICY
105         static internal TokenBasedSet s_tokenSet = new TokenBasedSet();
106
107         internal static bool IsMscorlibClassName (string className) {
108             Contract.Assert( c_mscorlibName == ((RuntimeAssembly)Assembly.GetExecutingAssembly()).GetSimpleName(),
109                 "mscorlib name mismatch" );
110
111             // If the class name does not look like a fully qualified name, we cannot simply determine if it's 
112             // an mscorlib.dll type so we should return true so the type can be matched with the
113             // right index in the TokenBasedSet.
114             int index = className.IndexOf(',');
115             if (index == -1)
116                 return true;
117
118             index = className.LastIndexOf(']');
119             if (index == -1)
120                 index = 0;
121
122             // Search for the string 'mscorlib' in the classname. If we find it, we will conservatively assume it's an mscorlib.dll type and load it.
123             for (int i = index; i < className.Length; i++) {
124                 if (className[i] == 'm' || className[i] == 'M') {
125                     if (String.Compare(className, i, c_mscorlibName, 0, c_mscorlibName.Length, StringComparison.OrdinalIgnoreCase) == 0)
126                         return true;
127                 }
128             }
129             return false;
130         }
131
132         static PermissionToken()
133         {
134             s_theTokenFactory = new PermissionTokenFactory( 4 );
135         }
136
137         internal PermissionToken()
138         {
139         }
140
141         internal PermissionToken(int index, PermissionTokenType type, String strTypeName)
142         {
143             m_index = index;
144             m_type = type;
145 #if FEATURE_CAS_POLICY
146             m_strTypeName = strTypeName;
147 #endif // FEATURE_CAS_POLICY
148         }
149
150         [System.Security.SecurityCritical]  // auto-generated
151         public static PermissionToken GetToken(Type cls)
152         {
153             if (cls == null)
154                 return null;
155             
156 #if FEATURE_CAS_POLICY
157             if (cls.GetInterface( "System.Security.Permissions.IBuiltInPermission" ) != null)
158             {
159                 if (s_reflectPerm == null)
160                     s_reflectPerm = new ReflectionPermission(PermissionState.Unrestricted);
161                 s_reflectPerm.Assert();
162                 MethodInfo method = cls.GetMethod( "GetTokenIndex", BindingFlags.Static | BindingFlags.NonPublic );
163                 Contract.Assert( method != null, "IBuiltInPermission types should have a static method called 'GetTokenIndex'" );
164
165                 // GetTokenIndex needs to be invoked without any security checks, since doing a security check
166                 // will involve a ReflectionTargetDemand which creates a CompressedStack and attempts to get the
167                 // token.
168                 RuntimeMethodInfo getTokenIndex = method as RuntimeMethodInfo;
169                 Contract.Assert(getTokenIndex != null, "method is not a RuntimeMethodInfo");
170                 int token = (int)getTokenIndex.UnsafeInvoke(null, BindingFlags.Default, null, null, null);
171                 return s_theTokenFactory.BuiltInGetToken(token, null, cls);
172             }
173             else
174 #endif // FEATURE_CAS_POLICY
175             {
176                 return s_theTokenFactory.GetToken(cls, null);
177             }
178         }
179
180         public static PermissionToken GetToken(IPermission perm)
181         {
182             if (perm == null)
183                 return null;
184
185             IBuiltInPermission ibPerm = perm as IBuiltInPermission;
186
187             if (ibPerm != null)
188                 return s_theTokenFactory.BuiltInGetToken( ibPerm.GetTokenIndex(), perm, null );
189             else
190                 return s_theTokenFactory.GetToken(perm.GetType(), perm);
191         }
192
193 #if FEATURE_CAS_POLICY
194         public static PermissionToken GetToken(String typeStr)
195         {
196             return GetToken( typeStr, false );
197         }
198
199 #if _DEBUG
200         [System.Security.SecuritySafeCritical]  // auto-generated
201         [MethodImplAttribute(MethodImplOptions.NoInlining)] // Methods containing StackCrawlMark local var has to be marked non-inlineable
202         private static void GetTokenHelper(String typeStr)
203         {
204             new PermissionSet(PermissionState.Unrestricted).Assert();
205             StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
206             Type type = RuntimeTypeHandle.GetTypeByName( typeStr.Trim().Replace( '\'', '\"' ), ref stackMark);
207             Contract.Assert( (type == null) || (type.Module.Assembly != System.Reflection.Assembly.GetExecutingAssembly()) || (typeStr.IndexOf("mscorlib", StringComparison.Ordinal) < 0),
208                 "We should not go through this path for mscorlib based permissions" );
209         }
210 #endif
211
212         public static PermissionToken GetToken(String typeStr, bool bCreateMscorlib)
213         {
214             if (typeStr == null)
215                 return null;
216
217             if (IsMscorlibClassName( typeStr ))
218             {
219                 if (!bCreateMscorlib)
220                 {
221                     return null;
222                 }
223                 else
224                 {
225                     return FindToken( Type.GetType( typeStr ) );
226                 }
227             }
228             else
229             {
230                 PermissionToken token = s_theTokenFactory.GetToken(typeStr);
231 #if _DEBUG
232                 GetTokenHelper(typeStr);
233 #endif
234                 return token;
235             }
236         }
237
238         [SecuritySafeCritical]
239         public static PermissionToken FindToken( Type cls )
240         {
241             if (cls == null)
242                 return null;
243              
244 #if FEATURE_CAS_POLICY
245             if (cls.GetInterface( "System.Security.Permissions.IBuiltInPermission" ) != null)
246             {
247                 if (s_reflectPerm == null)
248                     s_reflectPerm = new ReflectionPermission(PermissionState.Unrestricted);
249                 s_reflectPerm.Assert();
250                 MethodInfo method = cls.GetMethod( "GetTokenIndex", BindingFlags.Static | BindingFlags.NonPublic );
251                 Contract.Assert( method != null, "IBuiltInPermission types should have a static method called 'GetTokenIndex'" );
252
253                 // GetTokenIndex needs to be invoked without any security checks, since doing a security check
254                 // will involve a ReflectionTargetDemand which creates a CompressedStack and attempts to get the
255                 // token.
256                 RuntimeMethodInfo getTokenIndex = method as RuntimeMethodInfo;
257                 Contract.Assert(getTokenIndex != null, "method is not a RuntimeMethodInfo");
258                 int token = (int)getTokenIndex.UnsafeInvoke(null, BindingFlags.Default, null, null, null);
259                 return s_theTokenFactory.BuiltInGetToken(token, null, cls);
260             }
261             else
262 #endif // FEATURE_CAS_POLICY
263             {
264                 return s_theTokenFactory.FindToken( cls );
265             }
266         }
267 #endif // FEATURE_CAS_POLICY
268
269         public static PermissionToken FindTokenByIndex( int i )
270         {
271             return s_theTokenFactory.FindTokenByIndex( i );
272         }
273
274         public static bool IsTokenProperlyAssigned( IPermission perm, PermissionToken token )
275         {
276             PermissionToken heldToken = GetToken( perm );
277             if (heldToken.m_index != token.m_index)
278                 return false;
279
280             if (token.m_type != heldToken.m_type)
281                 return false;
282
283             if (perm.GetType().Module.Assembly == Assembly.GetExecutingAssembly() &&
284                 heldToken.m_index >= BuiltInPermissionIndex.NUM_BUILTIN_NORMAL + BuiltInPermissionIndex.NUM_BUILTIN_UNRESTRICTED)
285                 return false;
286
287             return true;
288         }
289
290 #if FEATURE_CAS_POLICY
291         public SecurityElement ToXml()
292         {
293             Contract.Assert( (m_type & PermissionTokenType.DontKnow) == 0, "Should have valid token type when ToXml is called" );
294             SecurityElement elRoot = new SecurityElement( "PermissionToken" );
295             if ((m_type & PermissionTokenType.BuiltIn) != 0)
296                 elRoot.AddAttribute( "Index", "" + this.m_index );
297             else
298                 elRoot.AddAttribute( "Name", SecurityElement.Escape( m_strTypeName ) );
299             elRoot.AddAttribute("Type", m_type.ToString("F"));
300             return elRoot;
301         }
302
303         public void FromXml(SecurityElement elRoot)
304         {
305             // For the most part there is no parameter checking here since this is an
306             // internal class and the serialization/deserialization path is controlled.
307
308             if (!elRoot.Tag.Equals( "PermissionToken" ))
309                 Contract.Assert( false, "Tried to deserialize non-PermissionToken element here" );
310
311             String strName = elRoot.Attribute( "Name" );
312             PermissionToken realToken;
313             if (strName != null)
314                 realToken = GetToken( strName, true );
315             else
316                 realToken = FindTokenByIndex( Int32.Parse( elRoot.Attribute( "Index" ), CultureInfo.InvariantCulture ) );
317             
318             this.m_index = realToken.m_index;
319             this.m_type = (PermissionTokenType) Enum.Parse(typeof(PermissionTokenType), elRoot.Attribute("Type"));
320             Contract.Assert((this.m_type & PermissionTokenType.DontKnow) == 0, "Should have valid token type when FromXml is called.");
321             this.m_strTypeName = realToken.m_strTypeName;
322         }
323 #endif // FEATURE_CAS_POLICY
324     }
325
326     // Package access only
327     internal class PermissionTokenFactory
328     {
329         private volatile int       m_size;
330         private volatile int       m_index;
331         private volatile Hashtable m_tokenTable;    // Cache of tokens by class string name
332         private volatile Hashtable m_handleTable;   // Cache of tokens by type handle (IntPtr)
333         private volatile Hashtable m_indexTable;    // Cache of tokens by index
334
335
336         // We keep an array of tokens for our built-in permissions.
337         // This is ordered in terms of unrestricted perms first, normals
338         // second.  Of course, all the ordering is based on the individual
339         // permissions sticking to the deal, so we do some simple boundary
340         // checking but mainly leave it to faith.
341
342         private volatile PermissionToken[] m_builtIn;
343
344         private const String s_unrestrictedPermissionInferfaceName = "System.Security.Permissions.IUnrestrictedPermission";
345
346         internal PermissionTokenFactory( int size )
347         {
348             m_builtIn = new PermissionToken[BuiltInPermissionIndex.NUM_BUILTIN_NORMAL + BuiltInPermissionIndex.NUM_BUILTIN_UNRESTRICTED];
349
350             m_size = size;
351             m_index = BuiltInPermissionIndex.NUM_BUILTIN_NORMAL + BuiltInPermissionIndex.NUM_BUILTIN_UNRESTRICTED;
352             m_tokenTable = null;
353             m_handleTable = new Hashtable(size);
354             m_indexTable = new Hashtable(size);
355         }
356
357 #if FEATURE_CAS_POLICY
358         [SecuritySafeCritical]
359         internal PermissionToken FindToken( Type cls )
360         {
361             IntPtr typePtr = cls.TypeHandle.Value;
362             PermissionToken tok = (PermissionToken)m_handleTable[typePtr];
363
364             if (tok != null)
365                 return tok;
366
367             if (m_tokenTable == null)
368                 return null;
369
370             tok = (PermissionToken)m_tokenTable[cls.AssemblyQualifiedName];
371
372             if (tok != null)
373             {
374                 lock (this)
375                 {
376                     m_handleTable.Add(typePtr, tok);
377                 }
378             }
379
380             return tok;
381         }
382 #endif // FEATURE_CAS_POLICY
383
384         internal PermissionToken FindTokenByIndex( int i )
385         {
386             PermissionToken token;
387
388             if (i < BuiltInPermissionIndex.NUM_BUILTIN_NORMAL + BuiltInPermissionIndex.NUM_BUILTIN_UNRESTRICTED)
389             {
390                 token = BuiltInGetToken( i, null, null );
391             }
392             else
393             {
394                 token = (PermissionToken)m_indexTable[i];
395             }
396
397             return token;
398         }
399
400         [SecuritySafeCritical]
401         internal PermissionToken GetToken(Type cls, IPermission perm)
402         {
403             Contract.Assert( cls != null, "Must pass in valid type" );
404
405             IntPtr typePtr = cls.TypeHandle.Value;
406             object tok = m_handleTable[typePtr];
407             if (tok == null)
408             {
409                 String typeStr = cls.AssemblyQualifiedName;
410                 tok = m_tokenTable != null ? m_tokenTable[typeStr] : null; // Assumes asynchronous lookups are safe
411
412                 if (tok == null)
413                 {
414                     lock (this)
415                     {
416                         if (m_tokenTable != null)
417                         {
418                             tok = m_tokenTable[typeStr]; // Make sure it wasn't just added
419                         }
420                         else
421                             m_tokenTable = new Hashtable(m_size, 1.0f, new PermissionTokenKeyComparer());
422
423                         if (tok == null)
424                         {
425                             if (perm != null)
426                             {
427                                 tok = new PermissionToken( m_index++, PermissionTokenType.IUnrestricted, typeStr );
428                             }
429                             else
430                             {
431                                 if (cls.GetInterface(s_unrestrictedPermissionInferfaceName) != null)
432                                     tok = new PermissionToken( m_index++, PermissionTokenType.IUnrestricted, typeStr );
433                                 else
434                                     tok = new PermissionToken( m_index++, PermissionTokenType.Normal, typeStr );
435                             }
436                             m_tokenTable.Add(typeStr, tok);
437                             m_indexTable.Add(m_index - 1, tok);
438                             PermissionToken.s_tokenSet.SetItem( ((PermissionToken)tok).m_index, tok );
439                         }
440
441                         if (!m_handleTable.Contains(typePtr))
442                             m_handleTable.Add( typePtr, tok );
443                     }
444                 }
445                 else
446                 {
447                     lock (this)
448                     {
449                         if (!m_handleTable.Contains(typePtr))
450                             m_handleTable.Add( typePtr, tok );
451                     }
452                 }
453             }
454
455             if ((((PermissionToken)tok).m_type & PermissionTokenType.DontKnow) != 0)
456             {
457                 if (perm != null)
458                 {
459                     Contract.Assert( !(perm is IBuiltInPermission), "This should not be called for built-ins" );
460                     ((PermissionToken)tok).m_type = PermissionTokenType.IUnrestricted;
461 #if FEATURE_CAS_POLICY
462                     ((PermissionToken)tok).m_strTypeName = perm.GetType().AssemblyQualifiedName;
463 #endif // FEATURE_CAS_POLICY
464                 }
465                 else
466                 {
467                     Contract.Assert( cls.GetInterface( "System.Security.Permissions.IBuiltInPermission" ) == null, "This shoudl not be called for built-ins" );
468                     if (cls.GetInterface(s_unrestrictedPermissionInferfaceName) != null)
469                         ((PermissionToken)tok).m_type = PermissionTokenType.IUnrestricted;
470                     else
471                         ((PermissionToken)tok).m_type = PermissionTokenType.Normal;
472 #if FEATURE_CAS_POLICY
473                     ((PermissionToken)tok).m_strTypeName = cls.AssemblyQualifiedName;
474 #endif // FEATURE_CAS_POLICY
475                 }
476             }
477
478             return (PermissionToken)tok;
479         }
480
481         internal PermissionToken GetToken(String typeStr)
482         {
483             Object tok = null;
484             tok = m_tokenTable != null ? m_tokenTable[typeStr] : null; // Assumes asynchronous lookups are safe
485             if (tok == null)
486             {
487                 lock (this)
488                 {
489                     if (m_tokenTable != null)
490                     {
491                         tok = m_tokenTable[typeStr]; // Make sure it wasn't just added
492                     }
493                     else
494                         m_tokenTable = new Hashtable(m_size, 1.0f, new PermissionTokenKeyComparer());
495                         
496                     if (tok == null)
497                     {
498                         tok = new PermissionToken( m_index++, PermissionTokenType.DontKnow, typeStr );
499                         m_tokenTable.Add(typeStr, tok);
500                         m_indexTable.Add(m_index - 1, tok);
501                         PermissionToken.s_tokenSet.SetItem(((PermissionToken)tok).m_index, tok);
502                     }
503                 }
504             }
505
506             return (PermissionToken)tok;
507         }
508
509         internal PermissionToken BuiltInGetToken( int index, IPermission perm, Type cls )
510         {
511             PermissionToken token = Volatile.Read(ref m_builtIn[index]);
512
513             if (token == null)
514             {
515                 lock (this)
516                 {
517                     token = m_builtIn[index];
518
519                     if (token == null)
520                     {
521                         PermissionTokenType permType = PermissionTokenType.DontKnow;
522
523                         if (perm != null)
524                         {
525                             permType = PermissionTokenType.IUnrestricted;
526                         }
527                         else if (cls != null)
528                         {
529                             permType = PermissionTokenType.IUnrestricted;
530                         }
531
532                         token = new PermissionToken( index, permType | PermissionTokenType.BuiltIn, null );
533                         Volatile.Write(ref m_builtIn[index], token);
534                         PermissionToken.s_tokenSet.SetItem( token.m_index, token );
535                     }
536                 }
537             }
538
539             if ((token.m_type & PermissionTokenType.DontKnow) != 0)
540             {
541                     token.m_type = PermissionTokenType.BuiltIn;
542
543                     if (perm != null)
544                     {
545                         token.m_type |= PermissionTokenType.IUnrestricted;
546                     }
547                     else if (cls != null)
548                     {
549                         token.m_type |= PermissionTokenType.IUnrestricted;
550                     }
551                     else
552                     {
553                         token.m_type |= PermissionTokenType.DontKnow;
554                     }
555             }
556
557             return token;
558         }
559     }
560 }