Just when I thought I had seen all possible 403 Forbidden errors and could pinpoint the 403 issues without looking into traces, I found myself surprised by another 403 error. I was testing a WWSAPI client to WCF server interop scenario. Only this time the WCF server was hosted on IIS 7.0 on Windows Server 2008. The WWSAPI client using WS_SSL_TRANSPORT_SECURITY_BINDING with client certificate failed with WS_E_ENDPOINT_ACCESS_DENIED (0x803D0005). The WS_ERROR object contained these error strings (they also showed up in WWSAPI traces in a reverse order. The order below resembles the stack trace of a managed exception):
There was an error communicating with the endpoint at 'https://NWSDC/securityTest/SslAppWithClientCert/Service1.svc'.
The server returned HTTP status code '403 (0x193)' with text 'Forbidden'.
The server understood the request, but cannot fulfill it.
Since this was a typical HTTPS mutual authentication scenario (i.e. client certificate is required), my past experience only pointed to these cases:
1. The client certificate is not specified.
2. The client certificate does not have a private key.
3. The SSL server certificate binding is not configured to negotiate client certificate.
4. The client certificate is not trusted by the server.
I was sure that the client certificate with a private key was specified properly since I’d used it in other scenarios. Also, the IIS web application was configured properly to require client certificate as I just did it. So I figured it had to be a distrusted client certificate case. But after I checked the client certificate, its issuer and the server certificate store. it turned out not to be the case. Couldn’t think of anything else from my experience, I had to resort to traces.
First I enabled the WCF tracing and reran the client. Nothing was captured. The error must have been returned by IIS before secure channel was established. So I enabled IIS Failed Request Tracing and reran the client. There in the tracing XML file, I clearly saw this Warning:
ModuleName="IIS Web Core", Notification="BEGIN_REQUEST", HttpStatus="403", HttpReason="Forbidden", HttpSubStatus="13", ErrorCode="The revocation function was unable to check revocation because the revocation server was offline. (0x80092013)", ConfigExceptionInfo=""
I suddenly realized that the client certificate I used contained a CRL distribution point (CDP) extension with a file scheme CRL. That must be the problem! So I changed the client code to use a different certificate that did not have a CDP extension. Problem fixed!