diff --git a/eng/SendToHelix.proj b/eng/SendToHelix.proj index a7aa7ca3a04..8aec9ec4119 100644 --- a/eng/SendToHelix.proj +++ b/eng/SendToHelix.proj @@ -25,7 +25,7 @@ - + diff --git a/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs b/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs index fa2121a2fa9..a1d35b58368 100644 --- a/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs +++ b/src/System.Private.ServiceModel/src/System/ServiceModel/Security/SecurityAppliedMessage.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime; using System.ServiceModel.Channels; +using System.Threading.Tasks; using System.Xml; using IPrefixGenerator = System.IdentityModel.IPrefixGenerator; @@ -138,6 +139,33 @@ protected override void OnWriteBodyContents(XmlDictionaryWriter writer) } } + protected override async Task OnWriteBodyContentsAsync(XmlDictionaryWriter writer) + { + switch (_state) + { + case BodyState.Created: + await InnerMessage.WriteBodyContentsAsync(writer); + return; + case BodyState.Signed: + case BodyState.EncryptedThenSigned: + XmlDictionaryReader reader = _fullBodyBuffer.GetReader(0); + reader.ReadStartElement(); + while (reader.NodeType != XmlNodeType.EndElement) + { + await writer.WriteNodeAsync(reader, false); + } + + reader.ReadEndElement(); + reader.Close(); + return; + case BodyState.Encrypted: + case BodyState.SignedThenEncrypted: + throw ExceptionHelper.PlatformNotSupported(); + default: + throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateBadStateException(nameof(OnWriteBodyContentsAsync))); + } + } + protected override void OnWriteMessage(XmlDictionaryWriter writer) { // For Kerb one shot, the channel binding will be need to be fished out of the message, cached and added to the @@ -197,6 +225,65 @@ protected override void OnWriteMessage(XmlDictionaryWriter writer) writer.WriteEndElement(); } + public override async Task OnWriteMessageAsync(XmlDictionaryWriter writer) + { + // For Kerb one shot, the channel binding will be need to be fished out of the message, cached and added to the + // token before calling ISC. + + AttachChannelBindingTokenIfFound(); + + EnsureUniqueSecurityApplication(); + + MessagePrefixGenerator prefixGenerator = new MessagePrefixGenerator(writer); + _securityHeader.StartSecurityApplication(); + + Headers.Add(_securityHeader); + + InnerMessage.WriteStartEnvelope(writer); + + Headers.RemoveAt(Headers.Count - 1); + + _securityHeader.ApplyBodySecurity(writer, prefixGenerator); + + InnerMessage.WriteStartHeaders(writer); + _securityHeader.ApplySecurityAndWriteHeaders(Headers, writer, prefixGenerator); + + _securityHeader.RemoveSignatureEncryptionIfAppropriate(); + + _securityHeader.CompleteSecurityApplication(); + _securityHeader.WriteHeader(writer, Version); + await writer.WriteEndElementAsync(); + + if (_fullBodyFragment != null) + { + ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_fullBodyFragment, 0, _fullBodyFragmentLength); + } + else + { + if (_startBodyFragment != null) + { + ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_startBodyFragment.GetBuffer(), 0, (int)_startBodyFragment.Length); + } + else + { + OnWriteStartBody(writer); + } + + await OnWriteBodyContentsAsync(writer); + + if (_endBodyFragment != null) + { + ((IFragmentCapableXmlDictionaryWriter)writer).WriteFragment(_endBodyFragment.GetBuffer(), 0, (int)_endBodyFragment.Length); + } + else + { + writer.WriteEndElement(); + } + } + + await writer.WriteEndElementAsync(); + } + private void AttachChannelBindingTokenIfFound() { // Only valid when using a TokenParameter of type KerberosSecurityTokenParameters which isn't currently supported diff --git a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs index 72a2654729a..3d497e65706 100644 --- a/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs +++ b/src/System.Private.ServiceModel/tests/Scenarios/Binding/WS/TransportWithMessageCredentialSecurity/BasicHttpTransportWithMessageCredentialSecurityTests.cs @@ -12,7 +12,7 @@ public class BasicHttpTransportWithMessageCredentialSecurityTests : ConditionalW nameof(Client_Certificate_Installed), nameof(SSL_Available))] [OuterLoop] - public static void Https_SecModeTransWithMessCred_CertClientCredential_Succeeds() + public static void BasicHttps_SecModeTransWithMessCred_CertClientCredential_Succeeds() { string clientCertThumb = null; EndpointAddress endpointAddress = null; @@ -54,6 +54,56 @@ public static void Https_SecModeTransWithMessCred_CertClientCredential_Succeeds( } } + [WcfTheory] + [Condition(nameof(Root_Certificate_Installed), + nameof(SSL_Available))] + [OuterLoop] + [InlineData(TransferMode.Buffered)] + [InlineData(TransferMode.Streamed)] + public static void BasicHttps_SecModeTransWithMessCred_UserNameClientCredential_Succeeds(TransferMode transferMode) + { + EndpointAddress endpointAddress = null; + string testString = "Hello"; + string username = null; + string password = null; + ChannelFactory factory = null; + IWcfService serviceProxy = null; + + try + { + // *** SETUP *** \\ + BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); + binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; + binding.TransferMode = transferMode; + endpointAddress = new EndpointAddress(new Uri(Endpoints.BasicHttps_SecModeTransWithMessCred_ClientCredTypeUserName + $"/{Enum.GetName(typeof(TransferMode), transferMode)}")); + + factory = new ChannelFactory(binding, endpointAddress); + username = Guid.NewGuid().ToString("n").Substring(0, 8); + char[] usernameArr = username.ToCharArray(); + Array.Reverse(usernameArr); + password = new string(usernameArr); + factory.Credentials.UserName.UserName = username; + factory.Credentials.UserName.Password = password; + + serviceProxy = factory.CreateChannel(); + + // *** EXECUTE *** \\ + string result = serviceProxy.Echo(testString); + + // *** VALIDATE *** \\ + Assert.Equal(testString, result); + + // *** CLEANUP *** \\ + ((ICommunicationObject)serviceProxy).Close(); + factory.Close(); + } + finally + { + // *** ENSURE CLEANUP *** \\ + ScenarioTestHelpers.CloseCommunicationObjects((ICommunicationObject)serviceProxy, factory); + } + } + [WcfFact] [Condition(nameof(Root_Certificate_Installed), nameof(SSL_Available))] diff --git a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs index 4dff329c664..f367b965530 100644 --- a/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs +++ b/src/System.Private.ServiceModel/tools/IISHostedWcfService/App_code/testhosts/BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.ServiceModel; using System.ServiceModel.Channels; @@ -11,13 +12,25 @@ namespace WcfService [TestServiceDefinition(Schema = ServiceSchema.HTTPS, BasePath = "BasicHttpsTransSecMessCredsUserName.svc")] internal class BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost : TestServiceHostBase { - protected override string Address { get { return "https-message-credentials-username"; } } + protected override IList GetBindings() + { + return new List { GetBufferedBinding(), GetStreamedBinding() }; + } - protected override Binding GetBinding() + protected Binding GetBufferedBinding() { BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; + binding.Name = "https-message-credentials-username/Buffered"; + return binding; + } + protected Binding GetStreamedBinding() + { + BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential); + binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName; + binding.TransferMode = TransferMode.Streamed; + binding.Name = "https-message-credentials-username/Streamed"; return binding; }