Three gears in action. Shallow dof, focus in the theeth that make contact. Metaphorical illustration of Industrial workforce, business worflow, teamwork cooperation, energy, transmition,  motor

Locking Down a Self Hosted WCF Service To the Current User


enter image description here

I am a big fan of using WCF services in my apps. Many times I like to have little services as part of my app, but the problem I have is that by default any user on the machine can access those services. Always thinking about security, I want to make sure that only the current user can access their own services.

Consider the following code:

class TestService
{

    private readonly ServiceHost _host;
    public TestService()
    {
        var pipePath = "net.pipe://localhost/Math";
        NetNamedPipeBinding binding = new NetNamedPipeBinding();
        _host = new ServiceHost(typeof(MathService), new Uri(pipePath));

        ServiceMetadataBehavior smb = _host.Description.Behaviors.Find<ServiceMetadataBehavior>();

        smb = new ServiceMetadataBehavior();

        _host.Description.Behaviors.Add(smb);
        _host.AddServiceEndpoint(typeof(IMath), binding, pipePath);
        _host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, 
                MetadataExchangeBindings.CreateMexNamedPipeBinding(), pipePath + "/mex");
    }

    public async Task StartAsync()
    {
        await Task.Factory.FromAsync(_host.BeginOpen(null, null), _host.EndOpen);
    }

    public string Endpoint { get { return _host.BaseAddresses.First().AbsoluteUri.ToString(); } }

}

[ServiceContract]
public interface IMath
{
    [OperationContract]
    int Add(int x, int y);
}

public class MathService : IMath
{
    public int Add(int x, int y)
    {
        unchecked
        {
            return x + y;
        }
    }
}

When I start this service, anyone on the machine can talk to the service via its URI (net.pipe://localhost/Math). In order to lock this service down, I did my research and found that you can implement a custom ServiceAuthorizationManager which will allow you to control who has access to your services.

To start, I created a simple implementation that checked the current user against the operation context security user:

public class CurrentUserOnlyAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        var currentUser = WindowsIdentity.GetCurrent()?.User;
        var contextUser = operationContext?.ServiceSecurityContext?.WindowsIdentity?.User;
        if (currentUser == null || contextUser==null)
            return false;

        return currentUser.Equals(contextUser);
    }
}

And when setting up my host, I have it use the new manager:

_host.Authorization.ServiceAuthorizationManager = new CurrentUserOnlyAuthorizationManager();

This works well, until you try and regenerate the service reference. By default the service references are generated using an anonymous request to the service. Since the anonymous user doesn’t match the user hosting the service the code prevents the anonymous user from accessing the service. Fortunately there was an MSDN article that addressed this specifically and gave the exact code to handle mex requests. So my final CurrentUserOnlyAuthorizationManager looks like this:

public class CurrentUserOnlyAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        // Allow MEX requests through.
        if (operationContext.EndpointDispatcher.ContractName == ServiceMetadataBehavior.MexContractName &&
            operationContext.EndpointDispatcher.ContractNamespace == "http://schemas.microsoft.com/2006/04/mex" &&
            operationContext.IncomingMessageHeaders.Action == "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get")
            return true;

        var currentUser = WindowsIdentity.GetCurrent()?.User;
        var contextUser = operationContext?.ServiceSecurityContext?.WindowsIdentity?.User;
        if (currentUser == null || contextUser==null)
            return false;

        return currentUser.Equals(contextUser);
    }
}

Once this is in place, then I am able to successfully regenerate the service references as needed through Visual Studio or the svcutil app. The final code for the test service is:

class TestService
{

    private readonly ServiceHost _host;
    public TestService()
    {
        var pipePath = "net.pipe://localhost/Math";
        NetNamedPipeBinding binding = new NetNamedPipeBinding();
        _host = new ServiceHost(typeof(MathService), new Uri(pipePath));

        ServiceMetadataBehavior smb = _host.Description.Behaviors.Find<ServiceMetadataBehavior>();

        smb = new ServiceMetadataBehavior();

        _host.Description.Behaviors.Add(smb);
        _host.AddServiceEndpoint(typeof(IMath), binding, pipePath);
        _host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexNamedPipeBinding(), pipePath + "/mex");

        _host.Authorization.ServiceAuthorizationManager = new CurrentUserOnlyAuthorizationManager();

    }

    public async Task StartAsync()
    {
        await Task.Factory.FromAsync(_host.BeginOpen(null, null), _host.EndOpen);
    }

    public string Endpoint { get { return _host.BaseAddresses.First().AbsoluteUri.ToString(); } }

}

[ServiceContract]
public interface IMath
{
    [OperationContract]
    int Add(int x, int y);
}

public class MathService : IMath
{
    public int Add(int x, int y)
    {
        unchecked
        {
            return x + y;
        }
    }
}

public class CurrentUserOnlyAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        // Allow MEX requests through.
        if (operationContext.EndpointDispatcher.ContractName == ServiceMetadataBehavior.MexContractName &&
            operationContext.EndpointDispatcher.ContractNamespace == "http://schemas.microsoft.com/2006/04/mex" &&
            operationContext.IncomingMessageHeaders.Action == "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get")
            return true;

        var currentUser = WindowsIdentity.GetCurrent()?.User;
        var contextUser = operationContext?.ServiceSecurityContext?.WindowsIdentity?.User;
        if (currentUser == null || contextUser==null)
            return false;

        return currentUser.Equals(contextUser);
    }
}

Now, if a user other than the user that started the service tries to access the service, they will get a SecurityAccessDeniedException. I have also posted this code up on Github, if you want to download it an play with it.

Image Credit: ralphbijker via Creative Commons

MagnifyingGlass

Code Analysis With Captured Variables

I am a big fan of using Code Analysis (formerly FXCop) in my Visual Studio projects to help catch issues. Recently I ran across a CA2208 warning that I needed to think about. Usually these are simple results of refactoring or forgetting to include the argument name in the ArgumentNullException or ArgumentOutOfRangeException. This one, however, was a little different. The value was passed to the exception constructor and the name was correct.

The following code reproduces the issue (note this is C# 6 code written in the VS 2015 Preview leveraging the new nameof keyword):

private static void Foo(string bar, string baz)
{
    if (bar == null)
        throw new ArgumentNullException(nameof(bar));

    Task.Run(() =>
    {
        if (baz == null)
            throw new ArgumentNullException(nameof(baz));

    });
}

The reason being is that rule checks to make sure the name being passed to the constructor of the exception matches a parameter of the current method. However, the code that gets generated for the delegate doesn’t pass baz as a parameter, rather it is a public field on the generated class. The code for the generated class essentially looks like this:

[CompilerGenerated]
private sealed class <>c__DisplayClass0
{
    public string baz;
    internal void <Foo>b__1()
    {
        bool flag = this.baz == null;
        if (flag)
        {
            throw new ArgumentNullException("baz");
        }
    }
}

So I have come to the conclusion to simply suppress this message for the time being. Hopefully with the new Roslyn analysis capabilities minor issues like these can be completely avoided.

Image Credit: Kit using Creative Commons License

Getting the Name of the Day of the Week in C#

Getting the current day of week in C# is pretty easy, if all you need is the english version of the day, for example:

MessageBox.Show(System.DateTime.Now.DayOfWeek.ToString());

But if you want to get the Day of Week for the current language the computer is setup for, the above code will not work. You need to access the Globalization class and get the day of week from there. The snippet of code below, demonstrates how to do it:

MessageBox.Show(System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat.DayNames[(int) 
            System.DateTime.Now.DayOfWeek]);

Why Does This Implicit Conversion Fail?

There was a recent question on StackOverflow that piqued my interest and was a rather interesting problem dealing with some odd behavior someone observed with implicit operators.

Consider the following wrapper class:

sealed class Wrapper<T> : IDisposable
{
    public T Child { get; private set; }
    public Wrapper(T child) { Child = child; }
    public static implicit operator Wrapper<T>(T child) { return new Wrapper<T>(child); }

    public static implicit operator T(Wrapper<T> host) { return host.Child; }

    public void Dispose()
    {
        try { Marshal.ReleaseComObject(Child); }
        catch { }
    }
}

And the consuming code:

// DON'T DO THIS
using (Wrapper<Excel.Application> _xlApp = new Excel.Application()) 
{
    Excel.Application xlApp = _xlApp; 
}

First of all, don’t do this to try and free up COM objects. The Garbage Collector will take care of it for you and you should just let it do its job.

Regardless of whether you should do it or not, the interesting part of the question is, why doesn’t this compile. It gives the following errors:

  • Cannot implicitly convert type ‘Microsoft.Office.Interop.Excel.Application’ to ‘CSharpWinForms.Wrapper<Microsoft.Office.Interop.Excel.Application>’
  • Cannot implicitly convert type ‘CSharpWinForms.Wrapper<Microsoft.Office.Interop.Excel.Application>’ to ‘Microsoft.Office.Interop.Excel.Application’

You would expect that the implicit operators would allow the conversions between the Wrapper and the generic parameter type without issue. The key to the issue here is knowing that Excel.Application is an interface and there are different rules for conversions with interfaces.

Let’s browse out to our C# spec ("C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC#\Specifications\1033\CSharp Language Specification.docx") and open it up to 10.10.3.

The two parts of the spec that are applicable here:

  • S0 and T0 are different types.

  • Either S0 or T0 is the class or struct type in which the operator declaration takes place.

  • Neither S0 nor T0 is an interface-type.

  • Excluding user-defined conversions, a conversion does not exist from S to T or from T to S.

and

  • If a pre-defined implicit conversion (§6.1) exists from type S to type T, all user-defined conversions (implicit or explicit) from S to T are ignored.

  • If a pre-defined explicit conversion (§6.2) exists from type S to type T, any user-defined explicit conversions from S to T are ignored. Furthermore:

    • If T is an interface type, user-defined implicit conversions from S to T are ignored.
    • Otherwise, user-defined implicit conversions from S to T are still considered.

This second section was added to the spec after it’s initial release and the Microsoft development team obviously spent time thinking about this, as explained in this Eric Lippert answer.

Specifically he calls out the problem with interface based implicit conversion:

Why? Because one has the reasonable expectation that when one converts a value to an interface, that you are testing whether the object in question implements the interface, not asking for an entirely different object that implements the interface. In COM terms, converting to an interface is QueryInterface — “do you implement this interface?” — and not QueryService — “can you find me someone who implements this interface?”

Finally he states:

However, generics muddy the waters considerably, the spec wording is not very clear, and the C# compiler contains a number of bugs in its implementation. Neither the spec nor the implementation are correct given certain edge cases involving generics, and that presents a difficult problem for me, the implementer. I am actually working with Mads today on clarifying this section of the spec, as I am implementing it in Roslyn next week.

So, in conclusion, the reason the conversion doesn’t work is because that is how the language is specced and if you consider Eric’s answer as to why, it does make a lot of sense.

How I Became a Security Researcher

To be clear, I don’t consider myself a security researcher, rather I am a programmer who happens to dabble in security research. Maybe this is a little bit of imposter syndrome kicking in, but I really think I just know enough to be dangerous.

A few months back I learned of the Online Services Bug Bounty program Microsoft was offering. I decided to give it a go. I am always looking for authorized hacking opportunities and really wasn’t expecting anything to come of it. I created my test tenants and started playing around. I decided to focus on XSS vulnerabilities, as that is something I am pretty comfortable with and is pretty easy to test client side. Whenever I have tested for XSS vulnerabilities, I have always found that dialog windows tend to be some of the biggest culprits, so I honed in on those in my test tenant.

Eventually I stumbled across an OWA dialog that wasn’t escaping strings. I was easily able to generate this (URLs hidden to protect the innocent):

And then with a little more tweaking, this:

It was clear I had found an XSS vulnerability. I followed the submission instructions on the bug bounty page and waited to hear back. Within a few days, I got an email from the Microsoft security team indicating they had forwarded the bug to the product team. About a week after that I received confirmation that the product team had reproduced the bug and were working on a fix. Here is the timeline for the fix:

Oct. 9 (Thursday)   - Reported Bug
Oct. 13 (Monday)    - Microsoft confirmed receipt of bug 
Oct. 21 (Tuesday)   - Product team confirms repro of bug
Oct. 29 (Wednesday) - Product team confirmed that bug exists in boxed products
Dec. 9 (Tuesday)    - Official security bulletin released.

Since this bug impacted user installed versions of exchange, it took a bit longer for the product team to fix. I didn’t continuously test the online URL to know exactly when it was fixed, but I know as of today the vulnerability is fixed.

The bulletin for the bug is MS14-075.

The Microsoft team has credited me for this fix in multiple locations: