Subject: Re: [Fwd: WWW-Authenticate challenge for client-certificates]

From: Bruno Harbulot <[email-masked]>
Date: Wed, 20 Jan 2010 15:33:25 +0000

Hello,

Martin Rex wrote:
> Bruno Harbulot wrote:
>> - If a certificate is mandatory, the 'require' mode leads to a handshake
>> error code on the client side, resulting in an abrupt error message,
>> which is confusing for most users.
> 
> Clarifying:  If the server's TLS is configure to require a client-cert,
> the server's TLS will produce the error message (and terminate the
> TLS handshake with a fatal alert message), which in turn will
> cause the client TLS implementation to report an error.

Indeed.

>> To try to solve this, I would like to suggest the introduction of
>> something along the lines of a 'transport' challenge, implying that the
>> authentication mechanism is outside the scope of HTTP itself.
>> This could be something like:
>> - WWW-Authenticate: transport
>> - WWW-Authenticate: transport mode="tls-client-certificate"
>>
>> To this, could be added parameters describing what kind of certificate
>> is preferred.

Just to clarify, I'm not suggesting implementing the client-certificate 
negotiation (or renegotiation) at the HTTP level.

I'd just like to address a gap in the HTTP authentication space, more 
specifically, the 401 status code says there MUST be one or more 
'WWW-Authenticate' header (with one or more challenges each).

If an HTTP client tries to access resource on an HTTP server that is 
protected and where authentication is required, the HTTP server will 
respond to the request with an HTTP 401 status code, and, for example 
"WWW-Authenticate: Basic xxxxx", thereby telling the client that it 
should send the request again, with appropriate authentication information.

This mechanism is useful in that:
- the WWW-Authenticate in the HTTP header is sufficient for the browser 
to know what to do (e.g. pop-up).
- a entity can still be associated with a response with 401 status code: 
there can be a page telling the user why access wasn't granted, where to 
get an account, etc.

At the moment, if a browser tries to connect to a page that requires TLS 
client-certificate authentication for granting access, and if this 
certificate isn't provided (irrespectively of how the TLS handshake 
went), the server will be most likely to return a 403 status, which 
implies that "the request SHOULD NOT be repeated" [RFC2616].

The current behaviour could be summarised as follows:
- Server configured for 'require' - Client sends a suitable certificate.
   - If this user is authorised: HTTP 200 OK, otherwise HTTP 403.

- Server configured for 'require' - Client doesn't send a certificate.
   - Server's TLS error message -> Client error message.
  This is the problematic behaviour in browsers: it confuses users and 
doesn't give much of a chance to tell them what was wrong with their 
certificate or where to get their certificate.

- Server configured for 'request' - Client sends a suitable certificate.
   - If this user is authorised: HTTP 200 OK, otherwise HTTP 403.

- Server configured for 'request' - Client doesn't send a certificate.
   - Will probably get 403, without a way to tell to send a certificate 
this time.

In HTTP terms, the 'require' configuration is just "rude" and the 
current 'request' configuration (without proper 401/WWW-Authenticate 
response) doesn't give much opportunity to provide an explanation and to 
try again.

This also prevents the server to present the client-cert authentication 
as one of a number of challenges. For example, using 'WWW-Authenticate', 
a server can offer an HTTP Basic challenge and a SPNEGO challenge at the 
same time, leaving the choice to the client.

> Unfortunately, the issue is _much_ more complex.
> Difficulties you have to address:
> 
>   1. whether or not the server requests a client certificate
>      is a decision of the server alone.  The client must not
>      send an unsolicited client certificate (that should cause
>      compliant servers to abort the handshake).

Indeed, but I don't think there would need to be a "pre-emptive" 
'Authorization' header in the same way in can be done with HTTP Basic 
for example. In fact, I don't think a request, even as a response to a 
401 status would need to have such an 'Authorization' header. All it 
would need, would be to have sent the client-certificate, through the 
TLS channel (perhaps re-negotiated, perhaps after a new connection).

This could be a good way to indicate to a browser that, even on the 
first attempt, it hadn't presented a certificate by default (assuming 
the server is in 'request' mode) for the first attempt, it should tell 
its TLS layer to send one for the second attempt.
Perhaps some indication of "realm" or something similar might help the 
browser make a better choice as to which certificate to use (this could 
be combined with improvement in user interfaces).

>   2. there currently is _no_ standardized fashion for TLS to
>      negotiate certain aspects of client certificate authentication
> 
>       a) client->server signaling that client des not have
>          a certificate (or is determined to not send one)
> 
>       b) client->server signaling that client has a certificate
>          and is willing to use it for client certificate authentication
>          (provided that server certificate validation succeeds).

I suppose you mean signaling before the handshake? Otherwise, sending an 
empty or non-empty Certificate message (from client to server) during 
the handshake should cover those two cases, respectively. Isn't this 
standard? I'm not sure what the problem would be otherwise.

>       c) server->client signaling that client may send *any*
>          certificate with the promise that the server will _NOT_
>          abort the handshake when the server does _not_ have a
>          trust anchor necessary to verify the certificate sent
>          by the client (requiring the server to either withhold
>          such a client cert from the server application, as if
>          the client had _not_ sent any cert -- at least _not_
>          provide such a client cert to server apps in the same
>          fashion as authenticated client certs.
> 
>          SSL/TLS client cert authentication kind-of blindly assumes
>          a PKI and SSLv3&TLSv1.0 _require_ a server to send a
>          non-empty list of trusted CAs.  Semantics of empty ca_list
>          in TLSv1.1&1.2 is underspecified.

In my experience with SSLv3 and TLSv1.0, more particularly Java's JSSE 
and Apache Httpd, even though there was no mention of emtpy ca_list in 
these versions, it works fine.

In Apache Httpd, using SSLCADNRequestFile with a file containing just a 
line return is enough to make it send an empty ca_list. Combined with 
"SSLVerifyClient optional_no_ca", it can accept any self-signed (or not) 
certificate.

In Java, setting the TrustManager do to this works fine:
     public X509Certificate[] getAcceptedIssuers() {
         return new X509Certificate[0];
     }

(That's something we use doing the FOAF+SSL work, where we verify the 
certificate at the application level, once the handshake is finished.)

This being said, I'm not suggesting making changes to the TLS 
specification, just conveying some extra information at the HTTP layer 
regarding the authentication that happens at the TLS layer, for better 
integration with the HTTP mechanisms.

>    3. (Bodo Moeller recently explained this to me):
>       cryptographic algorithm constraints on client certificates
>       According to rfc-5246 Section 7.4.6. there are constraints
>       if the client certificate is of one of these types:
>       rsa_fixed_dh, dss_fixed_dh, rsa_fixed_ecdh, ecdsa_fixed_ecdh
> 
>       http://tools.ietf.org/html/rfc5246#page-56
> 
>       restricting compatible client certs that can be used for
>       client cert authentication in TLS to _both_, characteristics
>       of the cipher suite plus characteristics of the server certificate
> 
>       This additional restriction does not apply to client certificates
>       rsa_sign, dsa_sign (and marginally to ecdsa_sign).

That's something I would leave as it is, for the TLS handshake to do. I 
don't think an WWW-Authenticate header should influence this.

If a 'WWW-Authenticate' for TLS certificate authentication was also to 
specify which certificate was preferred (something I'm not sure about at 
the moment), then the client would probably have to abide by the TLS 
requirements first and use the HTTP preference as a further hint only. 
For example, the ca_list sent by the server could contain a number of 
DNs, but the 'WWW-Authenticate' would have an order of preference, for 
this particular resource; if the preference list at the HTTP level was 
incompatible with the ca_list at the TLS layer (or other TLS 
requirements as you mention), then the TLS layer would prevail.

>> As a side note, I also think that such a mechanism could partly help
>> with the TLS renegotiation issue (still discussed on the IETF TLS
>> mailing list). Indeed, if the HTTP framework was able to detect a
>> renegotiation had occurred, it could make the client re-send the request
>> post-negotiation (rather than assume that the certificate presented
>> during the second handshake were also valid for the request sent before).
> 
> Changing/switching client identities is also a somewhat underspecified area.
> 
> In theory, clients can offer some means to "flush" the client-side
> TLS session cache (several browsers have that, I think), but it
> may be a function hidden in some configuration options many casual
> users have never used or maybe even looked at.

Indeed, in Firefox you can do this by logging out of the software 
security device (Preferences -> Advanced -> Encryption -> Security 
Devices -> 'Software Security Device' -> Log Out), and, in Internet 
Explorer: Internet Options -> Content -> Clear SSL State.

> I'm not sure that current / commonly used browsers, which offer
> multi-windows and tabbed browsing, have an adequate client-side
> session cache management (in the browser) to reliably manage many
> parallel sessions with the exact same server and individual
> TLS client identities/credentials for each session.

That's unfortunately not specific to TLS authentication. Browsers tend 
to have a single sign-on across tabs and windows, where you're logged on 
forever across all of them, for Basic/Digest/TLS/Cookie authentication 
with the exception that only cookies/forms tend to have a convenient log 
out mechanism. (Again, logging out of the software security device also 
logs you out in Firefox, but that's quite far away.)

Best wishes,

Bruno.