A common problem with service security is that username/password security is needed for authentication and authorization at the service boundary, but those same credentials are also required to consume other resources such as a database or underlying service. By default, username/password security will run the authentication and authorization of the credentials but only the username is available to the executing service code. This is typically made available through Thread.CurrentPrincipal.Identity.Name.
Storing username password credentials in a custom principal and identity against Thread.CurrentPrincipal is a really nice way of going. Thread.CurrentPrincipal returns IPrincipal which is a common framework type that will be available to all layers of a service executed by the thread. If Thread.CurrentPrincipal.Identity can return a custom IIdentity, then this is where the password can be made available. Using Thread.CurrentPrincipal frees up business and data access layers from relying on any security design that is tied up with a specific service implementation. The trick is how to get username password information into the thread that executes the service code.
One place that both the username and password is available is in the UserNamePasswordValidator.Validate() method. You could write a custom validator and hook it up in your service configuration, but this doesn't help you. There is no native facility to store the password from the validator. You can't store any credentials against Thread.CurrentPrincipal as the validator gets evaluated by WCF on a different thread than the thread that executes the service code. You could manually put it somewhere like in a static collection, but this would have potential security risks as your code will be handling the safety and security of multiple sets of credentials for users. This also means that somewhere else in the service implementation would require the credentials to be pulled out of the static and put into Thread.CurrentPrincipal. Using validators for this purpose is not the answer. Validators are for validating credentials, not storing them for later use.
Thankfully, there is a much more elegant way of passing these credentials around compared to the validator based solution. There is another place in WCF where there is access to both the username and the password and the security context of the service. Leveraging WCF extensibility is the answer.
Setting up the security context of the service is done using IAuthorizationPolicy implementations. The place in WCF where there is access to the username, password and IAuthorizationPolicy configuration is CustomUserNameSecurityTokenAuthenticator.ValidateUserNamePasswordCore. The ValidateUserNamePasswordCore method is passed the username and password parameters and returns a readonly collection of IAuthorizationPolicy objects. SecurityTokenAuthenticator, from which CustomUserNameSecurityTokenAuthenticator ultimately inherits from, is not configurable in WCF itself. Using Reflector to follow the calls against SecurityTokenAuthenticator, the place that is extensible in WCF such that a custom SecurityTokenAuthenticator can be used is ServiceCredentials. Creating a custom ServiceCredentials object, using custom objects between the ServiceCredentials and SecurityTokenAuthenticator call stack is the answer.
PasswordServiceCredentials
The custom ServiceCredentials class implementation returns a custom SecurityTokenManager if custom username password validation is enabled.
public class PasswordServiceCredentials : ServiceCredentials
{
public PasswordServiceCredentials()
{
}
private PasswordServiceCredentials(
PasswordServiceCredentials clone)
: base(clone)
{
}
protected override ServiceCredentials CloneCore()
{
return new PasswordServiceCredentials(this);
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
// Check if the current validation mode is for custom username password validation
if (UserNameAuthentication.UserNamePasswordValidationMode == UserNamePasswordValidationMode.Custom)
{
return new PasswordSecurityTokenManager(this);
}
Trace.TraceWarning(Resources.CustomUserNamePasswordValidationNotEnabled);
return base.CreateSecurityTokenManager();
}
}
PasswordSecurityTokenManager
The custom SecurityTokenManager returns a custom SecurityTokenAuthenticator when it finds a SecurityTokenRequirement for username security. It also ensures that a default validator is available if one is not configured.
internal class PasswordSecurityTokenManager : ServiceCredentialsSecurityTokenManager
{
public PasswordSecurityTokenManager(
PasswordServiceCredentials credentials)
: base(credentials)
{
}
public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
{
if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)
{
outOfBandTokenResolver = null;
// Get the current validator
UserNamePasswordValidator validator =
ServiceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator;
// Ensure that a validator exists
if (validator == null)
{
Trace.TraceWarning(Resources.NoCustomUserNamePasswordValidatorConfigured);
validator = new DefaultPasswordValidator();
}
return new PasswordSecurityTokenAuthenticator(validator);
}
return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
}
}
PasswordSecurityTokenAuthenticator
The custom SecurityTokenAuthenticator is where the half of the magic happens. Here there is the opportunity to return custom a IAuthorizationPolicy implementation that will allow us to inject a custom IPrincipal and IIdentity on the thread the executes the service code.
internal class PasswordSecurityTokenAuthenticator : CustomUserNameSecurityTokenAuthenticator
{
public PasswordSecurityTokenAuthenticator(
UserNamePasswordValidator validator)
: base(validator)
{
}
protected override ReadOnlyCollection<
IAuthorizationPolicy> ValidateUserNamePasswordCore(
String userName,
String password)
{
ReadOnlyCollection<IAuthorizationPolicy> currentPolicies = base.ValidateUserNamePasswordCore(
userName, password);
List<IAuthorizationPolicy> newPolicies = new List<IAuthorizationPolicy>(currentPolicies);
newPolicies.Add(new PasswordAuthorizationPolicy(userName, password));
return newPolicies.AsReadOnly();
}
}
PasswordAuthorizationPolicy
The IAuthorizationPolicy implementation is where the other half of the magic happens. This policy gets passed the username and password so that it can store the password in the security context. The policy will return false until it finds a GenericIdentity in the evaluation context that has the same username as the one provided to the policy. It then creates a custom IIdentity that exposes both the username and password and stores it back into the collection of identities in the evaluation context and also stores it against the PrimaryIdentity property. PrimaryIdentity is then exposed through ServiceSecurityContext.PrimaryIdentity in the service implementation. A custom principal is then created (without roles) and stores it against the Principal property of the context. Principal is then injected into Thread.CurrentPrincipal by WCF (depending on configuration).
using System;
using System.Collections.Generic;
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
using System.Security.Principal;
using System.ServiceModel;
using System.Threading;
namespace Neovolve.Framework.Communication.Security
{
internal class PasswordAuthorizationPolicy : IAuthorizationPolicy
{
public PasswordAuthorizationPolicy(
String userName,
String password)
{
const String UserNameParameterName = "userName";
if (String.IsNullOrEmpty(userName))
{
throw new ArgumentNullException(UserNameParameterName);
}
Id = Guid.NewGuid().ToString();
Issuer = ClaimSet.System;
UserName = userName;
Password = password;
}
public bool Evaluate(
EvaluationContext evaluationContext,
ref object state)
{
const String IdentitiesKey = "Identities";
// Check if the properties of the context has the identities list
if (evaluationContext.Properties.Count == 0
|| evaluationContext.Properties.ContainsKey(IdentitiesKey) == false
|| evaluationContext.Properties[IdentitiesKey] == null)
{
return false;
}
// Get the identities list
List<IIdentity> identities = evaluationContext.Properties[IdentitiesKey] as List<IIdentity>;
// Validate that the identities list is valid
if (identities == null)
{
return false;
}
// Get the current identity
IIdentity currentIdentity =
identities.Find(
identityMatch =>
identityMatch is GenericIdentity
&& String.Equals(identityMatch.Name, UserName, StringComparison.OrdinalIgnoreCase));
// Check if an identity was found
if (currentIdentity == null)
{
return false;
}
// Create new identity
PasswordIdentity newIdentity = new PasswordIdentity(
UserName, Password, currentIdentity.IsAuthenticated, currentIdentity.AuthenticationType);
const String PrimaryIdentityKey = "PrimaryIdentity";
// Update the list and the context with the new identity
identities.Remove(currentIdentity);
identities.Add(newIdentity);
evaluationContext.Properties[PrimaryIdentityKey] = newIdentity;
// Create a new principal for this identity
PasswordPrincipal newPrincipal = new PasswordPrincipal(newIdentity, null);
const String PrincipalKey = "Principal";
// Store the new principal in the context
evaluationContext.Properties[PrincipalKey] = newPrincipal;
// This policy has successfully been evaluated and doesn't need to be called again
return true;
}
public String Id
{
get;
private set;
}
public ClaimSet Issuer
{
get;
private set;
}
private String Password
{
get;
set;
}
private String UserName
{
get;
set;
}
}
}
Configuration
As mentioned above, the custom ServiceCredentials needs to be configured so that the custom IAuthorizationPolicy is evaluated. Under serviceBehaviors, the serviceCredentials element allows a custom type to be defined. userNameAuthentication needs to be set to Custom, otherwise Windows authentication of the username password credentials will be used by default. Lastly, serviceAuthorization needs to set userNamePasswordValidationMode to custom. Without the validation mode being custom, the custom IPrincipal will not be assigned against Thread.CurrentPrincipal.
The other thing to note is that because username password credentials are being passed over to the wire to the service, transport security is required to protect the credentials. The security mode should be set to TransportWIthMessageCredentials with a message client credential type as UserName.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpBindingConfig">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</netTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="Neovolve.Framework.Communication.SecurityTest.Service1Behavior"
name="Neovolve.Framework.Communication.SecurityHost.Service1">
<endpoint address="net.tcp://localhost:8792/PasswordSecurityTest"
binding="netTcpBinding" bindingConfiguration="netTcpBindingConfig"
contract="Neovolve.Framework.Communication.SecurityHost.IService1" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Neovolve.Framework.Communication.SecurityTest.Service1Behavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials type="Neovolve.Framework.Communication.Security.PasswordServiceCredentials, Neovolve.Framework.Communication.Security">
<serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom" />
</serviceCredentials>
<serviceAuthorization principalPermissionMode="Custom" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
The code
Download the code and compiled help for this post here. Note that the unit test in the solution must be run in debug rather than normal unit test running because of the use of the WcfServiceHost.exe. With the configuration defined, an x509 certificate is required on the machine with localhost as the subject name.
As soon as I read about IErrorHandler in Juval Lowy's book, I was sold. This interface in WCF is excellent to use for error handling and shielding at service boundaries. The interface comes with two methods.
- ProvideFault allows you to handle the error and determine what is returned to the client. This is where exception shielding comes in. The service implementation or any of the layers in the service can throw an exception. The error handler is the opportunity to determine whether the exception thrown is something that the service understands and is happy for the client to receive or whether the exception needs to be shielded into another exception/fault. A shielded exception would typically be a generic service exception that says that the service encountered an error.
- HandleError allows you to process some error specific logic asynchronous to the service call. This means that you could do some tracing/instrumentation or some expensive operation without blocking the client call.
There issue that I have with IErrorHandler is that the documentation is not detailed enough to allow you to hook up an error handler so that it is actually used. My recent experience has resulted in me defining an IErrorHandler, but not correctly configuring it so that it gets invoked. This highlights a disadvantage of the IErrorHandler design (assuming a configuration based setup). The service will still work without the handler being invoked, you will just get different exceptions on the client. This has a potential security risk if exception shielding has been written to shield sensitive information from clients.
The MSDN documentation (see here) provides examples about how to to create an IErrorHandler implementation including the behavior extension, but doesn't provide the full class examples. Hence my misunderstanding that resulted in my handler not being invoked. I suggest that two classes get created. One is the error handler, the other is the error handler element for configuration.
ErrorHandler
The error handler implements IErrorHandler, but also implements IServiceBehavior. This interface allows the error handler to be hooked up by configuration.
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WcfServiceLibrary1
{
public class ErrorHandler : IErrorHandler, IServiceBehavior
{
public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler = new ErrorHandler();
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if (channelDispatcher != null)
{
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
public bool HandleError(Exception error)
{
Trace.TraceError(error.ToString());
// Returning true indicates you performed your behavior.
return true;
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Shield the unknown exception
FaultException faultException = new FaultException(
"Server error encountered. All details have been logged.");
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
}
}
ErrorHandlerElement
The error handler element defines the extension behavior such that ErrorHandler can be defined against a service behavior.
using System;
using System.ServiceModel.Configuration;
namespace WcfServiceLibrary1
{
public class ErrorHandlerElement : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ErrorHandler();
}
public override Type BehaviorType
{
get
{
return typeof(ErrorHandler);
}
}
}
}
Configuration
This configuration identifies the ErrorHandlerElement as a behavior extension, which then allows errorHandler (the name of the configured extension) to be defined against the service behavior. This is how the error handler gets hooked up for the service.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation debug="true" />
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<errorHandler />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="errorHandler"
type="WcfServiceLibrary1.ErrorHandlerElement, WcfServiceLibrary1" />
</behaviorExtensions>
</extensions>
<services>
<service behaviorConfiguration="MyServiceBehavior"
name="WcfServiceLibrary1.Service1">
<endpoint binding="wsHttpBinding"
contract="WcfServiceLibrary1.IService1" />
</service>
</services>
</system.serviceModel>
</configuration>
This will hook up the error handler to cover any exceptions thrown by the service. Error handlers can also be debugged via the IDE.
I have an interesting scenario that I have just come across in my code. I have a foreach loop that is not getting 100% code coverage in unit tests. Prior to this, I really liked foreach for its ease of use and readability even though there is a minor performance penalty compared to using a for loop.
Here is the situation. I have a flush method that looks like this:
public void Flush()
{
// Loop through each listener
foreach (TraceListener listener in Source.Listeners)
{
// Flush the listener
listener.Flush();
}
}
Code coverage for this method says that 2 blocks not covered, 12.5% not covered, 14 blocks covered, 87.5% covered. Code metrics for this method are maintainability index is 80, cyclomatic complexity is 3, class coupling is 5 and lines of code is 2.
The IL for this method is:
.method public hidebysig instance void Flush() cil managed
{
.maxstack 2
.locals init (
[0] class [System]System.Diagnostics.TraceListener listener,
[1] class [mscorlib]System.Collections.IEnumerator CS$5$0000,
[2] bool CS$4$0001,
[3] class [mscorlib]System.IDisposable CS$0$0002)
L_0000: nop
L_0001: nop
L_0002: ldarg.0
L_0003: call instance class [System]System.Diagnostics.TraceSource MyNamespace.MyClass::get_Source()
L_0008: callvirt instance class [System]System.Diagnostics.TraceListenerCollection [System]System.Diagnostics.TraceSource::get_Listeners()
L_000d: callvirt instance class [mscorlib]System.Collections.IEnumerator [System]System.Diagnostics.TraceListenerCollection::GetEnumerator()
L_0012: stloc.1
L_0013: br.s L_002a
L_0015: ldloc.1
L_0016: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
L_001b: castclass [System]System.Diagnostics.TraceListener
L_0020: stloc.0
L_0021: nop
L_0022: ldloc.0
L_0023: callvirt instance void [System]System.Diagnostics.TraceListener::Flush()
L_0028: nop
L_0029: nop
L_002a: ldloc.1
L_002b: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
L_0030: stloc.2
L_0031: ldloc.2
L_0032: brtrue.s L_0015
L_0034: leave.s L_004d
L_0036: ldloc.1
L_0037: isinst [mscorlib]System.IDisposable
L_003c: stloc.3
L_003d: ldloc.3
L_003e: ldnull
L_003f: ceq
L_0041: stloc.2
L_0042: ldloc.2
L_0043: brtrue.s L_004c
L_0045: ldloc.3
L_0046: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_004b: nop
L_004c: endfinally
L_004d: nop
L_004e: ret
.try L_0013 to L_0036 finally handler L_0036 to L_004d
}
The UI for code coverage indicates that each line of code is hit. My guess is that there is something to do with the IEnumerator that is called when foreach is compiled.
I changed the code to this:
public void Flush()
{
// Loop through each listener
for (Int32 index = 0; index < Source.Listeners.Count; index++ )
{
TraceListener listener = Source.Listeners[index];
// Flush the listener
listener.Flush();
}
}
Code coverage now says that 0 blocks not covered, 0% not covered, 11 blocks covered, 100% covered. Code metrics for this method now say that maintainability index is 75, cyclomatic complexity is2, class coupling is 3 and lines of code is 3.
The IL for this method is now:
.method public hidebysig instance void Flush() cil managed
{
.maxstack 2
.locals init (
[0] int32 index,
[1] class [System]System.Diagnostics.TraceListener listener,
[2] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0024
L_0005: nop
L_0006: ldarg.0
L_0007: call instance class [System]System.Diagnostics.TraceSource MyNamespace.MyClass::get_Source()
L_000c: callvirt instance class [System]System.Diagnostics.TraceListenerCollection [System]System.Diagnostics.TraceSource::get_Listeners()
L_0011: ldloc.0
L_0012: callvirt instance class [System]System.Diagnostics.TraceListener [System]System.Diagnostics.TraceListenerCollection::get_Item(int32)
L_0017: stloc.1
L_0018: ldloc.1
L_0019: callvirt instance void [System]System.Diagnostics.TraceListener::Flush()
L_001e: nop
L_001f: nop
L_0020: ldloc.0
L_0021: ldc.i4.1
L_0022: add
L_0023: stloc.0
L_0024: ldloc.0
L_0025: ldarg.0
L_0026: call instance class [System]System.Diagnostics.TraceSource MyNamespace.MyClass::get_Source()
L_002b: callvirt instance class [System]System.Diagnostics.TraceListenerCollection [System]System.Diagnostics.TraceSource::get_Listeners()
L_0030: callvirt instance int32 [System]System.Diagnostics.TraceListenerCollection::get_Count()
L_0035: clt
L_0037: stloc.2
L_0038: ldloc.2
L_0039: brtrue.s L_0005
L_003b: ret
}
There are several posts around that talk about the performance difference of foreach vs for, but no-one seems to have actually posted metrics to base their stance on. One post that was in interesting read was How to Write High-Performance C# Code by Jeff Varszegi. As far as performance goes, the collection in this situation is always going to be very small so it is perhaps not that much of an issue.
I think that for loops would be faster after looking at the IL and understanding what foreach does under the covers. I don't think however that the performance difference is significant in itself. However, if foreach causes issues with code coverage, perhaps both these issues combined is enough of a reason to change coding practices.
Updated: Reformatted the IL code to avoid PRE tags that don't wrap.
I was playing with a new profile on my laptop last night as I was wanting to work with Outlook data syncing to my phone without changing the data on my normal profile. After I logged in, I found that Outlook wasn't responsive. I could use the mouse to do actions, but not the keyboard. I also couldn't close it. When I tried to add a SharePoint list Outlook would give me a message saying "A dialog is open. Close it and try again." The great thing was that there was no dialog open.
This frustrated me insanely for 30 minutes. I then remembered something from working at a small IT company about eight years ago. Back then, we had an issue with Word automation from VB6 on a server where the account running the service hadn't yet logged into the machine. Word was locking up on the dialog that appears asking for your name and initials. We had to log into the machine running the service with the service account, open Word, click Ok, log off and then the service would run the Word automation without a problem.
It then occurred to me that Outlook uses Word by default as its email editor. So when Outlook runs, it also spins up Word. As this is a new profile and I am only interested in Outlook, I hadn't run Word yet.
Sure enough, after opening Word, clicking Ok on the user details dialog and closing Word, Outlook worked like a charm.
I am so surprised that Microsoft haven't covered this scenario. It's crazy to render an application useless because the user hasn't dealt with a dialog that isn't displayed.