//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.Security { using System.Collections.Generic; using System.Collections.ObjectModel; using System.IdentityModel.Policy; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.IO; using System.Runtime; using System.Runtime.Serialization; using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security.Tokens; using System.Xml; using CanonicalizationDriver = System.IdentityModel.CanonicalizationDriver; using Psha1DerivedKeyGenerator = System.IdentityModel.Psha1DerivedKeyGenerator; abstract class SspiNegotiationTokenAuthenticator : NegotiationTokenAuthenticator { ExtendedProtectionPolicy extendedProtectionPolicy; string defaultServiceBinding; Object thisLock = new Object(); protected SspiNegotiationTokenAuthenticator() : base() { } public ExtendedProtectionPolicy ExtendedProtectionPolicy { get { return this.extendedProtectionPolicy; } set { this.extendedProtectionPolicy = value; } } protected Object ThisLock { get { return this.thisLock; } } public string DefaultServiceBinding { get { if (this.defaultServiceBinding == null) { lock (ThisLock) { if (this.defaultServiceBinding == null) { this.defaultServiceBinding = SecurityUtils.GetSpnFromIdentity( SecurityUtils.CreateWindowsIdentity(), new EndpointAddress(ListenUri)); } } } return this.defaultServiceBinding; } set { this.defaultServiceBinding = value; } } // abstract methods public abstract XmlDictionaryString NegotiationValueType { get; } protected abstract ReadOnlyCollection ValidateSspiNegotiation(ISspiNegotiation sspiNegotiation); protected abstract SspiNegotiationTokenAuthenticatorState CreateSspiState(byte[] incomingBlob, string incomingValueTypeUri); // helpers protected virtual void IssueServiceToken(SspiNegotiationTokenAuthenticatorState sspiState, ReadOnlyCollection authorizationPolicies, out SecurityContextSecurityToken serviceToken, out WrappedKeySecurityToken proofToken, out int issuedKeySize) { UniqueId contextId = SecurityUtils.GenerateUniqueId(); string id = SecurityUtils.GenerateId(); if (sspiState.RequestedKeySize == 0) { issuedKeySize = this.SecurityAlgorithmSuite.DefaultSymmetricKeyLength; } else { issuedKeySize = sspiState.RequestedKeySize; } byte[] key = new byte[issuedKeySize / 8]; CryptoHelper.FillRandomBytes(key); DateTime effectiveTime = DateTime.UtcNow; DateTime expirationTime = TimeoutHelper.Add(effectiveTime, this.ServiceTokenLifetime); serviceToken = IssueSecurityContextToken(contextId, id, key, effectiveTime, expirationTime, authorizationPolicies, this.EncryptStateInServiceToken); proofToken = new WrappedKeySecurityToken(string.Empty, key, sspiState.SspiNegotiation); } protected virtual void ValidateIncomingBinaryNegotiation(BinaryNegotiation incomingNego) { if (incomingNego == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToReceive))); } incomingNego.Validate(this.NegotiationValueType); } protected virtual BinaryNegotiation GetOutgoingBinaryNegotiation(ISspiNegotiation sspiNegotiation, byte[] outgoingBlob) { return new BinaryNegotiation(this.NegotiationValueType, outgoingBlob); } static void AddToDigest(HashAlgorithm negotiationDigest, Stream stream) { stream.Flush(); stream.Seek(0, SeekOrigin.Begin); CanonicalizationDriver canonicalizer = new CanonicalizationDriver(); canonicalizer.SetInput(stream); byte[] canonicalizedData = canonicalizer.GetBytes(); lock (negotiationDigest) { negotiationDigest.TransformBlock(canonicalizedData, 0, canonicalizedData.Length, canonicalizedData, 0); } } static void AddToDigest(SspiNegotiationTokenAuthenticatorState sspiState, RequestSecurityToken rst) { MemoryStream stream = new MemoryStream(); XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream); rst.RequestSecurityTokenXml.WriteTo(writer); writer.Flush(); AddToDigest(sspiState.NegotiationDigest, stream); } static void AddToDigest(SspiNegotiationTokenAuthenticatorState sspiState, RequestSecurityTokenResponse rstr, bool wasReceived) { MemoryStream stream = new MemoryStream(); XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream); if (wasReceived) { rstr.RequestSecurityTokenResponseXml.WriteTo(writer); } else { rstr.WriteTo(writer); } writer.Flush(); AddToDigest(sspiState.NegotiationDigest, stream); } static byte[] ComputeAuthenticator(SspiNegotiationTokenAuthenticatorState sspiState, byte[] key) { byte[] negotiationHash; lock (sspiState.NegotiationDigest) { sspiState.NegotiationDigest.TransformFinalBlock(CryptoHelper.EmptyBuffer, 0, 0); negotiationHash = sspiState.NegotiationDigest.Hash; } Psha1DerivedKeyGenerator generator = new Psha1DerivedKeyGenerator(key); return generator.GenerateDerivedKey(SecurityUtils.CombinedHashLabel, negotiationHash, SecurityNegotiationConstants.NegotiationAuthenticatorSize, 0); } // overrides protected override bool IsMultiLegNegotiation { get { return true; } } protected override Binding GetNegotiationBinding(Binding binding) { return binding; } protected override MessageFilter GetListenerFilter() { return new SspiNegotiationFilter(this); } protected override BodyWriter ProcessRequestSecurityToken(Message request, RequestSecurityToken requestSecurityToken, out SspiNegotiationTokenAuthenticatorState negotiationState) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (requestSecurityToken == null) { throw TraceUtility.ThrowHelperArgumentNull("requestSecurityToken", request); } if (requestSecurityToken.RequestType != null && requestSecurityToken.RequestType != this.StandardsManager.TrustDriver.RequestTypeIssue) { throw TraceUtility.ThrowHelperWarning(new SecurityNegotiationException(SR.GetString(SR.InvalidRstRequestType, requestSecurityToken.RequestType)), request); } BinaryNegotiation incomingNego = requestSecurityToken.GetBinaryNegotiation(); ValidateIncomingBinaryNegotiation(incomingNego); negotiationState = CreateSspiState(incomingNego.GetNegotiationData(), incomingNego.ValueTypeUri); AddToDigest(negotiationState, requestSecurityToken); negotiationState.Context = requestSecurityToken.Context; if (requestSecurityToken.KeySize != 0) { WSTrust.Driver.ValidateRequestedKeySize(requestSecurityToken.KeySize, this.SecurityAlgorithmSuite); } negotiationState.RequestedKeySize = requestSecurityToken.KeySize; string appliesToName; string appliesToNamespace; requestSecurityToken.GetAppliesToQName(out appliesToName, out appliesToNamespace); if (appliesToName == AddressingStrings.EndpointReference && appliesToNamespace == request.Version.Addressing.Namespace) { DataContractSerializer serializer; if (request.Version.Addressing == AddressingVersion.WSAddressing10) { serializer = DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddress10), DataContractSerializerDefaults.MaxItemsInObjectGraph); negotiationState.AppliesTo = requestSecurityToken.GetAppliesTo(serializer).ToEndpointAddress(); } else if (request.Version.Addressing == AddressingVersion.WSAddressingAugust2004) { serializer = DataContractSerializerDefaults.CreateSerializer(typeof(EndpointAddressAugust2004), DataContractSerializerDefaults.MaxItemsInObjectGraph); negotiationState.AppliesTo = requestSecurityToken.GetAppliesTo(serializer).ToEndpointAddress(); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, request.Version.Addressing))); } negotiationState.AppliesToSerializer = serializer; } return ProcessNegotiation(negotiationState, request, incomingNego); } protected override BodyWriter ProcessRequestSecurityTokenResponse(SspiNegotiationTokenAuthenticatorState negotiationState, Message request, RequestSecurityTokenResponse requestSecurityTokenResponse) { if (request == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("request"); } if (requestSecurityTokenResponse == null) { throw TraceUtility.ThrowHelperArgumentNull("requestSecurityTokenResponse", request); } if (requestSecurityTokenResponse.Context != negotiationState.Context) { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.BadSecurityNegotiationContext)), request); } AddToDigest(negotiationState, requestSecurityTokenResponse, true); BinaryNegotiation incomingNego = requestSecurityTokenResponse.GetBinaryNegotiation(); ValidateIncomingBinaryNegotiation(incomingNego); return ProcessNegotiation(negotiationState, request, incomingNego); } BodyWriter ProcessNegotiation(SspiNegotiationTokenAuthenticatorState negotiationState, Message incomingMessage, BinaryNegotiation incomingNego) { ISspiNegotiation sspiNegotiation = negotiationState.SspiNegotiation; byte[] outgoingBlob = sspiNegotiation.GetOutgoingBlob(incomingNego.GetNegotiationData(), SecurityUtils.GetChannelBindingFromMessage(incomingMessage), this.extendedProtectionPolicy); if (sspiNegotiation.IsValidContext == false) { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.InvalidSspiNegotiation)), incomingMessage); } // if there is no blob to send back the nego must be complete from the server side if (outgoingBlob == null && sspiNegotiation.IsCompleted == false) { throw TraceUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.NoBinaryNegoToSend)), incomingMessage); } BinaryNegotiation outgoingBinaryNegotiation; if (outgoingBlob != null) { outgoingBinaryNegotiation = GetOutgoingBinaryNegotiation(sspiNegotiation, outgoingBlob); } else { outgoingBinaryNegotiation = null; } BodyWriter replyBody; if (sspiNegotiation.IsCompleted) { ReadOnlyCollection authorizationPolicies = ValidateSspiNegotiation(sspiNegotiation); SecurityContextSecurityToken serviceToken; WrappedKeySecurityToken proofToken; int issuedKeySize; IssueServiceToken(negotiationState, authorizationPolicies, out serviceToken, out proofToken, out issuedKeySize); negotiationState.SetServiceToken(serviceToken); SecurityKeyIdentifierClause externalTokenReference = this.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(serviceToken, SecurityTokenReferenceStyle.External); SecurityKeyIdentifierClause internalTokenReference = this.IssuedSecurityTokenParameters.CreateKeyIdentifierClause(serviceToken, SecurityTokenReferenceStyle.Internal); RequestSecurityTokenResponse dummyRstr = new RequestSecurityTokenResponse(this.StandardsManager); dummyRstr.Context = negotiationState.Context; dummyRstr.KeySize = issuedKeySize; dummyRstr.TokenType = this.SecurityContextTokenUri; if (outgoingBinaryNegotiation != null) { dummyRstr.SetBinaryNegotiation(outgoingBinaryNegotiation); } dummyRstr.RequestedUnattachedReference = externalTokenReference; dummyRstr.RequestedAttachedReference = internalTokenReference; dummyRstr.SetLifetime(serviceToken.ValidFrom, serviceToken.ValidTo); if (negotiationState.AppliesTo != null) { if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressing10) { dummyRstr.SetAppliesTo(EndpointAddress10.FromEndpointAddress( negotiationState.AppliesTo), negotiationState.AppliesToSerializer); } else if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004) { dummyRstr.SetAppliesTo(EndpointAddressAugust2004.FromEndpointAddress( negotiationState.AppliesTo), negotiationState.AppliesToSerializer); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, incomingMessage.Version.Addressing))); } } dummyRstr.MakeReadOnly(); AddToDigest(negotiationState, dummyRstr, false); RequestSecurityTokenResponse negotiationRstr = new RequestSecurityTokenResponse(this.StandardsManager); negotiationRstr.RequestedSecurityToken = serviceToken; negotiationRstr.RequestedProofToken = proofToken; negotiationRstr.Context = negotiationState.Context; negotiationRstr.KeySize = issuedKeySize; negotiationRstr.TokenType = this.SecurityContextTokenUri; if (outgoingBinaryNegotiation != null) { negotiationRstr.SetBinaryNegotiation(outgoingBinaryNegotiation); } negotiationRstr.RequestedAttachedReference = internalTokenReference; negotiationRstr.RequestedUnattachedReference = externalTokenReference; if (negotiationState.AppliesTo != null) { if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressing10) { negotiationRstr.SetAppliesTo( EndpointAddress10.FromEndpointAddress(negotiationState.AppliesTo), negotiationState.AppliesToSerializer); } else if (incomingMessage.Version.Addressing == AddressingVersion.WSAddressingAugust2004) { negotiationRstr.SetAppliesTo( EndpointAddressAugust2004.FromEndpointAddress(negotiationState.AppliesTo), negotiationState.AppliesToSerializer); } else { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, incomingMessage.Version.Addressing))); } } negotiationRstr.MakeReadOnly(); byte[] authenticator = ComputeAuthenticator(negotiationState, serviceToken.GetKeyBytes()); RequestSecurityTokenResponse authenticatorRstr = new RequestSecurityTokenResponse(this.StandardsManager); authenticatorRstr.Context = negotiationState.Context; authenticatorRstr.SetAuthenticator(authenticator); authenticatorRstr.MakeReadOnly(); List rstrList = new List(2); rstrList.Add(negotiationRstr); rstrList.Add(authenticatorRstr); replyBody = new RequestSecurityTokenResponseCollection(rstrList, this.StandardsManager); } else { RequestSecurityTokenResponse rstr = new RequestSecurityTokenResponse(this.StandardsManager); rstr.Context = negotiationState.Context; rstr.SetBinaryNegotiation(outgoingBinaryNegotiation); rstr.MakeReadOnly(); AddToDigest(negotiationState, rstr, false); replyBody = rstr; } return replyBody; } class SspiNegotiationFilter : HeaderFilter { SspiNegotiationTokenAuthenticator authenticator; public SspiNegotiationFilter(SspiNegotiationTokenAuthenticator authenticator) { this.authenticator = authenticator; } public override bool Match(Message message) { if (message.Headers.Action == authenticator.RequestSecurityTokenAction.Value || message.Headers.Action == authenticator.RequestSecurityTokenResponseAction.Value) { return !SecurityVersion.Default.DoesMessageContainSecurityHeader(message); } else { return false; } } } } }