1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 // <OWNER>Microsoft</OWNER> 7 // 8 9 namespace System.Security.Policy 10 { 11 using System; 12 using System.Collections; 13 using System.Collections.Generic; 14 using System.Configuration.Assemblies; 15 using System.Diagnostics.Contracts; 16 using System.IO; 17 using System.Reflection; 18 using System.Runtime.CompilerServices; 19 using System.Runtime.InteropServices; 20 using System.Runtime.Remoting; 21 #if FEATURE_SERIALIZATION 22 using System.Runtime.Serialization; 23 using System.Runtime.Serialization.Formatters.Binary; 24 #endif // FEATURE_SERIALIZATION 25 using System.Security.Permissions; 26 using System.Security.Util; 27 using System.Threading; 28 using Microsoft.Win32.SafeHandles; 29 30 /// <summary> 31 /// The Evidence class keeps track of information that can be used to make security decisions about 32 /// an assembly or an AppDomain. There are two types of evidence, one is supplied by the CLR or a 33 /// host, the other supplied by the assembly itself. 34 /// 35 /// We keep a dictionary that maps each type of possbile evidence to an EvidenceTypeDescriptor which 36 /// contains the evidence objects themselves if they exist as well as some extra metadata about that 37 /// type of evidence. This dictionary is fully populated with keys for host evidence at all times and 38 /// for assembly evidence the first time the application evidence is touched. This means that if a 39 /// Type key does not exist in the dictionary, then that particular type of evidence will never be 40 /// given to the assembly or AppDomain in question as host evidence. The only exception is if the 41 /// user later manually adds host evidence via the AddHostEvidence API. 42 /// 43 /// Assembly supplied evidence is created up front, however host supplied evidence may be lazily 44 /// created. In the lazy creation case, the Type will map to either an EvidenceTypeDescriptor that does 45 /// not contain any evidence data or null. As requests come in for that evidence, we'll populate the 46 /// EvidenceTypeDescriptor appropriately. 47 /// </summary> 48 [Serializable] 49 [ComVisible(true)] 50 public sealed class Evidence 51 #if FEATURE_CAS_POLICY 52 : ICollection 53 #endif // FEATURE_CAS_POLICY 54 { 55 #if !FEATURE_CORECLR && FEATURE_RWLOCK 56 #if FEATURE_SERIALIZATION 57 [OptionalField(VersionAdded = 4)] 58 private Dictionary<Type, EvidenceTypeDescriptor> m_evidence; 59 60 [OptionalField(VersionAdded = 4)] 61 private bool m_deserializedTargetEvidence; 62 63 // These fields are only used to deserialize v2.0 serialized versions of Evidence. It will be null 64 // after the seriailzation process is complete, and should not be used. 65 #pragma warning disable 414 66 private volatile ArrayList m_hostList; 67 private volatile ArrayList m_assemblyList; 68 #pragma warning restore 414 69 #else // !FEATURE_SERIALIZATION 70 private Dictionary<Type, EvidenceTypeDescriptor> m_evidence; 71 #endif // FEATURE_SERIALIZATION 72 73 [NonSerialized] 74 private ReaderWriterLock m_evidenceLock; 75 76 [NonSerialized] 77 private uint m_version; 78 79 [NonSerialized] 80 private IRuntimeEvidenceFactory m_target; 81 82 private bool m_locked; 83 84 // If this evidence collection is a clone where we may need to backpatch to the original, this will 85 // reference the collection it was cloned from. See 86 // code:System.Security.Policy.Evidence#BackpatchGeneratedEvidence 87 [NonSerialized] 88 private WeakReference m_cloneOrigin; 89 90 private static volatile Type[] s_runtimeEvidenceTypes; 91 92 /// <summary> 93 /// Set of actions that we could perform if we detect that we are attempting to add evidence 94 /// when we already have evidence of that type stored. 95 /// </summary> 96 private enum DuplicateEvidenceAction 97 { 98 Throw, // Throw an exception 99 Merge, // Create a list of all the evidence objects 100 SelectNewObject // The newly added object wins 101 } 102 103 #if FEATURE_CAS_POLICY Evidence()104 public Evidence() 105 { 106 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 107 m_evidenceLock = new ReaderWriterLock(); 108 } 109 #endif // FEATURE_CAS_POLICY 110 111 /// <summary> 112 /// Create a deep copy of an evidence object 113 /// </summary> Evidence(Evidence evidence)114 public Evidence(Evidence evidence) 115 { 116 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 117 118 if (evidence != null) 119 { 120 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(evidence, EvidenceLockHolder.LockType.Reader)) 121 { 122 foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in evidence.m_evidence) 123 { 124 EvidenceTypeDescriptor cloneDescriptor = evidenceType.Value; 125 if (cloneDescriptor != null) 126 { 127 cloneDescriptor = cloneDescriptor.Clone(); 128 } 129 130 m_evidence[evidenceType.Key] = cloneDescriptor; 131 } 132 133 m_target = evidence.m_target; 134 m_locked = evidence.m_locked; 135 #if FEATURE_SERIALIZATION 136 m_deserializedTargetEvidence = evidence.m_deserializedTargetEvidence; 137 #endif // FEATURE_SERIALIZATION 138 139 // see code:System.Security.Policy.Evidence#BackpatchGeneratedEvidence 140 if (evidence.Target != null) 141 { 142 m_cloneOrigin = new WeakReference(evidence); 143 } 144 } 145 } 146 147 // see code:System.Security.Policy.Evidence#EvidenceLock 148 m_evidenceLock = new ReaderWriterLock(); 149 } 150 151 [Obsolete("This constructor is obsolete. Please use the constructor which takes arrays of EvidenceBase instead.")] Evidence(object[] hostEvidence, object[] assemblyEvidence)152 public Evidence(object[] hostEvidence, object[] assemblyEvidence) 153 { 154 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 155 156 // This is a legacy evidence entry point, so we add through the legacy add APIs in order to get 157 // proper legacy wrapping and merge behavior. 158 #pragma warning disable 618 159 if (hostEvidence != null) 160 { 161 foreach (object hostEvidenceObject in hostEvidence) 162 { 163 AddHost(hostEvidenceObject); 164 } 165 } 166 167 if (assemblyEvidence != null) 168 { 169 foreach (object assemblyEvidenceObject in assemblyEvidence) 170 { 171 AddAssembly(assemblyEvidenceObject); 172 } 173 } 174 #pragma warning restore 618 175 176 // see code:System.Security.Policy.Evidence#EvidenceLock 177 m_evidenceLock = new ReaderWriterLock(); 178 } 179 Evidence(EvidenceBase[] hostEvidence, EvidenceBase[] assemblyEvidence)180 public Evidence(EvidenceBase[] hostEvidence, EvidenceBase[] assemblyEvidence) 181 { 182 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 183 184 if (hostEvidence != null) 185 { 186 foreach (EvidenceBase hostEvidenceObject in hostEvidence) 187 { 188 AddHostEvidence(hostEvidenceObject, GetEvidenceIndexType(hostEvidenceObject), DuplicateEvidenceAction.Throw); 189 } 190 } 191 192 if (assemblyEvidence != null) 193 { 194 foreach (EvidenceBase assemblyEvidenceObject in assemblyEvidence) 195 { 196 AddAssemblyEvidence(assemblyEvidenceObject, GetEvidenceIndexType(assemblyEvidenceObject), DuplicateEvidenceAction.Throw); 197 } 198 } 199 200 // see code:System.Security.Policy.Evidence#EvidenceLock 201 m_evidenceLock = new ReaderWriterLock(); 202 } 203 204 /// <summary> 205 /// Create an empty evidence collection which will contain evidence for a specific assembly or 206 /// AppDomain 207 /// </summary> 208 [SecuritySafeCritical] Evidence(IRuntimeEvidenceFactory target)209 internal Evidence(IRuntimeEvidenceFactory target) 210 { 211 Contract.Assert(target != null); 212 213 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 214 m_target = target; 215 216 // Setup the types of evidence that the CLR can generate for a target as keys in the dictionary 217 foreach (Type runtimeEvidenceType in RuntimeEvidenceTypes) 218 { 219 BCLDebug.Assert(typeof(EvidenceBase).IsAssignableFrom(runtimeEvidenceType), "All runtime evidence types should be EvidenceBases"); 220 m_evidence[runtimeEvidenceType] = null; 221 } 222 223 QueryHostForPossibleEvidenceTypes(); 224 225 // see code:System.Security.Policy.Evidence#EvidenceLock 226 m_evidenceLock = new ReaderWriterLock(); 227 } 228 229 internal static Type[] RuntimeEvidenceTypes 230 { 231 get 232 { 233 if (s_runtimeEvidenceTypes == null) 234 { 235 Type[] runtimeEvidenceTypes = new Type[] 236 { 237 #if FEATURE_CLICKONCE 238 typeof(System.Runtime.Hosting.ActivationArguments), 239 #endif // FEATURE_CLICKONCE 240 #if FEATURE_CAS_POLICY 241 typeof(ApplicationDirectory), 242 #endif // FEATURE_CAS_POLICY 243 typeof(ApplicationTrust), 244 #if FEATURE_CAS_POLICY 245 typeof(GacInstalled), 246 typeof(Hash), 247 typeof(Publisher), 248 #endif // FEATURE_CAS_POLICY 249 typeof(Site), 250 typeof(StrongName), 251 typeof(Url), 252 typeof(Zone) 253 }; 254 255 #if FEATURE_CAS_POLICY 256 // We only supply permission request evidence in legacy CAS mode 257 if (AppDomain.CurrentDomain.IsLegacyCasPolicyEnabled) 258 { 259 #pragma warning disable 618 // We need to generate PermissionRequestEvidence in compatibility mode 260 int l = runtimeEvidenceTypes.Length; 261 Array.Resize(ref runtimeEvidenceTypes, l+1); 262 runtimeEvidenceTypes[l] = typeof(PermissionRequestEvidence); 263 #pragma warning restore 618 264 } 265 #endif // FEATURE_CAS_POLICY 266 267 s_runtimeEvidenceTypes = runtimeEvidenceTypes; 268 } 269 270 return s_runtimeEvidenceTypes; 271 } 272 } 273 274 // 275 // #EvidenceLock 276 // 277 // Evidence synchronization locking wrappers. In the case where the lock has not yet been created, 278 // we know that we're in the process of constructing the evidence collection and therefore we can 279 // act as though the evidence is locked. If there is a lock in place, then just delegate back to it. 280 // 281 // The nested EvidenceLockHolder and EvidenceUpgradeLockHolder utility classes can be used to wrap 282 // these methods when acquiring and releasing the evidence lock. 283 // 284 285 // Millisecond timeout when waiting to acquire the evidence lock 286 private const int LockTimeout = 5000; 287 288 private bool IsReaderLockHeld 289 { 290 get { return m_evidenceLock == null || m_evidenceLock.IsReaderLockHeld; } 291 } 292 293 private bool IsWriterLockHeld 294 { 295 get { return m_evidenceLock == null || m_evidenceLock.IsWriterLockHeld; } 296 } 297 AcquireReaderLock()298 private void AcquireReaderLock() 299 { 300 Contract.Assert(m_evidenceLock == null || !IsReaderLockHeld); 301 302 if (m_evidenceLock != null) 303 { 304 m_evidenceLock.AcquireReaderLock(LockTimeout); 305 } 306 } 307 AcquireWriterlock()308 private void AcquireWriterlock() 309 { 310 Contract.Assert(m_evidenceLock == null || !IsWriterLockHeld); 311 312 if (m_evidenceLock != null) 313 { 314 m_evidenceLock.AcquireWriterLock(LockTimeout); 315 } 316 } 317 DowngradeFromWriterLock(ref LockCookie lockCookie)318 private void DowngradeFromWriterLock(ref LockCookie lockCookie) 319 { 320 Contract.Assert(IsWriterLockHeld); 321 if (m_evidenceLock != null) 322 { 323 m_evidenceLock.DowngradeFromWriterLock(ref lockCookie); 324 } 325 } 326 UpgradeToWriterLock()327 private LockCookie UpgradeToWriterLock() 328 { 329 Contract.Assert(IsReaderLockHeld); 330 return m_evidenceLock != null ? m_evidenceLock.UpgradeToWriterLock(LockTimeout) : new LockCookie(); 331 } 332 ReleaseReaderLock()333 private void ReleaseReaderLock() 334 { 335 Contract.Assert(IsReaderLockHeld); 336 337 if (m_evidenceLock != null) 338 { 339 m_evidenceLock.ReleaseReaderLock(); 340 } 341 } 342 ReleaseWriterLock()343 private void ReleaseWriterLock() 344 { 345 Contract.Assert(IsWriterLockHeld); 346 347 if (m_evidenceLock != null) 348 { 349 m_evidenceLock.ReleaseWriterLock(); 350 } 351 } 352 353 [Obsolete("This method is obsolete. Please use AddHostEvidence instead.")] 354 [SecuritySafeCritical] AddHost(object id)355 public void AddHost(object id) 356 { 357 if (id == null) 358 throw new ArgumentNullException("id"); 359 if (!id.GetType().IsSerializable) 360 throw new ArgumentException(Environment.GetResourceString("Policy_EvidenceMustBeSerializable"), "id"); 361 Contract.EndContractBlock(); 362 363 if (m_locked) 364 { 365 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 366 } 367 368 EvidenceBase evidence = WrapLegacyEvidence(id); 369 Type evidenceIndex = GetEvidenceIndexType(evidence); 370 371 // Whidbey allowed for multiple types of the same evidence, so if we're being called via the Whidbey 372 // APIs, then allow the evidences to merge together. 373 AddHostEvidence(evidence, evidenceIndex, DuplicateEvidenceAction.Merge); 374 } 375 376 [Obsolete("This method is obsolete. Please use AddAssemblyEvidence instead.")] AddAssembly(object id)377 public void AddAssembly(object id) 378 { 379 if (id == null) 380 throw new ArgumentNullException("id"); 381 if (!id.GetType().IsSerializable) 382 throw new ArgumentException(Environment.GetResourceString("Policy_EvidenceMustBeSerializable"), "id"); 383 Contract.EndContractBlock(); 384 385 EvidenceBase evidence = WrapLegacyEvidence(id); 386 Type evidenceIndex = GetEvidenceIndexType(evidence); 387 388 // Whidbey allowed for multiple types of the same evidence, so if we're being called via the Whidbey 389 // APIs, then allow the evidences to merge together. 390 AddAssemblyEvidence(evidence, evidenceIndex, DuplicateEvidenceAction.Merge); 391 } 392 393 /// <summary> 394 /// Add a piece of evidence to the assembly supplied evidence list. This method will disallow adding 395 /// evidence if there is already evidence of that type in the assembly list. 396 /// </summary> 397 [ComVisible(false)] 398 public void AddAssemblyEvidence<T>(T evidence) where T : EvidenceBase 399 { 400 if (evidence == null) 401 throw new ArgumentNullException("evidence"); 402 Contract.EndContractBlock(); 403 404 // Index the evidence under the type that the Add function was called with, unless we were given 405 // a plain EvidenceBase or a wrapped legacy evidence. In that case, we need to index under a 406 // more specific type. 407 Type evidenceType = typeof(T); 408 if (typeof(T) == typeof(EvidenceBase) || evidence is ILegacyEvidenceAdapter) 409 { 410 evidenceType = GetEvidenceIndexType(evidence); 411 } 412 413 AddAssemblyEvidence(evidence, evidenceType, DuplicateEvidenceAction.Throw); 414 } 415 AddAssemblyEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)416 private void AddAssemblyEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction) 417 { 418 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 419 { 420 AddAssemblyEvidenceNoLock(evidence, evidenceType, duplicateAction); 421 } 422 } 423 AddAssemblyEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)424 private void AddAssemblyEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction) 425 { 426 Contract.Assert(IsWriterLockHeld); 427 Contract.Assert(evidence != null); 428 Contract.Assert(evidenceType != null); 429 430 // We need to make sure that any target supplied evidence is deserialized before adding to the 431 // Assembly collection in order to preserve the semantics that the evidence objects supplied by 432 // the target are the original versions and evidence objects added via the APIs are the duplicates. 433 DeserializeTargetEvidence(); 434 435 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType, true); 436 437 ++m_version; 438 if (descriptor.AssemblyEvidence == null) 439 { 440 descriptor.AssemblyEvidence = evidence; 441 } 442 else 443 { 444 descriptor.AssemblyEvidence = HandleDuplicateEvidence(descriptor.AssemblyEvidence, 445 evidence, 446 duplicateAction); 447 } 448 } 449 450 /// <summary> 451 /// Add a piece of evidence to the host supplied evidence list. This method will disallow adding 452 /// evidence if there is already evidence of that type in the host list. 453 /// </summary> 454 [ComVisible(false)] 455 public void AddHostEvidence<T>(T evidence) where T : EvidenceBase 456 { 457 if (evidence == null) 458 throw new ArgumentNullException("evidence"); 459 Contract.EndContractBlock(); 460 461 // Index the evidence under the type that the Add function was called with, unless we were given 462 // a plain EvidenceBase or a wrapped legacy evidence. In that case, we need to index under a 463 // more specific type. 464 Type evidenceType = typeof(T); 465 if (typeof(T) == typeof(EvidenceBase) || evidence is ILegacyEvidenceAdapter) 466 { 467 evidenceType = GetEvidenceIndexType(evidence); 468 } 469 470 AddHostEvidence(evidence, evidenceType, DuplicateEvidenceAction.Throw); 471 } 472 473 [SecuritySafeCritical] AddHostEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)474 private void AddHostEvidence(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction) 475 { 476 Contract.Assert(evidence != null); 477 Contract.Assert(evidenceType != null); 478 479 if (Locked) 480 { 481 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 482 } 483 484 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 485 { 486 AddHostEvidenceNoLock(evidence, evidenceType, duplicateAction); 487 } 488 } 489 490 /// <summary> 491 /// Add evidence to the host supplied evidence collection without acquiring the evidence lock or 492 /// checking to make sure that the caller has permission to bypass locked evidence. 493 /// </summary> AddHostEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction)494 private void AddHostEvidenceNoLock(EvidenceBase evidence, Type evidenceType, DuplicateEvidenceAction duplicateAction) 495 { 496 Contract.Assert(IsWriterLockHeld); 497 Contract.Assert(evidence != null); 498 Contract.Assert(evidenceType != null); 499 500 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType, true); 501 502 ++m_version; 503 if (descriptor.HostEvidence == null) 504 { 505 descriptor.HostEvidence = evidence; 506 } 507 else 508 { 509 descriptor.HostEvidence = HandleDuplicateEvidence(descriptor.HostEvidence, 510 evidence, 511 duplicateAction); 512 } 513 } 514 515 /// <summary> 516 /// Ask the host for the types of evidence that it might provide if it is asked. 517 /// 518 /// This should only be called when setting up the Evidence collection to interact with the 519 /// host, and should not be used once that connection is established and the evidence has been 520 /// made available to user code. 521 /// </summary> 522 [SecurityCritical] QueryHostForPossibleEvidenceTypes()523 private void QueryHostForPossibleEvidenceTypes() 524 { 525 #if FEATURE_CAS_POLICY 526 Contract.Assert(IsWriterLockHeld); 527 528 // First check to see if we have a HostSecurityManager 529 if (AppDomain.CurrentDomain.DomainManager != null) 530 { 531 HostSecurityManager hsm = AppDomain.CurrentDomain.DomainManager.HostSecurityManager; 532 if (hsm != null) 533 { 534 Type[] hostSuppliedTypes = null; 535 536 AppDomain targetDomain = m_target.Target as AppDomain; 537 Assembly targetAssembly = m_target.Target as Assembly; 538 539 // 540 // If the HostSecurityManager wants to supply evidence for the type of target that we have, 541 // then ask it what types of evidence it might supply. 542 // 543 544 if (targetAssembly != null && 545 (hsm.Flags & HostSecurityManagerOptions.HostAssemblyEvidence) == HostSecurityManagerOptions.HostAssemblyEvidence) 546 { 547 hostSuppliedTypes = hsm.GetHostSuppliedAssemblyEvidenceTypes(targetAssembly); 548 } 549 else if (targetDomain != null && 550 (hsm.Flags & HostSecurityManagerOptions.HostAppDomainEvidence) == HostSecurityManagerOptions.HostAppDomainEvidence) 551 { 552 hostSuppliedTypes = hsm.GetHostSuppliedAppDomainEvidenceTypes(); 553 } 554 555 // 556 // Finally, mark the descriptor for each of the types that the host can supply to indicate 557 // we should ask the host to generate them if we're asked. 558 // 559 560 if (hostSuppliedTypes != null) 561 { 562 foreach (Type hostEvidenceType in hostSuppliedTypes) 563 { 564 EvidenceTypeDescriptor evidenceDescriptor = GetEvidenceTypeDescriptor(hostEvidenceType, true); 565 evidenceDescriptor.HostCanGenerate = true; 566 } 567 } 568 } 569 } 570 #endif // FEATURE_CAS_POLICY 571 } 572 573 internal bool IsUnmodified 574 { 575 get { return m_version == 0; } 576 } 577 578 /// <summary> 579 /// Set or check to see if the evidence is locked. Locked evidence cannot have its host supplied 580 /// evidence list be modified without a successful demand for ControlEvidence. Any code can lock 581 /// evidence, but only code with ControlEvidence may unlock it. 582 /// 583 /// This lock is not the same as the synchronization lock that gates access to the evidence collection. 584 /// </summary> 585 public bool Locked 586 { 587 get 588 { 589 return m_locked; 590 } 591 592 [SecuritySafeCritical] 593 set 594 { 595 if (!value) 596 { 597 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 598 599 m_locked = false; 600 } 601 else 602 { 603 m_locked = true; 604 } 605 } 606 } 607 608 /// <summary> 609 /// Target of any delay generated evidence objects 610 /// </summary> 611 internal IRuntimeEvidenceFactory Target 612 { 613 get { return m_target; } 614 615 // 616 // There are two retargeting scenarios supported: 617 // 618 // 1. A PEFileEvidenceFactory is being upgraded to an AssemblyEvidenceFactory and we don't want 619 // to throw away any already generated evidence. 620 // 2. A detached evidence collection is being applied to an AppDomain and that domain has a 621 // HostSecurityManager. In that case, we want to attach the target to the AppDomain to 622 // allow the HostSecurityManager to get callbacks for delay generated evidence. 623 // 624 625 [SecurityCritical] 626 set 627 { 628 #if FEATURE_CAS_POLICY 629 Contract.Assert((m_target != null && m_target is PEFileEvidenceFactory && value != null && value is AssemblyEvidenceFactory) || 630 (m_target == null && value != null && value is AppDomainEvidenceFactory), 631 "Evidence retargeting should only be from PEFile -> Assembly or detached -> AppDomain."); 632 #endif // FEATURE_CAS_POLICY 633 634 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 635 { 636 m_target = value; 637 638 // Since we've updated what we're pointing at, we need to query the host to determine what 639 // types of evidence that it can generate for this new target. 640 QueryHostForPossibleEvidenceTypes(); 641 } 642 } 643 } 644 645 /// <summary> 646 /// Get the type that would be used to index into the evidence dictionary for this object 647 /// </summary> GetEvidenceIndexType(EvidenceBase evidence)648 private static Type GetEvidenceIndexType(EvidenceBase evidence) 649 { 650 Contract.Assert(evidence != null); 651 652 // 653 // Legacy wrapper evidence types should be indexed via the type of evidence that they're wrapping 654 // so check to see if we have one of those; otherwise just return the type itself. 655 // 656 657 ILegacyEvidenceAdapter adapter = evidence as ILegacyEvidenceAdapter; 658 return adapter == null ? evidence.GetType() : adapter.EvidenceType; 659 } 660 661 /// <summary> 662 /// Get the type descriptor for a specific type of evidence. This method should be used instead 663 /// of accessing the dictionary directly as it will handle the case where a new descriptor needs 664 /// to be created. 665 /// </summary> GetEvidenceTypeDescriptor(Type evidenceType)666 internal EvidenceTypeDescriptor GetEvidenceTypeDescriptor(Type evidenceType) 667 { 668 return GetEvidenceTypeDescriptor(evidenceType, false); 669 } 670 671 /// <summary> 672 /// Get the type descriptor for a specific type of evidence, optionally creating a descriptor if 673 /// we did not yet know about this type of evidence. This method should be used instead of 674 /// accessing the dictionary directly as it will handle the case where a new descriptor needs 675 /// to be created. 676 /// </summary> GetEvidenceTypeDescriptor(Type evidenceType, bool addIfNotExist)677 private EvidenceTypeDescriptor GetEvidenceTypeDescriptor(Type evidenceType, bool addIfNotExist) 678 { 679 Contract.Assert(IsReaderLockHeld || IsWriterLockHeld); 680 Contract.Assert(evidenceType != null); 681 682 // If we don't know about the type being indexed and we don't want to add it then exit out 683 EvidenceTypeDescriptor descriptor = null; 684 if (!m_evidence.TryGetValue(evidenceType, out descriptor) && !addIfNotExist) 685 { 686 return null; 687 } 688 689 // If we haven't yet created a descriptor for this type then create one now 690 if (descriptor == null) 691 { 692 descriptor = new EvidenceTypeDescriptor(); 693 #if _DEBUG 694 descriptor.SetEvidenceType(evidenceType); 695 #endif // _DEBUG 696 697 bool upgradedLock = false; 698 LockCookie upgradeCookie = new LockCookie(); 699 try 700 { 701 if (!IsWriterLockHeld) 702 { 703 upgradeCookie = UpgradeToWriterLock(); 704 upgradedLock = true; 705 } 706 707 m_evidence[evidenceType] = descriptor; 708 } 709 finally 710 { 711 if (upgradedLock) 712 DowngradeFromWriterLock(ref upgradeCookie); 713 } 714 } 715 716 return descriptor; 717 } 718 719 /// <summary> 720 /// This method is called if a piece of evidence is added but another piece of evidence of the same 721 /// type already existed. We have different strategies depending on compatibility concerns of the 722 /// calling code. 723 /// </summary> HandleDuplicateEvidence(EvidenceBase original, EvidenceBase duplicate, DuplicateEvidenceAction action)724 private static EvidenceBase HandleDuplicateEvidence(EvidenceBase original, 725 EvidenceBase duplicate, 726 DuplicateEvidenceAction action) 727 { 728 Contract.Assert(original != null); 729 Contract.Assert(duplicate != null); 730 Contract.Assert(original.GetType() == duplicate.GetType() || original.GetType() == typeof(LegacyEvidenceList)); 731 732 switch (action) 733 { 734 // Throw - duplicate evidence is not allowed (Arrowhead behavior), so throw an exception 735 case DuplicateEvidenceAction.Throw: 736 throw new InvalidOperationException(Environment.GetResourceString("Policy_DuplicateEvidence", duplicate.GetType().FullName)); 737 738 // SelectNewObject - MergeWithNoDuplicates behavior - the duplicate object wins 739 case DuplicateEvidenceAction.SelectNewObject: 740 return duplicate; 741 742 // Merge - compat behavior. Merge the old and new evidence into a list so that both may exist 743 case DuplicateEvidenceAction.Merge: 744 745 LegacyEvidenceList list = original as LegacyEvidenceList; 746 if (list == null) 747 { 748 list = new LegacyEvidenceList(); 749 list.Add(original); 750 } 751 752 list.Add(duplicate); 753 return list; 754 755 default: 756 BCLDebug.Assert(false, "Uknown DuplicateEvidenceAction"); 757 return null; 758 } 759 } 760 761 /// <summary> 762 /// Wrap evidence we recieved through a legacy API to ensure that it is stored in an EvidenceBase 763 /// </summary> WrapLegacyEvidence(object evidence)764 private static EvidenceBase WrapLegacyEvidence(object evidence) 765 { 766 Contract.Assert(evidence != null); 767 768 EvidenceBase wrappedEvidence = evidence as EvidenceBase; 769 if (wrappedEvidence == null) 770 { 771 wrappedEvidence = new LegacyEvidenceWrapper(evidence); 772 } 773 774 return wrappedEvidence; 775 } 776 777 /// <summary> 778 /// Upwrap evidence stored in a legacy adapter. 779 /// 780 /// This is only necessary for the case where multiple objects derived from EvidenceBase is 781 /// are added via the legacy APIs and are then retrieved via GetHostEvidence. This may occur if 782 /// a legacy application adds CLR supplied evidence types via the old APIs and a new application 783 /// consumes the resulting evidence. 784 /// </summary> UnwrapEvidence(EvidenceBase evidence)785 private static object UnwrapEvidence(EvidenceBase evidence) 786 { 787 ILegacyEvidenceAdapter adapter = evidence as ILegacyEvidenceAdapter; 788 return adapter == null ? evidence : adapter.EvidenceObject; 789 } 790 791 /// <summary> 792 /// Merge two evidence collections together. Note that this will cause all of the lazily 793 /// generated evidence for the input collection to be generated, as well as causing any lazily 794 /// generated evidence that both collections share to be generated in the target. 795 /// </summary> 796 [SecuritySafeCritical] Merge(Evidence evidence)797 public void Merge(Evidence evidence) 798 { 799 if (evidence == null) 800 { 801 return; 802 } 803 804 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 805 { 806 bool checkedLock = false; 807 IEnumerator hostEnumerator = evidence.GetHostEnumerator(); 808 while (hostEnumerator.MoveNext()) 809 { 810 if (Locked && !checkedLock) 811 { 812 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 813 checkedLock = true; 814 } 815 816 // If we could potentially have evidence of the type about to be merged into our host list, 817 // then make sure that we generate that evidence before merging. This will prevent the 818 // newly merged evidence from masking the value that we would have generated on our own. 819 Type hostEvidenceType = hostEnumerator.Current.GetType(); 820 if (m_evidence.ContainsKey(hostEvidenceType)) 821 { 822 GetHostEvidenceNoLock(hostEvidenceType); 823 } 824 825 EvidenceBase hostEvidence = WrapLegacyEvidence(hostEnumerator.Current); 826 AddHostEvidenceNoLock(hostEvidence, 827 GetEvidenceIndexType(hostEvidence), 828 DuplicateEvidenceAction.Merge); 829 } 830 831 // Add each piece of assembly evidence. We don't need to deserialize our copy of the 832 // evidence because AddAssemblyEvidenceNoLock will do this for us. 833 IEnumerator assemblyEnumerator = evidence.GetAssemblyEnumerator(); 834 while (assemblyEnumerator.MoveNext()) 835 { 836 EvidenceBase assemblyEvidence = WrapLegacyEvidence(assemblyEnumerator.Current); 837 AddAssemblyEvidenceNoLock(assemblyEvidence, 838 GetEvidenceIndexType(assemblyEvidence), 839 DuplicateEvidenceAction.Merge); 840 } 841 } 842 } 843 844 /// <summary> 845 /// Same as merge, except only one instance of any one evidence type is allowed. When duplicates 846 /// are found, the evidence in the input argument will have priority. Note this will force the 847 /// entire input evidence to be generated, and does not check for locked evidence 848 /// </summary> MergeWithNoDuplicates(Evidence evidence)849 internal void MergeWithNoDuplicates(Evidence evidence) 850 { 851 if (evidence == null) 852 { 853 return; 854 } 855 856 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 857 { 858 IEnumerator hostEnumerator = evidence.GetHostEnumerator(); 859 while (hostEnumerator.MoveNext()) 860 { 861 EvidenceBase hostEvidence = WrapLegacyEvidence(hostEnumerator.Current); 862 AddHostEvidenceNoLock(hostEvidence, 863 GetEvidenceIndexType(hostEvidence), 864 DuplicateEvidenceAction.SelectNewObject); 865 } 866 867 IEnumerator assemblyEnumerator = evidence.GetAssemblyEnumerator(); 868 while (assemblyEnumerator.MoveNext()) 869 { 870 EvidenceBase assemblyEvidence = WrapLegacyEvidence(assemblyEnumerator.Current); 871 AddAssemblyEvidenceNoLock(assemblyEvidence, 872 GetEvidenceIndexType(assemblyEvidence), 873 DuplicateEvidenceAction.SelectNewObject); 874 } 875 } 876 } 877 878 #if FEATURE_SERIALIZATION 879 /// <summary> 880 /// Do a full serialization of the evidence, which requires that we generate all of the evidence 881 /// we can and disconnect ourselves from the host and source assembly. 882 /// </summary> 883 [ComVisible(false)] 884 [OnSerializing] 885 [SecurityCritical] 886 [PermissionSet(SecurityAction.Assert, Unrestricted = true)] OnSerializing(StreamingContext context)887 private void OnSerializing(StreamingContext context) 888 { 889 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 890 { 891 // First, force all of the host evidence that might be lazily generated to be created 892 foreach (Type evidenceType in new List<Type>(m_evidence.Keys)) 893 { 894 GetHostEvidenceNoLock(evidenceType); 895 } 896 897 // Also ensure that all serialized assembly evidence has been created 898 DeserializeTargetEvidence(); 899 } 900 901 // Fill in legacy evidence lists. We can't guarantee thread-safety here using locks 902 // because we can't put a lock in the serialization code that will read the lists. 903 // The best we can do is prevent another thread from seeing a half-populated list. 904 // Therefore, we assign the lists after we've populated them fully (and declare them volatile.) 905 ArrayList hostList = new ArrayList(); 906 IEnumerator hostEnumerator = GetHostEnumerator(); 907 while (hostEnumerator.MoveNext()) 908 { 909 hostList.Add(hostEnumerator.Current); 910 } 911 m_hostList = hostList; 912 913 ArrayList assemblyList = new ArrayList(); 914 IEnumerator assemblyEnumerator = GetAssemblyEnumerator(); 915 while (assemblyEnumerator.MoveNext()) 916 { 917 assemblyList.Add(assemblyEnumerator.Current); 918 } 919 m_assemblyList = assemblyList; 920 } 921 922 /// <summary> 923 /// Finish deserializing legacy evidence 924 /// </summary> 925 [ComVisible(false)] 926 [OnDeserialized] 927 [SecurityCritical] OnDeserialized(StreamingContext context)928 private void OnDeserialized(StreamingContext context) 929 { 930 // Look at host and assembly evidence lists only if we serialized using Whidbey. 931 if (m_evidence == null) 932 { 933 m_evidence = new Dictionary<Type, EvidenceTypeDescriptor>(); 934 935 // Whidbey evidence may need to be wrapped or added to a LegacyEvidenceList, so we go 936 // through the legacy APIs to add them. 937 #pragma warning disable 618 938 if (m_hostList != null) 939 { 940 foreach (object evidenceObject in m_hostList) 941 { 942 if (evidenceObject != null) 943 { 944 AddHost(evidenceObject); 945 } 946 } 947 948 m_hostList = null; 949 } 950 951 if (m_assemblyList != null) 952 { 953 foreach (object evidenceObject in m_assemblyList) 954 { 955 if (evidenceObject != null) 956 { 957 AddAssembly(evidenceObject); 958 } 959 } 960 961 m_assemblyList = null; 962 } 963 #pragma warning restore 618 964 } 965 966 // see code:System.Security.Policy.Evidence#EvidenceLock 967 m_evidenceLock = new ReaderWriterLock(); 968 } 969 #endif // FEATURE_SERIALIZATION 970 971 /// <summary> 972 /// Load any serialized evidence out of the target assembly into our evidence collection. 973 /// 974 /// We allow entry to this method with only a reader lock held, since most of the time we will 975 /// not need to write to the evidence dictionary. If we haven't yet deserialized the target 976 /// evidence, then we will upgrade to a writer lock at that point. 977 /// </summary> DeserializeTargetEvidence()978 private void DeserializeTargetEvidence() 979 { 980 #if FEATURE_SERIALIZATION 981 Contract.Assert(IsReaderLockHeld || IsWriterLockHeld); 982 983 if (m_target != null && !m_deserializedTargetEvidence) 984 { 985 bool upgradedLock = false; 986 LockCookie lockCookie = new LockCookie(); 987 try 988 { 989 if (!IsWriterLockHeld) 990 { 991 lockCookie = UpgradeToWriterLock(); 992 upgradedLock = true; 993 } 994 995 // Set this to true here because AddAssemblyEvidenceNoLock will attempt to reenter this 996 // method creating possible infinite recursion. 997 m_deserializedTargetEvidence = true; 998 999 foreach (EvidenceBase targetEvidence in m_target.GetFactorySuppliedEvidence()) 1000 { 1001 AddAssemblyEvidenceNoLock(targetEvidence, GetEvidenceIndexType(targetEvidence), DuplicateEvidenceAction.Throw); 1002 } 1003 } 1004 finally 1005 { 1006 if (upgradedLock) 1007 DowngradeFromWriterLock(ref lockCookie); 1008 } 1009 } 1010 #endif // FEATURE_SERIALIZATION 1011 } 1012 1013 #if FEATURE_SERIALIZATION 1014 /// <summary> 1015 /// Serialize out raw evidence objects which have already been generated, ignoring any evidence 1016 /// which might be present but has not yet been created for this assembly. 1017 /// 1018 /// This is used for indexing into the security policy cache, since we know that once policy is 1019 /// resolved, the relevent membership conditions will have checked for any applicable evidence 1020 /// and therefore after poliyc resolution this evidence collection will contain any evidence 1021 /// objects necessary to arrive at its grant set. 1022 /// </summary> 1023 [SecurityCritical] RawSerialize()1024 internal byte[] RawSerialize() 1025 { 1026 try 1027 { 1028 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1029 { 1030 // Filter out any evidence which is not yet generated 1031 Dictionary<Type, EvidenceBase> generatedEvidence = new Dictionary<Type, EvidenceBase>(); 1032 foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in m_evidence) 1033 { 1034 if (evidenceType.Value != null && evidenceType.Value.HostEvidence != null) 1035 { 1036 generatedEvidence[evidenceType.Key] = evidenceType.Value.HostEvidence; 1037 } 1038 } 1039 1040 using (MemoryStream serializationStream = new MemoryStream()) 1041 { 1042 BinaryFormatter formatter = new BinaryFormatter(); 1043 formatter.Serialize(serializationStream, generatedEvidence); 1044 return serializationStream.ToArray(); 1045 } 1046 } 1047 } 1048 catch (SecurityException) 1049 { 1050 // We're running in a context where it's not safe to serialize the evidence out. In this case 1051 // Simply decline to cache the result of the policy evaluation 1052 return null; 1053 } 1054 } 1055 #endif // FEATURE_SERIALIZATION 1056 1057 // 1058 // ICollection implementation. All ICollection interface members are potentially much more 1059 // expensive in Arrowhead then they were downlevel. They should not be used if the standard Get and 1060 // Add methods will work instead. 1061 // 1062 1063 [Obsolete("Evidence should not be treated as an ICollection. Please use the GetHostEnumerator and GetAssemblyEnumerator methods rather than using CopyTo.")] CopyTo(Array array, int index)1064 public void CopyTo(Array array, int index) 1065 { 1066 if (array == null) 1067 throw new ArgumentNullException("array"); 1068 if (index < 0 || index > array.Length - Count) 1069 throw new ArgumentOutOfRangeException("index"); 1070 Contract.EndContractBlock(); 1071 1072 int currentIndex = index; 1073 1074 IEnumerator hostEnumerator = GetHostEnumerator(); 1075 while (hostEnumerator.MoveNext()) 1076 { 1077 array.SetValue(hostEnumerator.Current, currentIndex); 1078 ++currentIndex; 1079 } 1080 1081 IEnumerator assemblyEnumerator = GetAssemblyEnumerator(); 1082 while (assemblyEnumerator.MoveNext()) 1083 { 1084 array.SetValue(assemblyEnumerator.Current, currentIndex); 1085 ++currentIndex; 1086 } 1087 } 1088 GetHostEnumerator()1089 public IEnumerator GetHostEnumerator() 1090 { 1091 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1092 { 1093 return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Host); 1094 } 1095 } 1096 GetAssemblyEnumerator()1097 public IEnumerator GetAssemblyEnumerator() 1098 { 1099 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1100 { 1101 DeserializeTargetEvidence(); 1102 return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Assembly); 1103 } 1104 } 1105 1106 /// <summary> 1107 /// Get an enumerator that can iterate over the raw evidence objects stored for the assembly 1108 /// </summary> GetRawAssemblyEvidenceEnumerator()1109 internal RawEvidenceEnumerator GetRawAssemblyEvidenceEnumerator() 1110 { 1111 Contract.Assert(IsReaderLockHeld); 1112 DeserializeTargetEvidence(); 1113 return new RawEvidenceEnumerator(this, new List<Type>(m_evidence.Keys), false); 1114 } 1115 1116 /// <summary> 1117 /// Get an enumerator that can iterate over the raw evidence objects stored for the host 1118 /// </summary> 1119 /// <returns></returns> GetRawHostEvidenceEnumerator()1120 internal RawEvidenceEnumerator GetRawHostEvidenceEnumerator() 1121 { 1122 Contract.Assert(IsReaderLockHeld); 1123 return new RawEvidenceEnumerator(this, new List<Type>(m_evidence.Keys), true); 1124 } 1125 1126 [Obsolete("GetEnumerator is obsolete. Please use GetAssemblyEnumerator and GetHostEnumerator instead.")] GetEnumerator()1127 public IEnumerator GetEnumerator() 1128 { 1129 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1130 { 1131 return new EvidenceEnumerator(this, EvidenceEnumerator.Category.Host | EvidenceEnumerator.Category.Assembly); 1132 } 1133 } 1134 1135 /// <summary> 1136 /// Get a specific type of assembly supplied evidence 1137 /// </summary> 1138 [ComVisible(false)] 1139 public T GetAssemblyEvidence<T>() where T : EvidenceBase 1140 { 1141 return UnwrapEvidence(GetAssemblyEvidence(typeof(T))) as T; 1142 } 1143 GetAssemblyEvidence(Type type)1144 internal EvidenceBase GetAssemblyEvidence(Type type) 1145 { 1146 Contract.Assert(type != null); 1147 1148 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1149 { 1150 return GetAssemblyEvidenceNoLock(type); 1151 } 1152 } 1153 GetAssemblyEvidenceNoLock(Type type)1154 private EvidenceBase GetAssemblyEvidenceNoLock(Type type) 1155 { 1156 Contract.Assert(IsReaderLockHeld || IsWriterLockHeld); 1157 Contract.Assert(type != null); 1158 1159 DeserializeTargetEvidence(); 1160 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(type); 1161 if (descriptor != null) 1162 { 1163 return descriptor.AssemblyEvidence; 1164 } 1165 1166 return null; 1167 } 1168 1169 /// <summary> 1170 /// Get a specific type of host supplied evidence 1171 /// </summary> 1172 [ComVisible(false)] 1173 public T GetHostEvidence<T>() where T : EvidenceBase 1174 { 1175 return UnwrapEvidence(GetHostEvidence(typeof(T))) as T; 1176 } 1177 1178 /// <summary> 1179 /// Get a specific type of evidence from the host which may not have been verified yet. If the 1180 /// evidence was not verified, then don't mark it as being used yet. 1181 /// </summary> 1182 internal T GetDelayEvaluatedHostEvidence<T>() where T : EvidenceBase, IDelayEvaluatedEvidence 1183 { 1184 return UnwrapEvidence(GetHostEvidence(typeof(T), false)) as T; 1185 } 1186 GetHostEvidence(Type type)1187 internal EvidenceBase GetHostEvidence(Type type) 1188 { 1189 Contract.Assert(type != null); 1190 1191 return GetHostEvidence(type, true); 1192 } 1193 1194 [SecuritySafeCritical] GetHostEvidence(Type type, bool markDelayEvaluatedEvidenceUsed)1195 private EvidenceBase GetHostEvidence(Type type, bool markDelayEvaluatedEvidenceUsed) 1196 { 1197 Contract.Assert(type != null); 1198 1199 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1200 { 1201 EvidenceBase evidence = GetHostEvidenceNoLock(type); 1202 1203 if (markDelayEvaluatedEvidenceUsed) 1204 { 1205 IDelayEvaluatedEvidence delayEvidence = evidence as IDelayEvaluatedEvidence; 1206 if (delayEvidence != null) 1207 { 1208 delayEvidence.MarkUsed(); 1209 } 1210 } 1211 1212 return evidence; 1213 } 1214 } 1215 1216 /// <summary> 1217 /// Get host supplied evidence from the collection 1218 /// 1219 /// We attempt to find host evdience in the following order: 1220 /// 1221 /// 1. Already generated or explicitly supplied evidence 1222 /// 2. Evidence supplied by the CLR host 1223 /// 3. Evidence supplied by the CLR itself 1224 /// </summary> 1225 [SecurityCritical] GetHostEvidenceNoLock(Type type)1226 private EvidenceBase GetHostEvidenceNoLock(Type type) 1227 { 1228 Contract.Assert(IsReaderLockHeld || IsWriterLockHeld); 1229 Contract.Assert(type != null); 1230 1231 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(type); 1232 1233 // If the evidence descriptor doesn't exist for the host evidence type than the evidence doesn't 1234 // exist and neither the host nor the runtime can produce it. 1235 if (descriptor == null) 1236 { 1237 return null; 1238 } 1239 1240 // If the evidence has already been generated or if it was explicitly provided then return that 1241 if (descriptor.HostEvidence != null) 1242 { 1243 return descriptor.HostEvidence; 1244 } 1245 1246 // If we have a target, then the host or the runtime might be able to generate this type of 1247 // evidence on demand. 1248 if (m_target != null && !descriptor.Generated) 1249 { 1250 using (EvidenceUpgradeLockHolder lockHolder = new EvidenceUpgradeLockHolder(this)) 1251 { 1252 // Make sure that we don't attempt to generate this type of evidencea again if we fail to 1253 // generate it now. 1254 descriptor.Generated = true; 1255 1256 EvidenceBase generatedEvidence = GenerateHostEvidence(type, descriptor.HostCanGenerate); 1257 if (generatedEvidence != null) 1258 { 1259 descriptor.HostEvidence = generatedEvidence; 1260 1261 // 1262 // #BackpatchGeneratedEvidence 1263 // 1264 // If we were cloned from another evidence collection propigate any generated evidence 1265 // back to the original collection. Since Assembly and AppDomain both clone their 1266 // evidence before giving it to users, this prevents us from having to regenerate 1267 // evidence types on each clone that gets created. Note that we do not want to do this 1268 // backpatching if the origin already has evidence of this type or if it has had 1269 // this type of evidence removed from its collection. 1270 // 1271 1272 Evidence cloneOrigin = m_cloneOrigin != null ? m_cloneOrigin.Target as Evidence : null; 1273 if (cloneOrigin != null) 1274 { 1275 BCLDebug.Assert(cloneOrigin.Target != null && cloneOrigin.Target == Target, 1276 "Attempt to backpatch evidence to a collection with a different target."); 1277 1278 using (EvidenceLockHolder cloneLockHolder = new EvidenceLockHolder(cloneOrigin, EvidenceLockHolder.LockType.Writer)) 1279 { 1280 EvidenceTypeDescriptor cloneDescriptor = cloneOrigin.GetEvidenceTypeDescriptor(type); 1281 if (cloneDescriptor != null && cloneDescriptor.HostEvidence == null) 1282 { 1283 cloneDescriptor.HostEvidence = generatedEvidence.Clone() as EvidenceBase; 1284 } 1285 } 1286 } 1287 1288 } 1289 1290 return generatedEvidence; 1291 } 1292 } 1293 1294 // The evidence could not be generated and was not found 1295 return null; 1296 } 1297 1298 /// <summary> 1299 /// Attempt to generate host evidence on demand via calls to the runtime host or the evidence facotry 1300 /// </summary> 1301 [SecurityCritical] GenerateHostEvidence(Type type, bool hostCanGenerate)1302 private EvidenceBase GenerateHostEvidence(Type type, bool hostCanGenerate) 1303 { 1304 Contract.Assert(type != null); 1305 Contract.Assert(IsWriterLockHeld); 1306 1307 #if FEATURE_CAS_POLICY 1308 // First let the host generate the evidence if it can. 1309 if (hostCanGenerate) 1310 { 1311 AppDomain targetDomain = m_target.Target as AppDomain; 1312 Assembly targetAssembly = m_target.Target as Assembly; 1313 1314 EvidenceBase hostEvidence = null; 1315 if (targetDomain != null) 1316 { 1317 hostEvidence = AppDomain.CurrentDomain.HostSecurityManager.GenerateAppDomainEvidence(type); 1318 } 1319 else if (targetAssembly != null) 1320 { 1321 hostEvidence = AppDomain.CurrentDomain.HostSecurityManager.GenerateAssemblyEvidence(type, targetAssembly); 1322 } 1323 1324 // If the host generated the evidence, verify that it generated the evidence we expected 1325 // and use that. 1326 if (hostEvidence != null) 1327 { 1328 if (!type.IsAssignableFrom(hostEvidence.GetType())) 1329 { 1330 string hostType = AppDomain.CurrentDomain.HostSecurityManager.GetType().FullName; 1331 string recievedType = hostEvidence.GetType().FullName; 1332 string requestedType = type.FullName; 1333 1334 throw new InvalidOperationException(Environment.GetResourceString("Policy_IncorrectHostEvidence", hostType, recievedType, requestedType)); 1335 } 1336 1337 return hostEvidence; 1338 } 1339 } 1340 #endif // FEATURE_CAS_POLICY 1341 1342 // Finally, check to see if the CLR can generate the evidence 1343 return m_target.GenerateEvidence(type); 1344 } 1345 1346 [Obsolete("Evidence should not be treated as an ICollection. Please use GetHostEnumerator and GetAssemblyEnumerator to iterate over the evidence to collect a count.")] 1347 public int Count 1348 { 1349 get 1350 { 1351 int count = 0; 1352 1353 IEnumerator hostEvidence = GetHostEnumerator(); 1354 while (hostEvidence.MoveNext()) 1355 { 1356 ++count; 1357 } 1358 1359 IEnumerator assemblyEvidence = GetAssemblyEnumerator(); 1360 while (assemblyEvidence.MoveNext()) 1361 { 1362 ++count; 1363 } 1364 1365 return count; 1366 } 1367 } 1368 1369 /// <summary> 1370 /// Get the number of pieces of evidence which are currently generated, without causing any 1371 /// lazily generated evidence to be created. 1372 /// </summary> 1373 [ComVisible(false)] 1374 internal int RawCount 1375 { 1376 get 1377 { 1378 int count = 0; 1379 1380 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1381 { 1382 foreach (Type evidenceType in new List<Type>(m_evidence.Keys)) 1383 { 1384 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(evidenceType); 1385 1386 if (descriptor != null) 1387 { 1388 if (descriptor.AssemblyEvidence != null) 1389 { 1390 ++count; 1391 } 1392 if (descriptor.HostEvidence != null) 1393 { 1394 ++count; 1395 } 1396 } 1397 } 1398 } 1399 1400 return count; 1401 } 1402 } 1403 1404 public Object SyncRoot 1405 { 1406 get { return this; } 1407 } 1408 1409 public bool IsSynchronized 1410 { 1411 get { return true; } 1412 } 1413 1414 public bool IsReadOnly 1415 { 1416 get { return false; } 1417 } 1418 1419 #if FEATURE_CAS_POLICY 1420 [ComVisible(false)] Clone()1421 public Evidence Clone() 1422 { 1423 return new Evidence(this); 1424 } 1425 #endif // FEATURE_CAS_POLICY 1426 1427 [ComVisible(false)] 1428 [SecuritySafeCritical] Clear()1429 public void Clear() 1430 { 1431 if (Locked) 1432 { 1433 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 1434 } 1435 1436 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 1437 { 1438 ++m_version; 1439 m_evidence.Clear(); 1440 } 1441 } 1442 1443 [ComVisible(false)] 1444 [SecuritySafeCritical] RemoveType(Type t)1445 public void RemoveType(Type t) 1446 { 1447 if (t == null) 1448 throw new ArgumentNullException("t"); 1449 Contract.EndContractBlock(); 1450 1451 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Writer)) 1452 { 1453 EvidenceTypeDescriptor descriptor = GetEvidenceTypeDescriptor(t); 1454 if (descriptor != null) 1455 { 1456 ++m_version; 1457 1458 // If we've locked this evidence collection, we need to do the lock check in the case that 1459 // either we have host evidence, or that the host might generate it, since removing the 1460 // evidence will cause us to bypass the host's ability to ever generate the evidence. 1461 if (Locked && (descriptor.HostEvidence != null || descriptor.HostCanGenerate)) 1462 { 1463 new SecurityPermission(SecurityPermissionFlag.ControlEvidence).Demand(); 1464 } 1465 1466 m_evidence.Remove(t); 1467 } 1468 } 1469 } 1470 1471 /// <summary> 1472 /// Mark all of the already generated evidence in the collection as having been used during a 1473 /// policy evaluation. 1474 /// </summary> MarkAllEvidenceAsUsed()1475 internal void MarkAllEvidenceAsUsed() 1476 { 1477 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1478 { 1479 foreach (KeyValuePair<Type, EvidenceTypeDescriptor> evidenceType in m_evidence) 1480 { 1481 if (evidenceType.Value != null) 1482 { 1483 IDelayEvaluatedEvidence hostEvidence = evidenceType.Value.HostEvidence as IDelayEvaluatedEvidence; 1484 if (hostEvidence != null) 1485 { 1486 hostEvidence.MarkUsed(); 1487 } 1488 1489 IDelayEvaluatedEvidence assemblyEvidence = evidenceType.Value.AssemblyEvidence as IDelayEvaluatedEvidence; 1490 if (assemblyEvidence != null) 1491 { 1492 assemblyEvidence.MarkUsed(); 1493 } 1494 } 1495 } 1496 } 1497 } 1498 1499 #if FEATURE_CAS_POLICY 1500 /// <summary> 1501 /// Determine if delay evaluated strong name evidence is contained in this collection, and if so 1502 /// if it was used during policy evaluation. 1503 /// 1504 /// This method is called from the VM in SecurityPolicy::WasStrongNameEvidenceUsed 1505 /// This class should be used as an adapter layer to allow the public facing EvidenceEnumerator to 1506 /// be able to get the evidence values out of an Evidence class. It is tightly coupled with the 1507 /// internal data structures holding the evidence objects in the Evidence class. 1508 /// </summary> WasStrongNameEvidenceUsed()1509 private bool WasStrongNameEvidenceUsed() 1510 { 1511 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(this, EvidenceLockHolder.LockType.Reader)) 1512 { 1513 EvidenceTypeDescriptor snTypeDescriptor = GetEvidenceTypeDescriptor(typeof(StrongName)); 1514 if (snTypeDescriptor != null) 1515 { 1516 IDelayEvaluatedEvidence snEvidence = snTypeDescriptor.HostEvidence as IDelayEvaluatedEvidence; 1517 return snEvidence != null && snEvidence.WasUsed; 1518 } 1519 1520 return false; 1521 } 1522 } 1523 #endif // FEATURE_CAS_POLICY 1524 1525 /// <summary> 1526 /// Utility class to wrap acquiring a lock onto the evidence collection 1527 /// </summary> 1528 private class EvidenceLockHolder : IDisposable 1529 { 1530 private Evidence m_target; 1531 private LockType m_lockType; 1532 1533 public enum LockType 1534 { 1535 Reader, 1536 Writer 1537 } 1538 EvidenceLockHolder(Evidence target, LockType lockType)1539 public EvidenceLockHolder(Evidence target, LockType lockType) 1540 { 1541 Contract.Assert(target != null); 1542 Contract.Assert(lockType == LockType.Reader || lockType == LockType.Writer); 1543 1544 m_target = target; 1545 m_lockType = lockType; 1546 1547 if (m_lockType == LockType.Reader) 1548 { 1549 m_target.AcquireReaderLock(); 1550 } 1551 else 1552 { 1553 m_target.AcquireWriterlock(); 1554 } 1555 } 1556 Dispose()1557 public void Dispose() 1558 { 1559 if (m_lockType == LockType.Reader && m_target.IsReaderLockHeld) 1560 { 1561 m_target.ReleaseReaderLock(); 1562 } 1563 else if (m_lockType == LockType.Writer && m_target.IsWriterLockHeld) 1564 { 1565 m_target.ReleaseWriterLock(); 1566 } 1567 } 1568 } 1569 1570 /// <summary> 1571 /// Utility class to wrap upgrading an acquired reader lock to a writer lock and then 1572 /// downgrading it back to a reader lock. 1573 /// </summary> 1574 private class EvidenceUpgradeLockHolder : IDisposable 1575 { 1576 private Evidence m_target; 1577 private LockCookie m_cookie; 1578 EvidenceUpgradeLockHolder(Evidence target)1579 public EvidenceUpgradeLockHolder(Evidence target) 1580 { 1581 Contract.Assert(target != null); 1582 1583 m_target = target; 1584 m_cookie = m_target.UpgradeToWriterLock(); 1585 } 1586 Dispose()1587 public void Dispose() 1588 { 1589 if (m_target.IsWriterLockHeld) 1590 { 1591 m_target.DowngradeFromWriterLock(ref m_cookie); 1592 } 1593 } 1594 } 1595 1596 /// <summary> 1597 /// Enumerator that iterates directly over the evidence type map, returning back the evidence objects 1598 /// that are contained in it. This enumerator will generate any lazy evaluated evidence it finds, 1599 /// but it does not attempt to deal with legacy evidence adapters. 1600 /// 1601 /// This class should be used as an adapter layer to allow the public facing EvidenceEnumerator to 1602 /// be able to get the evidence values out of an Evidence class. It is tightly coupled with the 1603 /// internal data structures holding the evidence objects in the Evidence class. 1604 /// </summary> 1605 internal sealed class RawEvidenceEnumerator : IEnumerator<EvidenceBase> 1606 { 1607 private Evidence m_evidence; 1608 private bool m_hostEnumerator; // true to enumerate host evidence, false to enumerate assembly evidence 1609 private uint m_evidenceVersion; 1610 1611 private Type[] m_evidenceTypes; 1612 private int m_typeIndex; 1613 private EvidenceBase m_currentEvidence; 1614 1615 private static volatile List<Type> s_expensiveEvidence; 1616 RawEvidenceEnumerator(Evidence evidence, IEnumerable<Type> evidenceTypes, bool hostEnumerator)1617 public RawEvidenceEnumerator(Evidence evidence, IEnumerable<Type> evidenceTypes, bool hostEnumerator) 1618 { 1619 Contract.Assert(evidence != null); 1620 Contract.Assert(evidenceTypes != null); 1621 1622 m_evidence = evidence; 1623 m_hostEnumerator = hostEnumerator; 1624 m_evidenceTypes = GenerateEvidenceTypes(evidence, evidenceTypes, hostEnumerator); 1625 m_evidenceVersion = evidence.m_version; 1626 1627 Reset(); 1628 } 1629 1630 public EvidenceBase Current 1631 { 1632 get 1633 { 1634 if (m_evidence.m_version != m_evidenceVersion) 1635 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 1636 1637 return m_currentEvidence; 1638 } 1639 } 1640 1641 object IEnumerator.Current 1642 { 1643 get 1644 { 1645 if (m_evidence.m_version != m_evidenceVersion) 1646 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 1647 1648 return m_currentEvidence; 1649 } 1650 } 1651 1652 /// <summary> 1653 /// List of types of evidence that we would like to avoid generating if possible 1654 /// </summary> 1655 private static List<Type> ExpensiveEvidence 1656 { 1657 get 1658 { 1659 if (s_expensiveEvidence == null) 1660 { 1661 List<Type> expensiveEvidence = new List<Type>(); 1662 #if FEATURE_CAS_POLICY 1663 expensiveEvidence.Add(typeof(Hash)); 1664 expensiveEvidence.Add(typeof(Publisher)); 1665 #endif // FEATURE_CAS_POLICY 1666 s_expensiveEvidence = expensiveEvidence; 1667 1668 #if _DEBUG 1669 List<Type> runtimeTypes = new List<Type>(Evidence.RuntimeEvidenceTypes); 1670 foreach (Type expensiveType in s_expensiveEvidence) 1671 { 1672 BCLDebug.Assert(runtimeTypes.Contains(expensiveType), 1673 "Evidence type not generated by the runtime found in expensive evidence type list"); 1674 } 1675 #endif // _DEBUG 1676 } 1677 1678 return s_expensiveEvidence; 1679 } 1680 } 1681 Dispose()1682 public void Dispose() 1683 { 1684 return; 1685 } 1686 1687 /// <summary> 1688 /// Generate the array of types of evidence that could have values for 1689 /// </summary> GenerateEvidenceTypes(Evidence evidence, IEnumerable<Type> evidenceTypes, bool hostEvidence)1690 private static Type[] GenerateEvidenceTypes(Evidence evidence, 1691 IEnumerable<Type> evidenceTypes, 1692 bool hostEvidence) 1693 { 1694 Contract.Assert(evidence != null); 1695 Contract.Assert(evidenceTypes != null); 1696 1697 // 1698 // Sort the evidence being generated into three categories, which we enumerate in order: 1699 // 1. Evidence which has already been generated 1700 // 2. Evidence which is relatively inexpensive to generate 1701 // 3. Evidence which is expensive to generate. 1702 // 1703 // This allows us to be as efficient as possible in case the user of the enumerator stops the 1704 // enumeration before we step up to the next more expensive category. 1705 // 1706 1707 List<Type> alreadyGeneratedList = new List<Type>(); 1708 List<Type> inexpensiveList = new List<Type>(); 1709 List<Type> expensiveList = new List<Type>(ExpensiveEvidence.Count); 1710 1711 // Iterate over the evidence types classifying into the three groups. We need to copy the list 1712 // here since GetEvidenceTypeDescriptor will potentially update the evidence dictionary, which 1713 // evidenceTypes iterates over. 1714 foreach (Type evidenceType in evidenceTypes) 1715 { 1716 EvidenceTypeDescriptor descriptor = evidence.GetEvidenceTypeDescriptor(evidenceType); 1717 BCLDebug.Assert(descriptor != null, "descriptor != null"); 1718 1719 bool alreadyGenerated = (hostEvidence && descriptor.HostEvidence != null) || 1720 (!hostEvidence && descriptor.AssemblyEvidence != null); 1721 1722 if (alreadyGenerated) 1723 { 1724 alreadyGeneratedList.Add(evidenceType); 1725 } 1726 else if (ExpensiveEvidence.Contains(evidenceType)) 1727 { 1728 expensiveList.Add(evidenceType); 1729 } 1730 else 1731 { 1732 inexpensiveList.Add(evidenceType); 1733 } 1734 } 1735 1736 Type[] enumerationTypes = new Type[alreadyGeneratedList.Count + inexpensiveList.Count + expensiveList.Count]; 1737 alreadyGeneratedList.CopyTo(enumerationTypes, 0); 1738 inexpensiveList.CopyTo(enumerationTypes, alreadyGeneratedList.Count); 1739 expensiveList.CopyTo(enumerationTypes, alreadyGeneratedList.Count + inexpensiveList.Count); 1740 1741 return enumerationTypes; 1742 } 1743 1744 [SecuritySafeCritical] MoveNext()1745 public bool MoveNext() 1746 { 1747 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(m_evidence, EvidenceLockHolder.LockType.Reader)) 1748 { 1749 if (m_evidence.m_version != m_evidenceVersion) 1750 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 1751 1752 m_currentEvidence = null; 1753 1754 // Iterate over the possible types of evidence that we could have until we find one that 1755 // really exists, or we run out of posibilities. 1756 do 1757 { 1758 ++m_typeIndex; 1759 1760 if (m_typeIndex < m_evidenceTypes.Length) 1761 { 1762 if (m_hostEnumerator) 1763 { 1764 m_currentEvidence = m_evidence.GetHostEvidenceNoLock(m_evidenceTypes[m_typeIndex]); 1765 } 1766 else 1767 { 1768 m_currentEvidence = m_evidence.GetAssemblyEvidenceNoLock(m_evidenceTypes[m_typeIndex]); 1769 } 1770 } 1771 } 1772 while (m_typeIndex < m_evidenceTypes.Length && m_currentEvidence == null); 1773 } 1774 1775 return m_currentEvidence != null; 1776 } 1777 Reset()1778 public void Reset() 1779 { 1780 if (m_evidence.m_version != m_evidenceVersion) 1781 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); 1782 1783 m_typeIndex = -1; 1784 m_currentEvidence = null; 1785 } 1786 } 1787 1788 private sealed class EvidenceEnumerator : IEnumerator 1789 { 1790 private Evidence m_evidence; 1791 private Category m_category; 1792 private Stack m_enumerators; 1793 1794 private object m_currentEvidence; 1795 1796 [Flags] 1797 internal enum Category 1798 { 1799 Host = 0x1, // Enumerate only host supplied evidence 1800 Assembly = 0x2 // Enumerate only assembly supplied evidence 1801 } 1802 EvidenceEnumerator(Evidence evidence, Category category)1803 internal EvidenceEnumerator(Evidence evidence, Category category) 1804 { 1805 Contract.Assert(evidence != null); 1806 Contract.Assert(evidence.IsReaderLockHeld); 1807 1808 m_evidence = evidence; 1809 m_category = category; 1810 ResetNoLock(); 1811 } 1812 MoveNext()1813 public bool MoveNext() 1814 { 1815 IEnumerator currentEnumerator = CurrentEnumerator; 1816 1817 // No more enumerators means we can't go any further 1818 if (currentEnumerator == null) 1819 { 1820 m_currentEvidence = null; 1821 return false; 1822 } 1823 1824 // See if the current enumerator can continue 1825 if (currentEnumerator.MoveNext()) 1826 { 1827 // 1828 // If we've found an adapter for legacy evidence, we need to unwrap it for it to be the 1829 // current enumerator's value. For wrapped evidence, this is a simple unwrap, for a list of 1830 // evidence, we need to make that the current enumerator and get its first value. 1831 // 1832 1833 LegacyEvidenceWrapper legacyWrapper = currentEnumerator.Current as LegacyEvidenceWrapper; 1834 LegacyEvidenceList legacyList = currentEnumerator.Current as LegacyEvidenceList; 1835 1836 if (legacyWrapper != null) 1837 { 1838 m_currentEvidence = legacyWrapper.EvidenceObject; 1839 } 1840 else if (legacyList != null) 1841 { 1842 IEnumerator legacyListEnumerator = legacyList.GetEnumerator(); 1843 m_enumerators.Push(legacyListEnumerator); 1844 MoveNext(); 1845 } 1846 else 1847 { 1848 m_currentEvidence = currentEnumerator.Current; 1849 } 1850 1851 BCLDebug.Assert(m_currentEvidence != null, "m_currentEvidence != null"); 1852 return true; 1853 } 1854 else 1855 { 1856 // If we've reached the end of the current enumerator, move to the next one and try again 1857 m_enumerators.Pop(); 1858 return MoveNext(); 1859 } 1860 } 1861 1862 public object Current 1863 { 1864 get { return m_currentEvidence; } 1865 } 1866 1867 private IEnumerator CurrentEnumerator 1868 { 1869 get 1870 { 1871 return m_enumerators.Count > 0 ? m_enumerators.Peek() as IEnumerator : null; 1872 } 1873 } 1874 Reset()1875 public void Reset() 1876 { 1877 using (EvidenceLockHolder lockHolder = new EvidenceLockHolder(m_evidence, EvidenceLockHolder.LockType.Reader)) 1878 { 1879 ResetNoLock(); 1880 } 1881 } 1882 ResetNoLock()1883 private void ResetNoLock() 1884 { 1885 Contract.Assert(m_evidence != null); 1886 Contract.Assert(m_evidence.IsReaderLockHeld); 1887 1888 m_currentEvidence = null; 1889 m_enumerators = new Stack(); 1890 1891 if ((m_category & Category.Host) == Category.Host) 1892 { 1893 m_enumerators.Push(m_evidence.GetRawHostEvidenceEnumerator()); 1894 } 1895 if ((m_category & Category.Assembly) == Category.Assembly) 1896 { 1897 m_enumerators.Push(m_evidence.GetRawAssemblyEvidenceEnumerator()); 1898 } 1899 } 1900 } 1901 #endif //!FEATURE_CORECLR && FEATURE_RWLOCK 1902 } 1903 } 1904