1 //----------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 
5 namespace System.ServiceModel.Security
6 {
7     using System.IdentityModel.Tokens;
8     using System.Runtime;
9     using System.ServiceModel;
10     using System.ServiceModel.Diagnostics;
11     using System.Xml;
12     using ISignatureReaderProvider = System.IdentityModel.ISignatureReaderProvider;
13     using ISignatureValueSecurityElement = System.IdentityModel.ISignatureValueSecurityElement;
14     using SignedXml = System.IdentityModel.SignedXml;
15     using System.Collections.Generic;
16 
17     sealed class ReceiveSecurityHeaderElementManager : ISignatureReaderProvider
18     {
19         const int InitialCapacity = 8;
20         readonly ReceiveSecurityHeader securityHeader;
21         ReceiveSecurityHeaderEntry[] elements;
22         int count;
23         readonly string[] headerIds;
24         string[] predecryptionHeaderIds;
25         string bodyId;
26         string bodyContentId;
27         bool isPrimaryTokenSigned = false;
28 
ReceiveSecurityHeaderElementManager(ReceiveSecurityHeader securityHeader)29         public ReceiveSecurityHeaderElementManager(ReceiveSecurityHeader securityHeader)
30         {
31             this.securityHeader = securityHeader;
32             this.elements = new ReceiveSecurityHeaderEntry[InitialCapacity];
33             if (securityHeader.RequireMessageProtection)
34             {
35                 this.headerIds = new string[securityHeader.ProcessedMessage.Headers.Count];
36             }
37         }
38 
39         public int Count
40         {
41             get { return this.count; }
42         }
43 
44         public bool IsPrimaryTokenSigned
45         {
46             get { return this.isPrimaryTokenSigned; }
47             set { this.isPrimaryTokenSigned = value; }
48         }
49 
AppendElement( ReceiveSecurityHeaderElementCategory elementCategory, object element, ReceiveSecurityHeaderBindingModes bindingMode, string id, TokenTracker supportingTokenTracker)50         public void AppendElement(
51             ReceiveSecurityHeaderElementCategory elementCategory, object element,
52             ReceiveSecurityHeaderBindingModes bindingMode, string id, TokenTracker supportingTokenTracker)
53         {
54             if (id != null)
55             {
56                 VerifyIdUniquenessInSecurityHeader(id);
57             }
58             EnsureCapacityToAdd();
59             this.elements[this.count++].SetElement(elementCategory, element, bindingMode, id, false, null, supportingTokenTracker);
60         }
61 
AppendSignature(SignedXml signedXml)62         public void AppendSignature(SignedXml signedXml)
63         {
64             AppendElement(ReceiveSecurityHeaderElementCategory.Signature, signedXml,
65                 ReceiveSecurityHeaderBindingModes.Unknown, signedXml.Id, null);
66         }
67 
AppendReferenceList(ReferenceList referenceList)68         public void AppendReferenceList(ReferenceList referenceList)
69         {
70             AppendElement(ReceiveSecurityHeaderElementCategory.ReferenceList, referenceList,
71                 ReceiveSecurityHeaderBindingModes.Unknown, null, null);
72         }
73 
AppendEncryptedData(EncryptedData encryptedData)74         public void AppendEncryptedData(EncryptedData encryptedData)
75         {
76             AppendElement(ReceiveSecurityHeaderElementCategory.EncryptedData, encryptedData,
77                 ReceiveSecurityHeaderBindingModes.Unknown, encryptedData.Id, null);
78         }
79 
AppendSignatureConfirmation(ISignatureValueSecurityElement signatureConfirmationElement)80         public void AppendSignatureConfirmation(ISignatureValueSecurityElement signatureConfirmationElement)
81         {
82             AppendElement(ReceiveSecurityHeaderElementCategory.SignatureConfirmation, signatureConfirmationElement,
83                 ReceiveSecurityHeaderBindingModes.Unknown, signatureConfirmationElement.Id, null);
84         }
85 
AppendTimestamp(SecurityTimestamp timestamp)86         public void AppendTimestamp(SecurityTimestamp timestamp)
87         {
88             AppendElement(ReceiveSecurityHeaderElementCategory.Timestamp, timestamp,
89                 ReceiveSecurityHeaderBindingModes.Unknown, timestamp.Id, null);
90         }
91 
AppendSecurityTokenReference(SecurityKeyIdentifierClause strClause, string strId)92         public void AppendSecurityTokenReference(SecurityKeyIdentifierClause strClause, string strId)
93         {
94             if (!String.IsNullOrEmpty(strId))
95             {
96                 VerifyIdUniquenessInSecurityHeader(strId);
97                 AppendElement(ReceiveSecurityHeaderElementCategory.SecurityTokenReference, strClause, ReceiveSecurityHeaderBindingModes.Unknown, strId, null);
98             }
99         }
100 
AppendToken(SecurityToken token, ReceiveSecurityHeaderBindingModes mode, TokenTracker supportingTokenTracker)101         public void AppendToken(SecurityToken token, ReceiveSecurityHeaderBindingModes mode, TokenTracker supportingTokenTracker)
102         {
103             AppendElement(ReceiveSecurityHeaderElementCategory.Token, token,
104                 mode, token.Id, supportingTokenTracker);
105         }
106 
EnsureAllRequiredSecurityHeaderTargetsWereProtected()107         public void EnsureAllRequiredSecurityHeaderTargetsWereProtected()
108         {
109             Fx.Assert(this.securityHeader.RequireMessageProtection, "security header protection checks should only be done for message security");
110             ReceiveSecurityHeaderEntry entry;
111             for (int i = 0; i < this.count; i++)
112             {
113                 GetElementEntry(i, out entry);
114                 if (!entry.signed)
115                 {
116                     switch (entry.elementCategory)
117                     {
118                         case ReceiveSecurityHeaderElementCategory.Timestamp:
119                         case ReceiveSecurityHeaderElementCategory.SignatureConfirmation:
120                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
121                                 new MessageSecurityException(SR.GetString(SR.RequiredSecurityHeaderElementNotSigned, entry.elementCategory, entry.id)));
122                         case ReceiveSecurityHeaderElementCategory.Token:
123                             switch (entry.bindingMode)
124                             {
125                                 case ReceiveSecurityHeaderBindingModes.Signed:
126                                 case ReceiveSecurityHeaderBindingModes.SignedEndorsing:
127                                 case ReceiveSecurityHeaderBindingModes.Basic:
128                                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
129                                         new MessageSecurityException(SR.GetString(SR.RequiredSecurityTokenNotSigned, entry.element, entry.bindingMode)));
130                             }
131                             break;
132                     }
133                 }
134 
135                 if (!entry.encrypted)
136                 {
137                     if (entry.elementCategory == ReceiveSecurityHeaderElementCategory.Token &&
138                         entry.bindingMode == ReceiveSecurityHeaderBindingModes.Basic)
139                     {
140                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
141                             new MessageSecurityException(SR.GetString(SR.RequiredSecurityTokenNotEncrypted, entry.element, entry.bindingMode)));
142                     }
143                 }
144             }
145         }
146 
EnsureCapacityToAdd()147         void EnsureCapacityToAdd()
148         {
149             if (this.count == this.elements.Length)
150             {
151                 ReceiveSecurityHeaderEntry[] newElements = new ReceiveSecurityHeaderEntry[this.elements.Length * 2];
152                 Array.Copy(this.elements, 0, newElements, 0, this.count);
153                 this.elements = newElements;
154             }
155         }
156 
GetElement(int index)157         public object GetElement(int index)
158         {
159             Fx.Assert(0 <= index && index < this.count, "");
160             return this.elements[index].element;
161         }
162 
163         public T GetElement<T>(int index) where T : class
164         {
165             Fx.Assert(0 <= index && index < this.count, "");
166             return (T) this.elements[index].element;
167         }
168 
GetElementEntry(int index, out ReceiveSecurityHeaderEntry element)169         public void GetElementEntry(int index, out ReceiveSecurityHeaderEntry element)
170         {
171             Fx.Assert(0 <= index && index < this.count, "index out of range");
172             element = this.elements[index];
173         }
174 
GetElementCategory(int index)175         public ReceiveSecurityHeaderElementCategory GetElementCategory(int index)
176         {
177             Fx.Assert(0 <= index && index < this.count, "index out of range");
178             return this.elements[index].elementCategory;
179         }
180 
GetPrimarySignature(out XmlDictionaryReader reader, out string id)181         public void GetPrimarySignature(out XmlDictionaryReader reader, out string id)
182         {
183             ReceiveSecurityHeaderEntry entry;
184             for (int i = 0; i < this.count; i++)
185             {
186                 GetElementEntry(i, out entry);
187                 if (entry.elementCategory == ReceiveSecurityHeaderElementCategory.Signature &&
188                     entry.bindingMode == ReceiveSecurityHeaderBindingModes.Primary)
189                 {
190                     reader = GetReader(i, false);
191                     id = entry.id;
192                     return;
193                 }
194             }
195             reader = null;
196             id = null;
197             return;
198         }
199 
GetReader(int index, bool requiresEncryptedFormReader)200         internal XmlDictionaryReader GetReader(int index, bool requiresEncryptedFormReader)
201         {
202             Fx.Assert(0 <= index && index < this.count, "index out of range");
203             if (!requiresEncryptedFormReader)
204             {
205                 byte[] decryptedBuffer = this.elements[index].decryptedBuffer;
206                 if (decryptedBuffer != null)
207                 {
208                     return this.securityHeader.CreateDecryptedReader(decryptedBuffer);
209                 }
210             }
211             XmlDictionaryReader securityHeaderReader = this.securityHeader.CreateSecurityHeaderReader();
212             securityHeaderReader.ReadStartElement();
213             for (int i = 0; securityHeaderReader.IsStartElement() && i < index; i++)
214             {
215                 securityHeaderReader.Skip();
216             }
217             return securityHeaderReader;
218         }
219 
GetSignatureVerificationReader(string id, bool requiresEncryptedFormReaderIfDecrypted)220         public XmlDictionaryReader GetSignatureVerificationReader(string id, bool requiresEncryptedFormReaderIfDecrypted)
221         {
222             ReceiveSecurityHeaderEntry entry;
223             for (int i = 0; i < this.count; i++)
224             {
225                 GetElementEntry(i, out entry);
226                 bool encryptedForm = entry.encrypted && requiresEncryptedFormReaderIfDecrypted;
227                 bool isSignedToken = (entry.bindingMode == ReceiveSecurityHeaderBindingModes.Signed) || (entry.bindingMode == ReceiveSecurityHeaderBindingModes.SignedEndorsing);
228                 if (entry.MatchesId(id, encryptedForm))
229                 {
230                     SetSigned(i);
231                     if (!this.IsPrimaryTokenSigned)
232                     {
233                         this.IsPrimaryTokenSigned = entry.bindingMode == ReceiveSecurityHeaderBindingModes.Primary && entry.elementCategory == ReceiveSecurityHeaderElementCategory.Token;
234                     }
235                     return GetReader(i, encryptedForm);
236                 }
237                 else if (entry.MatchesId(id, isSignedToken))
238                 {
239                     SetSigned(i);
240                     if (!this.IsPrimaryTokenSigned)
241                     {
242                         this.IsPrimaryTokenSigned = entry.bindingMode == ReceiveSecurityHeaderBindingModes.Primary && entry.elementCategory == ReceiveSecurityHeaderElementCategory.Token;
243                     }
244                     return GetReader(i, isSignedToken);
245                 }
246             }
247             return null;
248         }
249 
OnDuplicateId(string id)250         void OnDuplicateId(string id)
251         {
252             throw TraceUtility.ThrowHelperError(
253                 new MessageSecurityException(SR.GetString(SR.DuplicateIdInMessageToBeVerified, id)), this.securityHeader.SecurityVerifiedMessage);
254         }
255 
SetBindingMode(int index, ReceiveSecurityHeaderBindingModes bindingMode)256         public void SetBindingMode(int index, ReceiveSecurityHeaderBindingModes bindingMode)
257         {
258             Fx.Assert(0 <= index && index < this.count, "index out of range");
259             this.elements[index].bindingMode = bindingMode;
260         }
261 
SetElement(int index, object element)262         public void SetElement(int index, object element)
263         {
264             Fx.Assert(0 <= index && index < this.count, "");
265             this.elements[index].element = element;
266         }
267 
ReplaceHeaderEntry(int index, ReceiveSecurityHeaderEntry element)268         public void ReplaceHeaderEntry(int index, ReceiveSecurityHeaderEntry element)
269         {
270             Fx.Assert(0 <= index && index < this.count, "");
271             this.elements[index] = element;
272         }
273 
SetElementAfterDecryption( int index, ReceiveSecurityHeaderElementCategory elementCategory, object element, ReceiveSecurityHeaderBindingModes bindingMode, string id, byte[] decryptedBuffer, TokenTracker supportingTokenTracker)274         public void SetElementAfterDecryption(
275             int index,
276             ReceiveSecurityHeaderElementCategory elementCategory, object element,
277             ReceiveSecurityHeaderBindingModes bindingMode, string id, byte[] decryptedBuffer, TokenTracker supportingTokenTracker)
278         {
279             Fx.Assert(0 <= index && index < this.count, "index out of range");
280             Fx.Assert(this.elements[index].elementCategory == ReceiveSecurityHeaderElementCategory.EncryptedData, "Replaced item must be EncryptedData");
281             if (id != null)
282             {
283                 VerifyIdUniquenessInSecurityHeader(id);
284             }
285             this.elements[index].PreserveIdBeforeDecryption();
286             this.elements[index].SetElement(elementCategory, element, bindingMode, id, true, decryptedBuffer, supportingTokenTracker);
287         }
288 
SetSignatureAfterDecryption(int index, SignedXml signedXml, byte[] decryptedBuffer)289         public void SetSignatureAfterDecryption(int index, SignedXml signedXml, byte[] decryptedBuffer)
290         {
291             SetElementAfterDecryption(index, ReceiveSecurityHeaderElementCategory.Signature,
292                                       signedXml, ReceiveSecurityHeaderBindingModes.Unknown, signedXml.Id, decryptedBuffer, null);
293         }
294 
SetSignatureConfirmationAfterDecryption(int index, ISignatureValueSecurityElement signatureConfirmationElement, byte[] decryptedBuffer)295         public void SetSignatureConfirmationAfterDecryption(int index, ISignatureValueSecurityElement signatureConfirmationElement, byte[] decryptedBuffer)
296         {
297             SetElementAfterDecryption(index, ReceiveSecurityHeaderElementCategory.SignatureConfirmation,
298                                       signatureConfirmationElement, ReceiveSecurityHeaderBindingModes.Unknown, signatureConfirmationElement.Id, decryptedBuffer, null);
299         }
300 
SetSigned(int index)301         internal void SetSigned(int index)
302         {
303             Fx.Assert(0 <= index && index < this.count, "");
304             this.elements[index].signed = true;
305             if (this.elements[index].supportingTokenTracker != null)
306             {
307                 this.elements[index].supportingTokenTracker.IsSigned = true;
308             }
309         }
310 
SetTimestampSigned(string id)311         public void SetTimestampSigned(string id)
312         {
313             for (int i = 0; i < this.count; i++)
314             {
315                 if (this.elements[i].elementCategory == ReceiveSecurityHeaderElementCategory.Timestamp &&
316                     this.elements[i].id == id)
317                 {
318                     SetSigned(i);
319                 }
320             }
321         }
322 
SetTokenAfterDecryption(int index, SecurityToken token, ReceiveSecurityHeaderBindingModes mode, byte[] decryptedBuffer, TokenTracker supportingTokenTracker)323         public void SetTokenAfterDecryption(int index, SecurityToken token, ReceiveSecurityHeaderBindingModes mode, byte[] decryptedBuffer, TokenTracker supportingTokenTracker)
324         {
325             SetElementAfterDecryption(index, ReceiveSecurityHeaderElementCategory.Token, token, mode, token.Id, decryptedBuffer, supportingTokenTracker);
326         }
327 
TryGetTokenElementIndexFromStrId(string strId, out int index)328         internal bool TryGetTokenElementIndexFromStrId(string strId, out int index)
329         {
330             index = -1;
331             SecurityKeyIdentifierClause strClause = null;
332             for (int position = 0; position < this.Count; position++)
333             {
334                 if (this.GetElementCategory(position) == ReceiveSecurityHeaderElementCategory.SecurityTokenReference)
335                 {
336                     strClause = this.GetElement(position) as SecurityKeyIdentifierClause;
337                     if (strClause.Id == strId)
338                         break;
339                 }
340             }
341 
342             if (strClause == null)
343                 return false;
344 
345             for (int position = 0; position < this.Count; position++)
346             {
347                 if (this.GetElementCategory(position) == ReceiveSecurityHeaderElementCategory.Token)
348                 {
349                     SecurityToken token = this.GetElement(position) as SecurityToken;
350                     if (token.MatchesKeyIdentifierClause(strClause))
351                     {
352                         index = position;
353                         return true;
354                     }
355                 }
356             }
357 
358             return false;
359         }
360 
VerifyUniquenessAndSetBodyId(string id)361         public void VerifyUniquenessAndSetBodyId(string id)
362         {
363             if (id != null)
364             {
365                 VerifyIdUniquenessInSecurityHeader(id);
366                 VerifyIdUniquenessInMessageHeadersAndBody(id, this.headerIds.Length);
367                 this.bodyId = id;
368             }
369         }
370 
VerifyUniquenessAndSetBodyContentId(string id)371         public void VerifyUniquenessAndSetBodyContentId(string id)
372         {
373             if (id != null)
374             {
375                 VerifyIdUniquenessInSecurityHeader(id);
376                 VerifyIdUniquenessInMessageHeadersAndBody(id, this.headerIds.Length);
377                 this.bodyContentId = id;
378             }
379         }
380 
VerifyUniquenessAndSetDecryptedHeaderId(string id, int headerIndex)381         public void VerifyUniquenessAndSetDecryptedHeaderId(string id, int headerIndex)
382         {
383             if (id != null)
384             {
385                 VerifyIdUniquenessInSecurityHeader(id);
386                 VerifyIdUniquenessInMessageHeadersAndBody(id, headerIndex);
387                 if (this.predecryptionHeaderIds == null)
388                 {
389                     this.predecryptionHeaderIds = new string[headerIds.Length];
390                 }
391                 this.predecryptionHeaderIds[headerIndex] = this.headerIds[headerIndex];
392                 this.headerIds[headerIndex] = id;
393             }
394         }
395 
VerifyUniquenessAndSetHeaderId(string id, int headerIndex)396         public void VerifyUniquenessAndSetHeaderId(string id, int headerIndex)
397         {
398             if (id != null)
399             {
400                 VerifyIdUniquenessInSecurityHeader(id);
401                 VerifyIdUniquenessInMessageHeadersAndBody(id, headerIndex);
402                 this.headerIds[headerIndex] = id;
403             }
404         }
405 
VerifyIdUniquenessInHeaderIdTable(string id, int headerCount, string[] headerIdTable)406         void VerifyIdUniquenessInHeaderIdTable(string id, int headerCount, string[] headerIdTable)
407         {
408             for (int i = 0; i < headerCount; i++)
409             {
410                 if (headerIdTable[i] == id)
411                 {
412                     OnDuplicateId(id);
413                 }
414             }
415         }
416 
VerifyIdUniquenessInSecurityHeader(string id)417         void VerifyIdUniquenessInSecurityHeader(string id)
418         {
419             Fx.Assert(id != null, "Uniqueness should only be tested for non-empty ids");
420             for (int i = 0; i < this.count; i++)
421             {
422                 if (this.elements[i].id == id || this.elements[i].encryptedFormId == id)
423                 {
424                     OnDuplicateId(id);
425                 }
426             }
427         }
428 
VerifyIdUniquenessInMessageHeadersAndBody(string id, int headerCount)429         void VerifyIdUniquenessInMessageHeadersAndBody(string id, int headerCount)
430         {
431             Fx.Assert(id != null, "Uniqueness should only be tested for non-empty ids");
432             VerifyIdUniquenessInHeaderIdTable(id, headerCount, this.headerIds);
433             if (this.predecryptionHeaderIds != null)
434             {
435                 VerifyIdUniquenessInHeaderIdTable(id, headerCount, this.predecryptionHeaderIds);
436             }
437             if (this.bodyId == id || this.bodyContentId == id)
438             {
439                 OnDuplicateId(id);
440             }
441         }
442 
ISignatureReaderProvider.GetReader(object callbackContext)443         XmlDictionaryReader ISignatureReaderProvider.GetReader(object callbackContext)
444         {
445             int index = (int)callbackContext;
446             Fx.Assert(index < this.Count, "Invalid Context provided.");
447             return GetReader(index, false);
448         }
449 
VerifySignatureConfirmationWasFound()450         public void VerifySignatureConfirmationWasFound()
451         {
452             ReceiveSecurityHeaderEntry entry;
453             for (int i = 0; i < this.count; i++)
454             {
455                 GetElementEntry(i, out entry);
456                 if (entry.elementCategory == ReceiveSecurityHeaderElementCategory.SignatureConfirmation)
457                 {
458                     return;
459                 }
460             }
461 
462             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.SignatureConfirmationWasExpected)));
463         }
464 
465     }
466 }
467