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