Skip to content

Commit

Permalink
Fix TransportWithMessageCredentials with TransferMode.Streamed missin…
Browse files Browse the repository at this point in the history
…g security header (#4873)

* Update helix queue name for internal builds

* Fix missing security headers when using streamed mode with TransportWithMessageCredentials
  • Loading branch information
mconnew authored Aug 15, 2022
1 parent a197a75 commit ecd0022
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<IWcfService> 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<IWcfService>(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))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -11,13 +12,25 @@ namespace WcfService
[TestServiceDefinition(Schema = ServiceSchema.HTTPS, BasePath = "BasicHttpsTransSecMessCredsUserName.svc")]
internal class BasicHttpsTransportSecurityMessageCredentialsUserNameTestServiceHost : TestServiceHostBase<IWcfService>
{
protected override string Address { get { return "https-message-credentials-username"; } }
protected override IList<Binding> GetBindings()
{
return new List<Binding> { 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;
}

Expand Down

0 comments on commit ecd0022

Please sign in to comment.