SecurityContextCookieSerializer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.ServiceModel.Security.Tokens
  5. {
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.IdentityModel.Claims;
  9. using System.IdentityModel.Policy;
  10. using System.IO;
  11. using System.Runtime;
  12. using System.Runtime.Serialization;
  13. using System.Security.Principal;
  14. using System.ServiceModel;
  15. using System.ServiceModel.Dispatcher;
  16. using System.Xml;
  17. struct SecurityContextCookieSerializer
  18. {
  19. const int SupportedPersistanceVersion = 1;
  20. SecurityStateEncoder securityStateEncoder;
  21. IList<Type> knownTypes;
  22. public SecurityContextCookieSerializer(SecurityStateEncoder securityStateEncoder, IList<Type> knownTypes)
  23. {
  24. if (securityStateEncoder == null)
  25. {
  26. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("securityStateEncoder");
  27. }
  28. this.securityStateEncoder = securityStateEncoder;
  29. this.knownTypes = knownTypes ?? new List<Type>();
  30. }
  31. SecurityContextSecurityToken DeserializeContext(byte[] serializedContext, byte[] cookieBlob, string id, XmlDictionaryReaderQuotas quotas)
  32. {
  33. SctClaimDictionary dictionary = SctClaimDictionary.Instance;
  34. XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(serializedContext, 0, serializedContext.Length, dictionary, quotas, null, null);
  35. int cookieVersion = -1;
  36. UniqueId cookieContextId = null;
  37. DateTime effectiveTime = SecurityUtils.MinUtcDateTime;
  38. DateTime expiryTime = SecurityUtils.MaxUtcDateTime;
  39. byte[] key = null;
  40. string localId = null;
  41. UniqueId keyGeneration = null;
  42. DateTime keyEffectiveTime = SecurityUtils.MinUtcDateTime;
  43. DateTime keyExpirationTime = SecurityUtils.MaxUtcDateTime;
  44. List<ClaimSet> claimSets = null;
  45. IList<IIdentity> identities = null;
  46. bool isCookie = true;
  47. reader.ReadFullStartElement(dictionary.SecurityContextSecurityToken, dictionary.EmptyString);
  48. while (reader.IsStartElement())
  49. {
  50. if (reader.IsStartElement(dictionary.Version, dictionary.EmptyString))
  51. {
  52. cookieVersion = reader.ReadElementContentAsInt();
  53. }
  54. else if (reader.IsStartElement(dictionary.ContextId, dictionary.EmptyString))
  55. {
  56. cookieContextId = reader.ReadElementContentAsUniqueId();
  57. }
  58. else if (reader.IsStartElement(dictionary.Id, dictionary.EmptyString))
  59. {
  60. localId = reader.ReadElementContentAsString();
  61. }
  62. else if (reader.IsStartElement(dictionary.EffectiveTime, dictionary.EmptyString))
  63. {
  64. effectiveTime = new DateTime(XmlHelper.ReadElementContentAsInt64(reader), DateTimeKind.Utc);
  65. }
  66. else if (reader.IsStartElement(dictionary.ExpiryTime, dictionary.EmptyString))
  67. {
  68. expiryTime = new DateTime(XmlHelper.ReadElementContentAsInt64(reader), DateTimeKind.Utc);
  69. }
  70. else if (reader.IsStartElement(dictionary.Key, dictionary.EmptyString))
  71. {
  72. key = reader.ReadElementContentAsBase64();
  73. }
  74. else if (reader.IsStartElement(dictionary.KeyGeneration, dictionary.EmptyString))
  75. {
  76. keyGeneration = reader.ReadElementContentAsUniqueId();
  77. }
  78. else if (reader.IsStartElement(dictionary.KeyEffectiveTime, dictionary.EmptyString))
  79. {
  80. keyEffectiveTime = new DateTime(XmlHelper.ReadElementContentAsInt64(reader), DateTimeKind.Utc);
  81. }
  82. else if (reader.IsStartElement(dictionary.KeyExpiryTime, dictionary.EmptyString))
  83. {
  84. keyExpirationTime = new DateTime(XmlHelper.ReadElementContentAsInt64(reader), DateTimeKind.Utc);
  85. }
  86. else if (reader.IsStartElement(dictionary.Identities, dictionary.EmptyString))
  87. {
  88. identities = SctClaimSerializer.DeserializeIdentities(reader, dictionary, DataContractSerializerDefaults.CreateSerializer(typeof(IIdentity), this.knownTypes, int.MaxValue));
  89. }
  90. else if (reader.IsStartElement(dictionary.ClaimSets, dictionary.EmptyString))
  91. {
  92. reader.ReadStartElement();
  93. DataContractSerializer claimSetSerializer = DataContractSerializerDefaults.CreateSerializer(typeof(ClaimSet), this.knownTypes, int.MaxValue);
  94. DataContractSerializer claimSerializer = DataContractSerializerDefaults.CreateSerializer(typeof(Claim), this.knownTypes, int.MaxValue);
  95. claimSets = new List<ClaimSet>(1);
  96. while (reader.IsStartElement())
  97. {
  98. claimSets.Add(SctClaimSerializer.DeserializeClaimSet(reader, dictionary, claimSetSerializer, claimSerializer));
  99. }
  100. reader.ReadEndElement();
  101. }
  102. else if (reader.IsStartElement(dictionary.IsCookieMode, dictionary.EmptyString))
  103. {
  104. isCookie = reader.ReadElementString() == "1" ? true : false;
  105. }
  106. else
  107. {
  108. OnInvalidCookieFailure(SR.GetString(SR.SctCookieXmlParseError));
  109. }
  110. }
  111. reader.ReadEndElement();
  112. if (cookieVersion != SupportedPersistanceVersion)
  113. {
  114. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SerializedTokenVersionUnsupported, cookieVersion)));
  115. }
  116. if (cookieContextId == null)
  117. {
  118. OnInvalidCookieFailure(SR.GetString(SR.SctCookieValueMissingOrIncorrect, "ContextId"));
  119. }
  120. if (key == null || key.Length == 0)
  121. {
  122. OnInvalidCookieFailure(SR.GetString(SR.SctCookieValueMissingOrIncorrect, "Key"));
  123. }
  124. if (localId != id)
  125. {
  126. OnInvalidCookieFailure(SR.GetString(SR.SctCookieValueMissingOrIncorrect, "Id"));
  127. }
  128. List<IAuthorizationPolicy> authorizationPolicies;
  129. if (claimSets != null)
  130. {
  131. authorizationPolicies = new List<IAuthorizationPolicy>(1);
  132. authorizationPolicies.Add(new SctUnconditionalPolicy(identities, claimSets, expiryTime));
  133. }
  134. else
  135. {
  136. authorizationPolicies = null;
  137. }
  138. return new SecurityContextSecurityToken(cookieContextId, localId, key, effectiveTime, expiryTime,
  139. authorizationPolicies != null ? authorizationPolicies.AsReadOnly() : null, isCookie, cookieBlob, keyGeneration, keyEffectiveTime, keyExpirationTime);
  140. }
  141. public byte[] CreateCookieFromSecurityContext(UniqueId contextId, string id, byte[] key, DateTime tokenEffectiveTime,
  142. DateTime tokenExpirationTime, UniqueId keyGeneration, DateTime keyEffectiveTime, DateTime keyExpirationTime,
  143. ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
  144. {
  145. if (contextId == null)
  146. {
  147. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("contextId");
  148. }
  149. if (key == null)
  150. {
  151. throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("key");
  152. }
  153. MemoryStream stream = new MemoryStream();
  154. XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream, SctClaimDictionary.Instance, null);
  155. SctClaimDictionary dictionary = SctClaimDictionary.Instance;
  156. writer.WriteStartElement(dictionary.SecurityContextSecurityToken, dictionary.EmptyString);
  157. writer.WriteStartElement(dictionary.Version, dictionary.EmptyString);
  158. writer.WriteValue(SupportedPersistanceVersion);
  159. writer.WriteEndElement();
  160. if (id != null)
  161. writer.WriteElementString(dictionary.Id, dictionary.EmptyString, id);
  162. XmlHelper.WriteElementStringAsUniqueId(writer, dictionary.ContextId, dictionary.EmptyString, contextId);
  163. writer.WriteStartElement(dictionary.Key, dictionary.EmptyString);
  164. writer.WriteBase64(key, 0, key.Length);
  165. writer.WriteEndElement();
  166. if (keyGeneration != null)
  167. {
  168. XmlHelper.WriteElementStringAsUniqueId(writer, dictionary.KeyGeneration, dictionary.EmptyString, keyGeneration);
  169. }
  170. XmlHelper.WriteElementContentAsInt64(writer, dictionary.EffectiveTime, dictionary.EmptyString, tokenEffectiveTime.ToUniversalTime().Ticks);
  171. XmlHelper.WriteElementContentAsInt64(writer, dictionary.ExpiryTime, dictionary.EmptyString, tokenExpirationTime.ToUniversalTime().Ticks);
  172. XmlHelper.WriteElementContentAsInt64(writer, dictionary.KeyEffectiveTime, dictionary.EmptyString, keyEffectiveTime.ToUniversalTime().Ticks);
  173. XmlHelper.WriteElementContentAsInt64(writer, dictionary.KeyExpiryTime, dictionary.EmptyString, keyExpirationTime.ToUniversalTime().Ticks);
  174. AuthorizationContext authContext = null;
  175. if (authorizationPolicies != null)
  176. authContext = AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies);
  177. if (authContext != null && authContext.ClaimSets.Count != 0)
  178. {
  179. DataContractSerializer identitySerializer = DataContractSerializerDefaults.CreateSerializer(typeof(IIdentity), this.knownTypes, int.MaxValue);
  180. DataContractSerializer claimSetSerializer = DataContractSerializerDefaults.CreateSerializer(typeof(ClaimSet), this.knownTypes, int.MaxValue);
  181. DataContractSerializer claimSerializer = DataContractSerializerDefaults.CreateSerializer(typeof(Claim), this.knownTypes, int.MaxValue);
  182. SctClaimSerializer.SerializeIdentities(authContext, dictionary, writer, identitySerializer);
  183. writer.WriteStartElement(dictionary.ClaimSets, dictionary.EmptyString);
  184. for (int i = 0; i < authContext.ClaimSets.Count; i++)
  185. {
  186. SctClaimSerializer.SerializeClaimSet(authContext.ClaimSets[i], dictionary, writer, claimSetSerializer, claimSerializer);
  187. }
  188. writer.WriteEndElement();
  189. }
  190. writer.WriteEndElement();
  191. writer.Flush();
  192. byte[] serializedContext = stream.ToArray();
  193. return this.securityStateEncoder.EncodeSecurityState(serializedContext);
  194. }
  195. public SecurityContextSecurityToken CreateSecurityContextFromCookie(byte[] encodedCookie, UniqueId contextId, UniqueId generation, string id, XmlDictionaryReaderQuotas quotas)
  196. {
  197. byte[] cookie = null;
  198. try
  199. {
  200. cookie = this.securityStateEncoder.DecodeSecurityState(encodedCookie);
  201. }
  202. catch (Exception e)
  203. {
  204. if (Fx.IsFatal(e))
  205. {
  206. throw;
  207. }
  208. OnInvalidCookieFailure(SR.GetString(SR.SctCookieBlobDecodeFailure), e);
  209. }
  210. SecurityContextSecurityToken sct = DeserializeContext(cookie, encodedCookie, id, quotas);
  211. if (sct.ContextId != contextId)
  212. {
  213. OnInvalidCookieFailure(SR.GetString(SR.SctCookieValueMissingOrIncorrect, "ContextId"));
  214. }
  215. if (sct.KeyGeneration != generation)
  216. {
  217. OnInvalidCookieFailure(SR.GetString(SR.SctCookieValueMissingOrIncorrect, "KeyGeneration"));
  218. }
  219. return sct;
  220. }
  221. internal static void OnInvalidCookieFailure(string reason)
  222. {
  223. OnInvalidCookieFailure(reason, null);
  224. }
  225. internal static void OnInvalidCookieFailure(string reason, Exception e)
  226. {
  227. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.InvalidSecurityContextCookie, reason), e));
  228. }
  229. class SctUnconditionalPolicy : IAuthorizationPolicy
  230. {
  231. SecurityUniqueId id = SecurityUniqueId.Create();
  232. IList<IIdentity> identities;
  233. IList<ClaimSet> claimSets;
  234. DateTime expirationTime;
  235. public SctUnconditionalPolicy(IList<IIdentity> identities, IList<ClaimSet> claimSets, DateTime expirationTime)
  236. {
  237. this.identities = identities;
  238. this.claimSets = claimSets;
  239. this.expirationTime = expirationTime;
  240. }
  241. public string Id
  242. {
  243. get { return this.id.Value; }
  244. }
  245. public ClaimSet Issuer
  246. {
  247. get { return ClaimSet.System; }
  248. }
  249. public bool Evaluate(EvaluationContext evaluationContext, ref object state)
  250. {
  251. for (int i = 0; i < this.claimSets.Count; ++i)
  252. {
  253. evaluationContext.AddClaimSet(this, this.claimSets[i]);
  254. }
  255. if (this.identities != null)
  256. {
  257. object obj;
  258. if (!evaluationContext.Properties.TryGetValue(SecurityUtils.Identities, out obj))
  259. {
  260. evaluationContext.Properties.Add(SecurityUtils.Identities, this.identities);
  261. }
  262. else
  263. {
  264. // null if other overrides the property with something else
  265. List<IIdentity> dstIdentities = obj as List<IIdentity>;
  266. if (dstIdentities != null)
  267. {
  268. dstIdentities.AddRange(this.identities);
  269. }
  270. }
  271. }
  272. evaluationContext.RecordExpirationTime(this.expirationTime);
  273. return true;
  274. }
  275. }
  276. }
  277. }