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