Use a Service Account to execute an operation

Topics: Usage Scenarios, Technical Questions
Feb 13, 2009 at 5:49 PM
In our current design we have some internal WCF services using WsHttp bindings which in turn need a valid Windows Account to be executed.  I am wanting the MSE to handle all the security aspect of things and based on your current documentation it looks like that may not be to difficult.  But after the authentication and authorization happen within the MSE I want for now to use a service account to execute the underlying service.  With your example I should be able to validate a certificate and then create the header information to get a user name through to the broker but after that happens I want to use a service account to execute the operation.  So for a proof-of-concept I created an ImpersonationBehavior that will set the ClientCredentials for the backend calls using delegation.  Here is the code that I have: 

// This is from implementing IEndpointBehavior 
// and this is the only method I implement right now 
public
void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
    // remove the endpoint behavior client credentials and specify new ones.  
    endpoint.Behaviors.Remove<ClientCredentials>();
    ClientCredentials clientCredentials = new ClientCredentials();
    clientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.
TokenImpersonationLevel.Delegation;
    clientCredentials.Windows.AllowNtlm =
false;
    // _credentials is set through the BehaviorExtensonElement that I created
    clientCredentials.Windows.ClientCredential =
new System.Net.NetworkCredential(_credentials.UserName, _credentials.Password, _credentials.Domain);
    endpoint.Behaviors.Add(clientCredentials);
}

I then set the policy in my channel that is used for my operation to this policy I created.  I have this set up on a distributed MSE with the Messenger and Broker seperated if that makes any difference but I think in this case it probably doesn't.  I setup a debug point in the code and attached to the MSE and was able to step into it so I know it is getting set but I still get the error below.  Is there a step I am missing in the implemenation of IEndpointBehavior that is needed in order for this to work?  Thanks in advance...

-Corey

Event Type: Error
Event Source: MSE Runtime

Description:
Communication Error in BeginRequest: SOAP security negotiation with '<MyServiceUrl>' for target '<MyServiceUrl>' failed. See inner exception for more details.stack trace:
Server stack trace:
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityUtils.OpenCommunicationObject(ICommunicationObject obj, TimeSpan timeout)
   at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
   at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Open()
   at Microsoft.MSE.Runtime.Services.Broker.BrokerServiceInstance.BeginRequest(Message message, AsyncCallback cb, Object state)

Developer
Feb 17, 2009 at 12:08 AM
If you look at the WCF docs, it says that modifying the ServiceEndpoint parameter in an IEndpointBehavior implementation results in undefined behavior.  This reference is provided for examination only.
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.iendpointbehavior.aspx

If you want to use a service account to do the invocation of the backend service.  I'd suggest changing the MSE Runtime Server windows service to run under a domain account that has the proper privileges to access the service implementation.  This would follow a trusted subsystem model where the MSE does user AuthN & AuthZ while using a single service account to perform all operations against the service implementation which sounds like what you are trying to do.



Feb 18, 2009 at 10:49 PM
This is exactly what I am trying to do.  Thanks for the info.  I am having issues though because of the seperation of the messenger and broker when I change the account it runs under the messenger is no longer able to open up a link to the broker.  The exact error is: Error Creating MSE Endpoint: http:<endpoint> Unable to open broker communication channel.  I would like to have the messenger runtime service stick with running under the local system account while the broker runtime service to run under the Domain account if possible.  Right now if I change the account these two run under to anything other then Local System it doesn't work.  Is there other settings somewhere that I need to alter to allow this communication to work?  I am using a NetTcp binding in between the two servers for the broker connection as well if that is relevant.  Thanks again for the help...

 

Developer
Feb 19, 2009 at 5:23 PM
What binding are you using for communicating between the Messenger and Broker.  Using either WsHttp (Soap12) or NetTcp I'm able to communicate between the two ok even when they are running under different accounts.
Feb 19, 2009 at 5:36 PM
Edited Feb 19, 2009 at 6:20 PM
The broker is setup to use NetTcp.   So you were able to have an account on the broker runtime that was different than the messenger runtime?  I will have to see what may be different then.  I keep getting errors from the messenger box that says it can't connect to the Broker.
Developer
Feb 19, 2009 at 5:45 PM
if you create a c:\temp\logs folder on your computer and restart the serivices you'll be able to inspect the WCF service trace log (using the Service Trace Viewer in the SDK) to see what is happening.  Could possibly be an endpoint identity problem, if so that may be difficult to work around since we don't really expose much control over the broker service proxy we create to call the Broker endpoint.
Feb 19, 2009 at 6:46 PM
I was actually looking at the trace log to see if I can find anything.  I am not seeing any exceptions on the messenger but on the broker I am.  On the broker side this is one of the errors:

<dir>

<

 

 

E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">

 

<

 

 

 

System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

EventID>131075</EventID>

 

<

 

 

 

Type>3</Type>

 

<

 

 

 

SubType Name="Error">0</SubType>

 

<

 

 

 

Level>2</Level>

 

<

 

 

 

TimeCreated SystemTime="2009-02-19T18:38:13.5290425Z" />

 

<

 

 

 

Source Name="System.ServiceModel" />

 

<

 

 

 

Correlation ActivityID="{2615fc05-07bf-4d52-9306-353b9e2a15d3}" />

 

<

 

 

 

Execution ProcessName="Microsoft.MSE.Runtime.ServiceHost" ProcessID="3000" ThreadID="5" />

 

<

 

 

 

Channel />

 

<

 

 

 

Computer><brokerserverurl></Computer>

 

<dir></dir>

</

 

 

 

</dir>

System>

 

<

 

 

 

ApplicationData>

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

TraceData>

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

DataItem>

 

<

 

 

 

TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.Diagnostics.ThrowingException.aspx</TraceIdentifier>

 

<

 

 

 

Description>Throwing an exception.</Description>

 

<

 

 

 

AppDomain>Microsoft.MSE.Runtime.ServiceHost.exe</AppDomain>

 

<

 

 

 

Exception>

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

ExceptionType>System.ServiceModel.Security.SecurityNegotiationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

 

<

 

 

 

Message>Authentication failed on the remote side (the stream might still be available for additional authentication attempts).</Message>

 

<

 

 

 

StackTrace>

 

 

at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(Stream stream, SecurityMessageProperty&amp; remoteSecurity)

at System.ServiceModel.Channels.StreamSecurityUpgradeAcceptorBase.AcceptUpgrade(Stream stream)

at System.ServiceModel.Channels.InitialServerConnectionReader.UpgradeConnection(IConnection connection, StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts)

at System.ServiceModel.Channels.ServerSessionPreambleConnectionReader.ServerFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)

at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

at System.ServiceModel.Dispatcher.ChannelHandler.OpenAndEnsurePump()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()

at System.Security.SecurityContext.Run(SecurityContext securityContext, ContextCallback callback, Object state)

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)

at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)

at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

</

 

 

StackTrace>

 

<

 

 

 

ExceptionString>System.ServiceModel.Security.SecurityNegotiationException: Authentication failed on the remote side (the stream might still be available for additional authentication attempts). ---&gt; System.Security.Authentication.AuthenticationException: Authentication failed on the remote side (the stream might still be available for additional authentication attempts). ---&gt; System.ComponentModel.Win32Exception: The target principal name is incorrect

 

--- End of inner exception stack trace ---

at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)

at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(Stream stream, SecurityMessageProperty&amp; remoteSecurity)

--- End of inner exception stack trace ---

 

</ExceptionString>

 

<

 

 

 

 

InnerException>

 

<

 

 

 

ExceptionType>System.Security.Authentication.AuthenticationException, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

 

<

 

 

 

Message>Authentication failed on the remote side (the stream might still be available for additional authentication attempts).</Message>

 

<

 

 

 

StackTrace>

 

<dir></dir><dir></dir><dir></dir>

 

<dir>

at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)

at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(Stream stream, SecurityMessageProperty&amp; remoteSecurity)

<dir></dir>
</dir>

</

 

 

StackTrace>

 

<

 

 

 

ExceptionString>System.Security.Authentication.AuthenticationException: Authentication failed on the remote side (the stream might still be available for additional authentication attempts). ---&gt; System.ComponentModel.Win32Exception: The target principal name is incorrect

 

--- End of inner exception stack trace ---

at System.Net.Security.NegoState.ProcessAuthentication(LazyAsyncResult lazyResult)

at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(Stream stream, SecurityMessageProperty&amp; remoteSecurity)

 

</ExceptionString>

 

<

 

 

 

 

InnerException>

 

<dir>

<

</dir><dir>

 

</dir><dir>

 

</dir>

 

<dir>

ExceptionType>System.ComponentModel.Win32Exception, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>

 

<

 

 

 

Message>The target principal name is incorrect</Message>

 

<

 

 

 

StackTrace>

 

<dir></dir><dir></dir><dir></dir>

 

<dir>

at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeAcceptor.OnAcceptUpgrade(Stream stream, SecurityMessageProperty&amp; remoteSecurity)

at System.ServiceModel.Channels.StreamSecurityUpgradeAcceptorBase.AcceptUpgrade(Stream stream)

at System.ServiceModel.Channels.InitialServerConnectionReader.UpgradeConnection(IConnection connection, StreamUpgradeAcceptor upgradeAcceptor, IDefaultCommunicationTimeouts defaultTimeouts)

at System.ServiceModel.Channels.ServerSessionPreambleConnectionReader.ServerFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)

at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

at System.ServiceModel.Dispatcher.ChannelHandler.OpenAndEnsurePump()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke2()

at System.Security.SecurityContext.Run(SecurityContext securityContext, ContextCallback callback, Object state)

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.WorkItem.Invoke()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ProcessCallbacks()

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.CompletionCallback(Object state)

at System.ServiceModel.Channels.IOThreadScheduler.CriticalHelper.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)

at System.ServiceModel.Diagnostics.Utility.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)

at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

<dir></dir>
</dir>

</

 

 

StackTrace>

 

<

 

 

 

ExceptionString>System.ComponentModel.Win32Exception: The target principal name is incorrect</ExceptionString>

 

<

 

 

 

NativeErrorCode>80090322</NativeErrorCode>

 

<dir></dir>

</

 

 

 

</dir>

InnerException>

 

</

 

 

 

InnerException>

 

<dir></dir>

</

 

 

 

</dir>

Exception>

 

<dir></dir>

</

 

 

 

</dir>

TraceRecord>

 

</

 

 

 

DataItem>

 

<dir></dir>

</

 

 

 

</dir>

TraceData>

 

<dir></dir>

</

 

 

 

</dir>

ApplicationData>
</
E2ETraceEvent>

 

<dir></dir>

 

</dir>
Feb 20, 2009 at 12:02 AM
Edited Feb 20, 2009 at 12:02 AM
The only way I was able to handle the communication correctly was to have the NetTcp binding security mode set to None.  For some reason though even with an account I know works the call's security negotiation with the Backend service doesn't work.  I made sure the account works by calling the same backend service with the account that I used to run the broker runtime.  Is the call made by the broker using a different account then the one the broker runtime service runs under?
Feb 20, 2009 at 6:48 PM
I found my issue and still don't have a way to fix it.  I ran fiddler and noticed that when I call going through the MSE the soap message being sent to the backend services is missing the security header.  So when it tries to negotiate it is unable to because there is no information for it to.  I ran fiddler on the straight call to the services and it has all the information it needs in order to make the call meaning it has the security header information.  It looks like the MSE broker is not setting up correctly using the imported binding from our service call.  Any ideas or is this a bug?
Developer
Feb 24, 2009 at 7:36 PM
What does the binding look like that the MSE is using to call your service implementation?  If you know what your binding should be, you could modify the imported one to match what it should be (i.e. Message Security with Windows Credentials, etc.).
We have had issues with importing various types of services that use secure bindings so you may need to specify the imported binding yourself.
Feb 24, 2009 at 7:56 PM
That is one of the things I checked and they are exactly the same.  It is using message security with windows credentials.   So it looks like the imported bindings imported correctly.