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