SecurityContextTokenCache.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Security
  5. {
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Collections.ObjectModel;
  10. using System.Runtime;
  11. using System.ServiceModel.Diagnostics;
  12. using System.ServiceModel.Security.Tokens;
  13. using System.Xml;
  14. // This is the in-memory cache used for caching SCTs
  15. sealed class SecurityContextTokenCache : TimeBoundedCache
  16. {
  17. // if there are less than lowWaterMark entries, no purging is done
  18. static int lowWaterMark = 50;
  19. // frequency of purging the cache of stale entries
  20. // this is set to 10 mins as SCTs are expected to have long lifetimes
  21. static TimeSpan purgingInterval = TimeSpan.FromMinutes(10);
  22. static double pruningFactor = 0.20;
  23. bool replaceOldestEntries = true;
  24. static SctEffectiveTimeComparer sctEffectiveTimeComparer = new SctEffectiveTimeComparer();
  25. TimeSpan clockSkew;
  26. public SecurityContextTokenCache( int capacity, bool replaceOldestEntries )
  27. : this( capacity, replaceOldestEntries, SecurityProtocolFactory.defaultMaxClockSkew )
  28. {
  29. }
  30. public SecurityContextTokenCache(int capacity, bool replaceOldestEntries, TimeSpan clockSkew)
  31. : base(lowWaterMark, capacity, null, PurgingMode.TimerBasedPurge, purgingInterval, true)
  32. {
  33. this.replaceOldestEntries = replaceOldestEntries;
  34. this.clockSkew = clockSkew;
  35. }
  36. public void AddContext(SecurityContextSecurityToken token)
  37. {
  38. TryAddContext(token, true);
  39. }
  40. public bool TryAddContext(SecurityContextSecurityToken token)
  41. {
  42. return TryAddContext(token, false);
  43. }
  44. bool TryAddContext(SecurityContextSecurityToken token, bool throwOnFailure)
  45. {
  46. if (token == null)
  47. {
  48. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
  49. }
  50. if ( !SecurityUtils.IsCurrentlyTimeEffective( token.ValidFrom, token.ValidTo, this.clockSkew ) )
  51. {
  52. if (token.KeyGeneration == null)
  53. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SecurityContextExpiredNoKeyGeneration, token.ContextId));
  54. else
  55. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SecurityContextExpired, token.ContextId, token.KeyGeneration.ToString()));
  56. }
  57. if ( !SecurityUtils.IsCurrentlyTimeEffective( token.KeyEffectiveTime, token.KeyExpirationTime, this.clockSkew ) )
  58. {
  59. if (token.KeyGeneration == null)
  60. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SecurityContextKeyExpiredNoKeyGeneration, token.ContextId));
  61. else
  62. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(SR.GetString(SR.SecurityContextKeyExpired, token.ContextId, token.KeyGeneration.ToString()));
  63. }
  64. object hashKey = GetHashKey(token.ContextId, token.KeyGeneration);
  65. bool wasTokenAdded = base.TryAddItem(hashKey, (SecurityContextSecurityToken)token.Clone(), false);
  66. if (!wasTokenAdded)
  67. {
  68. if (throwOnFailure)
  69. {
  70. if (token.KeyGeneration == null)
  71. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ContextAlreadyRegisteredNoKeyGeneration, token.ContextId)));
  72. else
  73. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ContextAlreadyRegistered, token.ContextId, token.KeyGeneration.ToString())));
  74. }
  75. }
  76. return wasTokenAdded;
  77. }
  78. object GetHashKey(UniqueId contextId, UniqueId generation)
  79. {
  80. if (generation == null)
  81. {
  82. return contextId;
  83. }
  84. else
  85. {
  86. return new ContextAndGenerationKey(contextId, generation);
  87. }
  88. }
  89. public void ClearContexts()
  90. {
  91. base.ClearItems();
  92. }
  93. public SecurityContextSecurityToken GetContext(UniqueId contextId, UniqueId generation)
  94. {
  95. if (contextId == null)
  96. {
  97. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contextId");
  98. }
  99. object hashKey = GetHashKey(contextId, generation);
  100. SecurityContextSecurityToken sct = (SecurityContextSecurityToken)base.GetItem(hashKey);
  101. return sct != null ? (SecurityContextSecurityToken)sct.Clone() : null;
  102. }
  103. public void RemoveContext(UniqueId contextId, UniqueId generation, bool throwIfNotPresent)
  104. {
  105. if (contextId == null)
  106. {
  107. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contextId");
  108. }
  109. object hashKey = GetHashKey(contextId, generation);
  110. if (!base.TryRemoveItem(hashKey) && throwIfNotPresent)
  111. {
  112. if (generation == null)
  113. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ContextNotPresentNoKeyGeneration, contextId)));
  114. else
  115. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ContextNotPresent, contextId, generation.ToString())));
  116. }
  117. }
  118. ArrayList GetMatchingKeys(UniqueId contextId)
  119. {
  120. if (contextId == null)
  121. {
  122. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contextId");
  123. }
  124. ArrayList matchingKeys = new ArrayList(2);
  125. bool lockHeld = false;
  126. try
  127. {
  128. try { }
  129. finally
  130. {
  131. base.CacheLock.AcquireReaderLock(-1);
  132. lockHeld = true;
  133. }
  134. foreach (object key in this.Entries.Keys)
  135. {
  136. bool isMatch = false;
  137. if (key is UniqueId)
  138. {
  139. isMatch = (((UniqueId)key) == contextId);
  140. }
  141. else
  142. {
  143. isMatch = (((ContextAndGenerationKey)key).ContextId == contextId);
  144. }
  145. if (isMatch)
  146. {
  147. matchingKeys.Add(key);
  148. }
  149. }
  150. }
  151. finally
  152. {
  153. if (lockHeld)
  154. {
  155. base.CacheLock.ReleaseReaderLock();
  156. }
  157. }
  158. return matchingKeys;
  159. }
  160. public void RemoveAllContexts(UniqueId contextId)
  161. {
  162. ArrayList matchingKeys = GetMatchingKeys(contextId);
  163. for (int i = 0; i < matchingKeys.Count; ++i)
  164. {
  165. base.TryRemoveItem(matchingKeys[i]);
  166. }
  167. }
  168. public void UpdateContextCachingTime(SecurityContextSecurityToken token, DateTime expirationTime)
  169. {
  170. if (token.ValidTo <= expirationTime.ToUniversalTime())
  171. {
  172. return;
  173. }
  174. base.TryReplaceItem(GetHashKey(token.ContextId, token.KeyGeneration), token, expirationTime);
  175. }
  176. public Collection<SecurityContextSecurityToken> GetAllContexts(UniqueId contextId)
  177. {
  178. ArrayList matchingKeys = GetMatchingKeys(contextId);
  179. Collection<SecurityContextSecurityToken> matchingContexts = new Collection<SecurityContextSecurityToken>();
  180. for (int i = 0; i < matchingKeys.Count; ++i)
  181. {
  182. SecurityContextSecurityToken token = base.GetItem(matchingKeys[i]) as SecurityContextSecurityToken;
  183. if (token != null)
  184. {
  185. matchingContexts.Add(token);
  186. }
  187. }
  188. return matchingContexts;
  189. }
  190. protected override ArrayList OnQuotaReached(Hashtable cacheTable)
  191. {
  192. if (!this.replaceOldestEntries)
  193. {
  194. SecurityTraceRecordHelper.TraceSecurityContextTokenCacheFull(this.Capacity, 0);
  195. return base.OnQuotaReached(cacheTable);
  196. }
  197. else
  198. {
  199. List<SecurityContextSecurityToken> tokens = new List<SecurityContextSecurityToken>(cacheTable.Count);
  200. foreach (IExpirableItem value in cacheTable.Values)
  201. {
  202. SecurityContextSecurityToken token = (SecurityContextSecurityToken)ExtractItem(value);
  203. tokens.Add(token);
  204. }
  205. tokens.Sort(sctEffectiveTimeComparer);
  206. int pruningAmount = (int)(((double)this.Capacity) * pruningFactor);
  207. pruningAmount = pruningAmount <= 0 ? this.Capacity : pruningAmount;
  208. ArrayList keys = new ArrayList(pruningAmount);
  209. for (int i = 0; i < pruningAmount; ++i)
  210. {
  211. keys.Add(GetHashKey(tokens[i].ContextId, tokens[i].KeyGeneration));
  212. OnRemove(tokens[i]);
  213. }
  214. SecurityTraceRecordHelper.TraceSecurityContextTokenCacheFull(this.Capacity, pruningAmount);
  215. return keys;
  216. }
  217. }
  218. sealed class SctEffectiveTimeComparer : IComparer<SecurityContextSecurityToken>
  219. {
  220. public int Compare(SecurityContextSecurityToken sct1, SecurityContextSecurityToken sct2)
  221. {
  222. if (sct1 == sct2)
  223. {
  224. return 0;
  225. }
  226. if (sct1.ValidFrom.ToUniversalTime() < sct2.ValidFrom.ToUniversalTime())
  227. {
  228. return -1;
  229. }
  230. else if (sct1.ValidFrom.ToUniversalTime() > sct2.ValidFrom.ToUniversalTime())
  231. {
  232. return 1;
  233. }
  234. else
  235. {
  236. // compare the key effective times
  237. if (sct1.KeyEffectiveTime.ToUniversalTime() < sct2.KeyEffectiveTime.ToUniversalTime())
  238. {
  239. return -1;
  240. }
  241. else if (sct1.KeyEffectiveTime.ToUniversalTime() > sct2.KeyEffectiveTime.ToUniversalTime())
  242. {
  243. return 1;
  244. }
  245. else
  246. {
  247. return 0;
  248. }
  249. }
  250. }
  251. }
  252. protected override void OnRemove(object item)
  253. {
  254. ((IDisposable)item).Dispose();
  255. base.OnRemove(item);
  256. }
  257. struct ContextAndGenerationKey
  258. {
  259. UniqueId contextId;
  260. UniqueId generation;
  261. public ContextAndGenerationKey(UniqueId contextId, UniqueId generation)
  262. {
  263. Fx.Assert(contextId != null && generation != null, "");
  264. this.contextId = contextId;
  265. this.generation = generation;
  266. }
  267. public UniqueId ContextId
  268. {
  269. get
  270. {
  271. return this.contextId;
  272. }
  273. }
  274. public UniqueId Generation
  275. {
  276. get
  277. {
  278. return this.generation;
  279. }
  280. }
  281. public override int GetHashCode()
  282. {
  283. return this.contextId.GetHashCode() ^ this.generation.GetHashCode();
  284. }
  285. public override bool Equals(object obj)
  286. {
  287. if (obj is ContextAndGenerationKey)
  288. {
  289. ContextAndGenerationKey key2 = ((ContextAndGenerationKey)obj);
  290. return (key2.ContextId == this.contextId && key2.Generation == this.generation);
  291. }
  292. else
  293. {
  294. return false;
  295. }
  296. }
  297. public static bool operator ==(ContextAndGenerationKey a, ContextAndGenerationKey b)
  298. {
  299. if (object.ReferenceEquals(a, null))
  300. {
  301. return object.ReferenceEquals(b, null);
  302. }
  303. return (a.Equals(b));
  304. }
  305. public static bool operator !=(ContextAndGenerationKey a, ContextAndGenerationKey b)
  306. {
  307. return !(a == b);
  308. }
  309. }
  310. }
  311. }