1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.IdentityModel
6 {
7     using System.Collections.Generic;
8     using System.Collections.ObjectModel;
9     using System.Diagnostics;
10     using System.IdentityModel.Claims;
11     using System.IdentityModel.Diagnostics;
12     using System.IdentityModel.Policy;
13     using System.IdentityModel.Tokens;
14     using System.Runtime;
15     using System.Security;
16     using System.Security.Authentication.ExtendedProtection;
17     using System.Security.Cryptography.X509Certificates;
18     using System.Security.Permissions;
19     using System.Security.Principal;
20     using System.Text;
21     using System.Xml;
22     using Microsoft.Win32;
23 
24     static class SecurityUtils
25     {
26         public const string Identities = "Identities";
27         static int fipsAlgorithmPolicy = -1;
28         public const int WindowsVistaMajorNumber = 6;
29         static IIdentity anonymousIdentity;
30 
31         // these should be kept in sync with IIS70
32         public const string AuthTypeNTLM = "NTLM";
33         public const string AuthTypeNegotiate = "Negotiate";
34         public const string AuthTypeKerberos = "Kerberos";
35         public const string AuthTypeAnonymous = "";
36         public const string AuthTypeCertMap = "SSL/PCT"; // mapped from a cert
37         public const string AuthTypeBasic = "Basic"; //LogonUser
38 
39         internal static IIdentity AnonymousIdentity
40         {
41             get
42             {
43                 if (anonymousIdentity == null)
44                     anonymousIdentity = SecurityUtils.CreateIdentity(String.Empty);
45                 return anonymousIdentity;
46             }
47         }
48 
49         public static DateTime MaxUtcDateTime
50         {
51             get
52             {
53                 // + and -  TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow.
54                 return new DateTime(DateTime.MaxValue.Ticks - TimeSpan.TicksPerDay, DateTimeKind.Utc);
55             }
56         }
57 
58         public static DateTime MinUtcDateTime
59         {
60             get
61             {
62                 // + and -  TimeSpan.TicksPerDay is to compensate the DateTime.ParseExact (to localtime) overflow.
63                 return new DateTime(DateTime.MinValue.Ticks + TimeSpan.TicksPerDay, DateTimeKind.Utc);
64             }
65         }
66 
CreateIdentity(string name, string authenticationType)67         internal static IIdentity CreateIdentity(string name, string authenticationType)
68         {
69             return new GenericIdentity(name, authenticationType);
70         }
71 
CreateIdentity(string name)72         internal static IIdentity CreateIdentity(string name)
73         {
74             return new GenericIdentity(name);
75         }
76 
CloneBuffer(byte[] buffer)77         internal static byte[] CloneBuffer(byte[] buffer)
78         {
79             return CloneBuffer(buffer, 0, buffer.Length);
80         }
81 
CloneBuffer(byte[] buffer, int offset, int len)82         internal static byte[] CloneBuffer(byte[] buffer, int offset, int len)
83         {
84             DiagnosticUtility.DebugAssert(offset >= 0, "Negative offset passed to CloneBuffer.");
85             DiagnosticUtility.DebugAssert(len >= 0, "Negative len passed to CloneBuffer.");
86             DiagnosticUtility.DebugAssert(buffer.Length - offset >= len, "Invalid parameters to CloneBuffer.");
87 
88             byte[] copy = DiagnosticUtility.Utility.AllocateByteArray(len);
89             Buffer.BlockCopy(buffer, offset, copy, 0, len);
90             return copy;
91         }
92 
CreateSymmetricSecurityKeys( byte[] key )93         internal static ReadOnlyCollection<SecurityKey> CreateSymmetricSecurityKeys( byte[] key )
94         {
95             List<SecurityKey> temp = new List<SecurityKey>( 1 );
96             temp.Add( new InMemorySymmetricSecurityKey( key ) );
97             return temp.AsReadOnly();
98         }
99 
EncryptKey(SecurityToken wrappingToken, string encryptionMethod, byte[] keyToWrap)100         internal static byte[] EncryptKey(SecurityToken wrappingToken, string encryptionMethod, byte[] keyToWrap)
101         {
102             SecurityKey wrappingSecurityKey = null;
103             if (wrappingToken.SecurityKeys != null)
104             {
105                 for (int i = 0; i < wrappingToken.SecurityKeys.Count; ++i)
106                 {
107                     if (wrappingToken.SecurityKeys[i].IsSupportedAlgorithm(encryptionMethod))
108                     {
109                         wrappingSecurityKey = wrappingToken.SecurityKeys[i];
110                         break;
111                     }
112                 }
113             }
114             if (wrappingSecurityKey == null)
115             {
116                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.CannotFindMatchingCrypto, encryptionMethod));
117             }
118             return wrappingSecurityKey.EncryptKey(encryptionMethod, keyToWrap);
119         }
120 
MatchesBuffer(byte[] src, byte[] dst)121         internal static bool MatchesBuffer(byte[] src, byte[] dst)
122         {
123             return MatchesBuffer(src, 0, dst, 0);
124         }
125 
MatchesBuffer(byte[] src, int srcOffset, byte[] dst, int dstOffset)126         internal static bool MatchesBuffer(byte[] src, int srcOffset, byte[] dst, int dstOffset)
127         {
128             DiagnosticUtility.DebugAssert(dstOffset >= 0, "Negative dstOffset passed to MatchesBuffer.");
129             DiagnosticUtility.DebugAssert(srcOffset >= 0, "Negative srcOffset passed to MatchesBuffer.");
130 
131             // defensive programming
132             if ((dstOffset < 0) || (srcOffset < 0))
133                 return false;
134 
135             if (src == null || srcOffset >= src.Length)
136                 return false;
137             if (dst == null || dstOffset >= dst.Length)
138                 return false;
139             if ((src.Length - srcOffset) != (dst.Length - dstOffset))
140                 return false;
141 
142             for (int i = srcOffset, j = dstOffset; i < src.Length; i++, j++)
143             {
144                 if (src[i] != dst[j])
145                     return false;
146             }
147             return true;
148         }
149 
GetCertificateId(X509Certificate2 certificate)150         internal static string GetCertificateId(X509Certificate2 certificate)
151         {
152             string certificateId = certificate.SubjectName.Name;
153             if (string.IsNullOrEmpty(certificateId))
154                 certificateId = certificate.Thumbprint;
155             return certificateId;
156         }
157 
158         [Fx.Tag.SecurityNote(Critical = "Calls critical method X509Certificate2.Reset.",
159             Safe = "Per review from CLR security team, this method does nothing unsafe.")]
160         [SecuritySafeCritical]
ResetCertificate(X509Certificate2 certificate)161         internal static void ResetCertificate(X509Certificate2 certificate)
162         {
163             certificate.Reset();
164         }
165 
IsCurrentlyTimeEffective(DateTime effectiveTime, DateTime expirationTime, TimeSpan maxClockSkew)166         internal static bool IsCurrentlyTimeEffective(DateTime effectiveTime, DateTime expirationTime, TimeSpan maxClockSkew)
167         {
168             DateTime curEffectiveTime = (effectiveTime < DateTime.MinValue.Add(maxClockSkew)) ? effectiveTime : effectiveTime.Subtract(maxClockSkew);
169             DateTime curExpirationTime = (expirationTime > DateTime.MaxValue.Subtract(maxClockSkew)) ? expirationTime : expirationTime.Add(maxClockSkew);
170             DateTime curTime = DateTime.UtcNow;
171 
172             return (curEffectiveTime.ToUniversalTime() <= curTime) && (curTime < curExpirationTime.ToUniversalTime());
173         }
174 
175         // Federal Information Processing Standards Publications
176         // at http://www.itl.nist.gov/fipspubs/geninfo.htm
177         internal static bool RequiresFipsCompliance
178         {
179             [Fx.Tag.SecurityNote(Critical = "Calls an UnsafeNativeMethod and a Critical method (GetFipsAlgorithmPolicyKeyFromRegistry.",
180                 Safe = "processes the return and just returns a bool, which is safe.")]
181             [SecuritySafeCritical]
182             get
183             {
184                 if (fipsAlgorithmPolicy == -1)
185                 {
186                     if (Environment.OSVersion.Version.Major >= WindowsVistaMajorNumber)
187                     {
188                         bool fipsEnabled;
189 #pragma warning suppress 56523 // we check for the return code of the method instead of calling GetLastWin32Error
190                         bool readPolicy = (CAPI.S_OK == CAPI.BCryptGetFipsAlgorithmMode(out fipsEnabled));
191 
192                         if (readPolicy && fipsEnabled)
193                             fipsAlgorithmPolicy = 1;
194                         else
195                             fipsAlgorithmPolicy = 0;
196                     }
197                     else
198                     {
199                         fipsAlgorithmPolicy = GetFipsAlgorithmPolicyKeyFromRegistry();
200                         if (fipsAlgorithmPolicy != 1)
201                             fipsAlgorithmPolicy = 0;
202                     }
203                 }
204                 return fipsAlgorithmPolicy == 1;
205             }
206         }
207 
208         const string fipsPolicyRegistryKey = @"System\CurrentControlSet\Control\Lsa";
209 
210         /// <SecurityNote>
211         /// Critical - Asserts to get a value from the registry
212         /// </SecurityNote>
213         [SecurityCritical]
214         [RegistryPermission(SecurityAction.Assert, Read = @"HKEY_LOCAL_MACHINE\" + fipsPolicyRegistryKey)]
GetFipsAlgorithmPolicyKeyFromRegistry()215         static int GetFipsAlgorithmPolicyKeyFromRegistry()
216         {
217             int fipsAlgorithmPolicy = -1;
218             using (RegistryKey fipsAlgorithmPolicyKey = Registry.LocalMachine.OpenSubKey(fipsPolicyRegistryKey, false))
219             {
220                 if (fipsAlgorithmPolicyKey != null)
221                 {
222                     object data = fipsAlgorithmPolicyKey.GetValue("FIPSAlgorithmPolicy");
223                     if (data != null)
224                         fipsAlgorithmPolicy = (int)data;
225                 }
226             }
227             return fipsAlgorithmPolicy;
228         }
229 
230         class SimpleAuthorizationContext : AuthorizationContext
231         {
232             SecurityUniqueId id;
233             UnconditionalPolicy policy;
234             IDictionary<string, object> properties;
235 
SimpleAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)236             public SimpleAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)
237             {
238                 this.policy = (UnconditionalPolicy)authorizationPolicies[0];
239                 Dictionary<string, object> properties = new Dictionary<string, object>();
240                 if (this.policy.PrimaryIdentity != null && this.policy.PrimaryIdentity != SecurityUtils.AnonymousIdentity)
241                 {
242                     List<IIdentity> identities = new List<IIdentity>();
243                     identities.Add(this.policy.PrimaryIdentity);
244                     properties.Add(SecurityUtils.Identities, identities);
245                 }
246                 // Might need to port ReadOnlyDictionary?
247                 this.properties = properties;
248             }
249 
250             public override string Id
251             {
252                 get
253                 {
254                     if (this.id == null)
255                         this.id = SecurityUniqueId.Create();
256                     return this.id.Value;
257                 }
258             }
259             public override ReadOnlyCollection<ClaimSet> ClaimSets { get { return this.policy.Issuances; } }
260             public override DateTime ExpirationTime { get { return this.policy.ExpirationTime; } }
261             public override IDictionary<string, object> Properties { get { return this.properties; } }
262         }
263 
CreateDefaultAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)264         internal static AuthorizationContext CreateDefaultAuthorizationContext(IList<IAuthorizationPolicy> authorizationPolicies)
265         {
266             AuthorizationContext authorizationContext;
267             // This is faster than Policy evaluation.
268             if (authorizationPolicies != null && authorizationPolicies.Count == 1 && authorizationPolicies[0] is UnconditionalPolicy)
269             {
270                 authorizationContext = new SimpleAuthorizationContext(authorizationPolicies);
271             }
272             // degenerate case
273             else if (authorizationPolicies == null || authorizationPolicies.Count <= 0)
274             {
275                 return DefaultAuthorizationContext.Empty;
276             }
277             else
278             {
279                 // there are some policies, run them until they are all done
280                 DefaultEvaluationContext evaluationContext = new DefaultEvaluationContext();
281                 object[] policyState = new object[authorizationPolicies.Count];
282                 object done = new object();
283 
284                 int oldContextCount;
285                 do
286                 {
287                     oldContextCount = evaluationContext.Generation;
288 
289                     for (int i = 0; i < authorizationPolicies.Count; i++)
290                     {
291                         if (policyState[i] == done)
292                             continue;
293 
294                         IAuthorizationPolicy policy = authorizationPolicies[i];
295                         if (policy == null)
296                         {
297                             policyState[i] = done;
298                             continue;
299                         }
300 
301                         if (policy.Evaluate(evaluationContext, ref policyState[i]))
302                         {
303                             policyState[i] = done;
304 
305                             if (DiagnosticUtility.ShouldTraceVerbose)
306                             {
307                                 TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.AuthorizationPolicyEvaluated,
308                                     SR.GetString(SR.AuthorizationPolicyEvaluated, policy.Id));
309                             }
310                         }
311                     }
312 
313                 } while (oldContextCount < evaluationContext.Generation);
314 
315                 authorizationContext = new DefaultAuthorizationContext(evaluationContext);
316             }
317 
318             if (DiagnosticUtility.ShouldTraceInformation)
319             {
320                 TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.AuthorizationContextCreated,
321                     SR.GetString(SR.AuthorizationContextCreated, authorizationContext.Id));
322             }
323 
324             return authorizationContext;
325         }
326 
ClaimSetToString(ClaimSet claimSet)327         internal static string ClaimSetToString(ClaimSet claimSet)
328         {
329             StringBuilder sb = new StringBuilder();
330             sb.AppendLine("ClaimSet [");
331             for (int i = 0; i < claimSet.Count; i++)
332             {
333                 Claim claim = claimSet[i];
334                 if (claim != null)
335                 {
336                     sb.Append("  ");
337                     sb.AppendLine(claim.ToString());
338                 }
339             }
340             string prefix = "] by ";
341             ClaimSet issuer = claimSet;
342             do
343             {
344                 // PreSharp Bug: A null-dereference can occur here.
345 #pragma warning suppress 56506 // issuer was just set to this.
346                 issuer = issuer.Issuer;
347                 sb.AppendFormat("{0}{1}", prefix, issuer == claimSet ? "Self" : (issuer.Count <= 0 ? "Unknown" : issuer[0].ToString()));
348                 prefix = " -> ";
349             } while (issuer.Issuer != issuer);
350             return sb.ToString();
351         }
352 
353         // This is the workaround, Since store.Certificates returns a full collection
354         // of certs in store.  These are holding native resources.
ResetAllCertificates(X509Certificate2Collection certificates)355         internal static void ResetAllCertificates(X509Certificate2Collection certificates)
356         {
357             if (certificates != null)
358             {
359                 for (int i = 0; i < certificates.Count; ++i)
360                 {
361                     ResetCertificate(certificates[i]);
362                 }
363             }
364         }
365 
ReadContentAsBase64(XmlDictionaryReader reader, long maxBufferSize)366         internal static byte[] ReadContentAsBase64(XmlDictionaryReader reader, long maxBufferSize)
367         {
368             if (reader == null)
369                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("reader");
370 
371             // Code cloned from System.Xml.XmlDictionaryReder.
372             byte[][] buffers = new byte[32][];
373             byte[] buffer;
374             // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when converting text
375             int count = 384;
376             int bufferCount = 0;
377             int totalRead = 0;
378             while (true)
379             {
380                 buffer = new byte[count];
381                 buffers[bufferCount++] = buffer;
382                 int read = 0;
383                 while (read < buffer.Length)
384                 {
385                     int actual = reader.ReadContentAsBase64(buffer, read, buffer.Length - read);
386                     if (actual == 0)
387                         break;
388                     read += actual;
389                 }
390                 if (totalRead > maxBufferSize - read)
391                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new LimitExceededException(SR.GetString(SR.BufferQuotaExceededReadingBase64, maxBufferSize)));
392                 totalRead += read;
393                 if (read < buffer.Length)
394                     break;
395                 count = count * 2;
396             }
397             buffer = new byte[totalRead];
398             int offset = 0;
399             for (int i = 0; i < bufferCount - 1; i++)
400             {
401                 Buffer.BlockCopy(buffers[i], 0, buffer, offset, buffers[i].Length);
402                 offset += buffers[i].Length;
403             }
404             Buffer.BlockCopy(buffers[bufferCount - 1], 0, buffer, offset, totalRead - offset);
405             return buffer;
406         }
407 
DecryptKey(SecurityToken unwrappingToken, string encryptionMethod, byte[] wrappedKey, out SecurityKey unwrappingSecurityKey)408         internal static byte[] DecryptKey(SecurityToken unwrappingToken, string encryptionMethod, byte[] wrappedKey, out SecurityKey unwrappingSecurityKey)
409         {
410             unwrappingSecurityKey = null;
411             if (unwrappingToken.SecurityKeys != null)
412             {
413                 for (int i = 0; i < unwrappingToken.SecurityKeys.Count; ++i)
414                 {
415                     if (unwrappingToken.SecurityKeys[i].IsSupportedAlgorithm(encryptionMethod))
416                     {
417                         unwrappingSecurityKey = unwrappingToken.SecurityKeys[i];
418                         break;
419                     }
420                 }
421             }
422             if (unwrappingSecurityKey == null)
423             {
424                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityMessageSerializationException(SR.GetString(SR.CannotFindMatchingCrypto, encryptionMethod)));
425             }
426             return unwrappingSecurityKey.DecryptKey(encryptionMethod, wrappedKey);
427         }
428 
TryCreateX509CertificateFromRawData(byte[] rawData, out X509Certificate2 certificate)429         public static bool TryCreateX509CertificateFromRawData(byte[] rawData, out X509Certificate2 certificate)
430         {
431             certificate = (rawData == null || rawData.Length == 0) ? null : new X509Certificate2(rawData);
432             return certificate != null && certificate.Handle != IntPtr.Zero;
433         }
434 
DecodeHexString(string hexString)435         internal static byte[] DecodeHexString(string hexString)
436         {
437             hexString = hexString.Trim();
438 
439             bool spaceSkippingMode = false;
440 
441             int i = 0;
442             int length = hexString.Length;
443 
444             if ((length >= 2) &&
445                 (hexString[0] == '0') &&
446                 ((hexString[1] == 'x') || (hexString[1] == 'X')))
447             {
448                 length = hexString.Length - 2;
449                 i = 2;
450             }
451 
452             if (length < 2)
453                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
454 
455             byte[] sArray;
456 
457             if (length >= 3 && hexString[i + 2] == ' ')
458             {
459                 if (length % 3 != 2)
460                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
461 
462                 spaceSkippingMode = true;
463 
464                 // Each hex digit will take three spaces, except the first (hence the plus 1).
465                 sArray = DiagnosticUtility.Utility.AllocateByteArray(length / 3 + 1);
466             }
467             else
468             {
469                 if (length % 2 != 0)
470                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
471 
472                 spaceSkippingMode = false;
473 
474                 // Each hex digit will take two spaces
475                 sArray = DiagnosticUtility.Utility.AllocateByteArray(length / 2);
476             }
477 
478             int digit;
479             int rawdigit;
480             for (int j = 0; i < hexString.Length; i += 2, j++)
481             {
482                 rawdigit = ConvertHexDigit(hexString[i]);
483                 digit = ConvertHexDigit(hexString[i + 1]);
484                 sArray[j] = (byte)(digit | (rawdigit << 4));
485                 if (spaceSkippingMode)
486                     i++;
487             }
488             return (sArray);
489         }
490 
ConvertHexDigit(Char val)491         static int ConvertHexDigit(Char val)
492         {
493             if (val <= '9' && val >= '0')
494                 return (val - '0');
495             else if (val >= 'a' && val <= 'f')
496                 return ((val - 'a') + 10);
497             else if (val >= 'A' && val <= 'F')
498                 return ((val - 'A') + 10);
499             else
500                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SR.GetString(SR.InvalidHexString)));
501         }
502 
CreateAuthorizationPolicies(ClaimSet claimSet)503         internal static ReadOnlyCollection<IAuthorizationPolicy> CreateAuthorizationPolicies(ClaimSet claimSet)
504         {
505             return CreateAuthorizationPolicies(claimSet, SecurityUtils.MaxUtcDateTime);
506         }
507 
CreateAuthorizationPolicies(ClaimSet claimSet, DateTime expirationTime)508         internal static ReadOnlyCollection<IAuthorizationPolicy> CreateAuthorizationPolicies(ClaimSet claimSet, DateTime expirationTime)
509         {
510             List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
511             policies.Add(new UnconditionalPolicy(claimSet, expirationTime));
512             return policies.AsReadOnly();
513         }
514 
GenerateId()515         internal static string GenerateId()
516         {
517             return SecurityUniqueId.Create().Value;
518         }
519 
IsSupportedAlgorithm(string algorithm, SecurityToken token)520         internal static bool IsSupportedAlgorithm(string algorithm, SecurityToken token)
521         {
522             if (token.SecurityKeys == null)
523             {
524                 return false;
525             }
526             for (int i = 0; i < token.SecurityKeys.Count; ++i)
527             {
528                 if (token.SecurityKeys[i].IsSupportedAlgorithm(algorithm))
529                 {
530                     return true;
531                 }
532             }
533             return false;
534         }
535 
CloneIdentityIfNecessary(IIdentity identity)536         internal static IIdentity CloneIdentityIfNecessary(IIdentity identity)
537         {
538             if (identity != null)
539             {
540                 WindowsIdentity wid = identity as WindowsIdentity;
541                 if (wid != null)
542                 {
543                     return CloneWindowsIdentityIfNecessary(wid);
544                 }
545                 //X509Identity x509 = identity as X509Identity;
546                 //if (x509 != null)
547                 //{
548                 //    return x509.Clone();
549                 //}
550             }
551             return identity;
552         }
553 
554         /// <SecurityNote>
555         /// Critical - calls two critical methods: UnsafeGetWindowsIdentityToken and UnsafeCreateWindowsIdentityFromToken
556         /// Safe - "clone" operation is considered safe despite using WindowsIdentity IntPtr token
557         ///        must not let IntPtr token leak in or out
558         /// </SecurityNote>
559         [SecuritySafeCritical]
CloneWindowsIdentityIfNecessary(WindowsIdentity wid)560         internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid)
561         {
562             return CloneWindowsIdentityIfNecessary(wid, wid.AuthenticationType);
563         }
564 
565         [SecuritySafeCritical]
CloneWindowsIdentityIfNecessary(WindowsIdentity wid, string authenticationType)566         internal static WindowsIdentity CloneWindowsIdentityIfNecessary(WindowsIdentity wid, string authenticationType)
567         {
568 
569             if (wid != null)
570             {
571                 IntPtr token = UnsafeGetWindowsIdentityToken(wid);
572                 if (token != IntPtr.Zero)
573                 {
574                     return UnsafeCreateWindowsIdentityFromToken(token, authenticationType);
575                 }
576             }
577             return wid;
578         }
579 
580         /// <SecurityNote>
581         /// Critical - elevates in order to return the WindowsIdentity.Token property
582         ///            caller must protect return value
583         /// </SecurityNote>
584         [SecurityCritical]
585         [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
UnsafeGetWindowsIdentityToken(WindowsIdentity wid)586         static IntPtr UnsafeGetWindowsIdentityToken(WindowsIdentity wid)
587         {
588             return wid.Token;
589         }
590 
591         /// <SecurityNote>
592         /// Critical - elevates in order to construct a WindowsIdentity instance from an IntPtr
593         ///            caller must protect parameter return value
594         /// </SecurityNote>
595         // We pass the authenticationType in as WindowsIdentity will all into a priviledged call in LSA which could fail
596         // resulting in a null authenticationType.
597         [SecurityCritical]
598         [SecurityPermission(SecurityAction.Assert, ControlPrincipal = true, UnmanagedCode = true)]
UnsafeCreateWindowsIdentityFromToken(IntPtr token, string authenticationType)599         static WindowsIdentity UnsafeCreateWindowsIdentityFromToken(IntPtr token, string authenticationType)
600         {
601             if (authenticationType != null)
602             {
603                 return new WindowsIdentity(token, authenticationType);
604             }
605             else
606             {
607                 return new WindowsIdentity(token);
608             }
609         }
610 
CloneClaimSetIfNecessary(ClaimSet claimSet)611         internal static ClaimSet CloneClaimSetIfNecessary(ClaimSet claimSet)
612         {
613             if (claimSet != null)
614             {
615                 WindowsClaimSet wic = claimSet as WindowsClaimSet;
616                 if (wic != null)
617                 {
618                     return wic.Clone();
619                 }
620                 //X509CertificateClaimSet x509 = claimSet as X509CertificateClaimSet;
621                 //if (x509 != null)
622                 //{
623                 //    return x509.Clone();
624                 //}
625             }
626             return claimSet;
627         }
628 
CloneClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)629         internal static ReadOnlyCollection<ClaimSet> CloneClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)
630         {
631             if (claimSets != null)
632             {
633                 bool clone = false;
634                 for (int i = 0; i < claimSets.Count; ++i)
635                 {
636                     if (claimSets[i] is WindowsClaimSet)// || claimSets[i] is X509CertificateClaimSet)
637                     {
638                         clone = true;
639                         break;
640                     }
641                 }
642                 if (clone)
643                 {
644                     List<ClaimSet> ret = new List<ClaimSet>(claimSets.Count);
645                     for (int i = 0; i < claimSets.Count; ++i)
646                     {
647                         ret.Add(SecurityUtils.CloneClaimSetIfNecessary(claimSets[i]));
648                     }
649                     return ret.AsReadOnly();
650                 }
651             }
652             return claimSets;
653         }
654 
DisposeClaimSetIfNecessary(ClaimSet claimSet)655         internal static void DisposeClaimSetIfNecessary(ClaimSet claimSet)
656         {
657             if (claimSet != null)
658             {
659                 SecurityUtils.DisposeIfNecessary(claimSet as WindowsClaimSet);
660             }
661         }
662 
DisposeClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)663         internal static void DisposeClaimSetsIfNecessary(ReadOnlyCollection<ClaimSet> claimSets)
664         {
665             if (claimSets != null)
666             {
667                 for (int i = 0; i < claimSets.Count; ++i)
668                 {
669                     SecurityUtils.DisposeIfNecessary(claimSets[i] as WindowsClaimSet);
670                 }
671             }
672         }
673 
CloneAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)674         internal static ReadOnlyCollection<IAuthorizationPolicy> CloneAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
675         {
676             if (authorizationPolicies != null && authorizationPolicies.Count > 0)
677             {
678                 bool clone = false;
679                 for (int i = 0; i < authorizationPolicies.Count; ++i)
680                 {
681                     UnconditionalPolicy policy = authorizationPolicies[i] as UnconditionalPolicy;
682                     if (policy != null && policy.IsDisposable)
683                     {
684                         clone = true;
685                         break;
686                     }
687                 }
688                 if (clone)
689                 {
690                     List<IAuthorizationPolicy> ret = new List<IAuthorizationPolicy>(authorizationPolicies.Count);
691                     for (int i = 0; i < authorizationPolicies.Count; ++i)
692                     {
693                         UnconditionalPolicy policy = authorizationPolicies[i] as UnconditionalPolicy;
694                         if (policy != null)
695                         {
696                             ret.Add(policy.Clone());
697                         }
698                         else
699                         {
700                             ret.Add(authorizationPolicies[i]);
701                         }
702                     }
703                     return ret.AsReadOnly();
704                 }
705             }
706             return authorizationPolicies;
707         }
708 
DisposeAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)709         public static void DisposeAuthorizationPoliciesIfNecessary(ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
710         {
711             if (authorizationPolicies != null && authorizationPolicies.Count > 0)
712             {
713                 for (int i = 0; i < authorizationPolicies.Count; ++i)
714                 {
715                     DisposeIfNecessary(authorizationPolicies[i] as UnconditionalPolicy);
716                 }
717             }
718         }
719 
DisposeIfNecessary(IDisposable obj)720         public static void DisposeIfNecessary(IDisposable obj)
721         {
722             if (obj != null)
723             {
724                 obj.Dispose();
725             }
726         }
727     }
728 
729     /// <summary>
730     /// Internal helper class to help keep Kerberos and Spnego in sync.
731     /// This code is shared by:
732     ///     System\IdentityModel\Tokens\KerberosReceiverSecurityToken.cs
733     ///     System\ServiceModel\Security\WindowsSspiNegotiation.cs
734     /// Both this code paths require this logic.
735     /// </summary>
736     internal class ExtendedProtectionPolicyHelper
737     {
738         //
739         // keep the defaults: _protectionScenario and _policyEnforcement, in sync with: static class System.ServiceModel.Channel.ChannelBindingUtility
740         // We can't access those defaults as IdentityModel cannot take a dependency on ServiceModel
741         //
742         static ExtendedProtectionPolicy disabledPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
743 
744         PolicyEnforcement _policyEnforcement;
745         ProtectionScenario _protectionScenario;
746         ChannelBinding _channelBinding;
747         ServiceNameCollection _serviceNameCollection;
748         bool _checkServiceBinding;
749 
ExtendedProtectionPolicyHelper(ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)750         public ExtendedProtectionPolicyHelper(ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
751         {
752             _protectionScenario = DefaultPolicy.ProtectionScenario;
753             _policyEnforcement = DefaultPolicy.PolicyEnforcement;
754 
755             _channelBinding = channelBinding;
756             _serviceNameCollection = null;
757             _checkServiceBinding = true;
758 
759             if (extendedProtectionPolicy != null)
760             {
761                 _policyEnforcement = extendedProtectionPolicy.PolicyEnforcement;
762                 _protectionScenario = extendedProtectionPolicy.ProtectionScenario;
763                 _serviceNameCollection = extendedProtectionPolicy.CustomServiceNames;
764             }
765 
766             if (_policyEnforcement == PolicyEnforcement.Never)
767             {
768                 _checkServiceBinding = false;
769             }
770         }
771 
ShouldAddChannelBindingToASC()772         public bool ShouldAddChannelBindingToASC()
773         {
774             return (_channelBinding != null && _policyEnforcement != PolicyEnforcement.Never && _protectionScenario != ProtectionScenario.TrustedProxy);
775         }
776 
777         public ChannelBinding ChannelBinding
778         {
779             get { return _channelBinding; }
780         }
781 
782         public bool ShouldCheckServiceBinding
783         {
784             get { return _checkServiceBinding; }
785         }
786 
787         public ServiceNameCollection ServiceNameCollection
788         {
789             get { return _serviceNameCollection; }
790         }
791 
792         public ProtectionScenario ProtectionScenario
793         {
794             get { return _protectionScenario; }
795         }
796 
797         public PolicyEnforcement PolicyEnforcement
798         {
799             get { return _policyEnforcement; }
800         }
801 
802         /// <summary>
803         /// ServiceBinding check has the following logic:
804         /// 1. Check PolicyEnforcement - never => return true;
805         /// 1. Check status returned from SecurityContext which is obtained when querying for the serviceBinding
806         /// 2. Check PolicyEnforcement
807         ///     a. WhenSupported - valid when OS does not support, null serviceBinding is valid
808         ///     b. Always - a non-empty servicebinding must be available
809         /// 3. if serviceBinding is non null, check that an expected value is in the ServiceNameCollection - ignoring case
810         ///    note that the empty string must be explicitly specified in the serviceNames.
811         /// </summary>
812         /// <param name="securityContext to ">status Code returned when obtaining serviceBinding from SecurityContext</param>
813         /// <returns>If servicebinding is valid</returns>
CheckServiceBinding(SafeDeleteContext securityContext, string defaultServiceBinding)814         public void CheckServiceBinding(SafeDeleteContext securityContext, string defaultServiceBinding)
815         {
816             if (_policyEnforcement == PolicyEnforcement.Never)
817             {
818                 return;
819             }
820 
821             string serviceBinding = null;
822             int statusCode = SspiWrapper.QuerySpecifiedTarget(securityContext, out serviceBinding);
823 
824             if (statusCode != (int)SecurityStatus.OK)
825             {
826                 // only two acceptable non-zero values
827                 // client OS not patched: stausCode == TargetUnknown
828                 // service OS not patched: statusCode == Unsupported
829                 if (statusCode != (int)SecurityStatus.TargetUnknown && statusCode != (int)SecurityStatus.Unsupported)
830                 {
831                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
832                 }
833 
834                 // if policyEnforcement is Always we needed to see a TargetName (SPN)
835                 if (_policyEnforcement == PolicyEnforcement.Always)
836                 {
837                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
838                 }
839 
840                 // in this case we accept because either the client or service is not patched.
841                 if (_policyEnforcement == PolicyEnforcement.WhenSupported)
842                 {
843                     return;
844                 }
845 
846                 // guard against futures, force failure and fix as necessary
847                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationNoServiceBinding)));
848             }
849 
850             switch (_policyEnforcement)
851             {
852                 case PolicyEnforcement.WhenSupported:
853                     // serviceBinding == null => client is not patched
854                     if (serviceBinding == null)
855                         return;
856                     break;
857 
858                 case PolicyEnforcement.Always:
859                     // serviceBinding == null => client is not patched
860                     // serviceBinding == "" => SB was not specified
861                     if (string.IsNullOrEmpty(serviceBinding))
862                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
863                     break;
864             }
865 
866             // iff no values were 'user' set, then check the defaultServiceBinding
867             if (_serviceNameCollection == null || _serviceNameCollection.Count < 1)
868             {
869                 if (defaultServiceBinding == null)
870                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
871 
872                 if (string.Compare(defaultServiceBinding, serviceBinding, StringComparison.OrdinalIgnoreCase) == 0)
873                     return;
874 
875                 if (string.IsNullOrEmpty(serviceBinding))
876                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
877                 else
878                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, serviceBinding)));
879             }
880 
881             if (_serviceNameCollection != null)
882             {
883                 if (_serviceNameCollection.Contains(serviceBinding))
884                 {
885                     return;
886                 }
887             }
888 
889             if (string.IsNullOrEmpty(serviceBinding))
890                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, string.Empty)));
891             else
892                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenException(SR.GetString(SR.InvalidServiceBindingInSspiNegotiationServiceBindingNotMatched, serviceBinding)));
893         }
894 
895         /// <summary>
896         /// Keep this in sync with \System\ServiceModel\Channels\ChannelBindingUtility.cs
897         /// </summary>
898         public static ExtendedProtectionPolicy DefaultPolicy
899         {   //
900             //keep the default in sync with : static class System.ServiceModel.Channels.ChannelBindingUtility
901             //we can't use these defaults as IdentityModel cannot take a dependency on ServiceModel
902             //
903 
904             // Current POR is "Never" respect the above note.
905 
906             get { return disabledPolicy; }
907         }
908     }
909 
910     static class EmptyReadOnlyCollection<T>
911     {
912         public static ReadOnlyCollection<T> Instance = new ReadOnlyCollection<T>(new List<T>());
913     }
914 }
915