Closed Bug 135261 Opened 22 years ago Closed 22 years ago

SSL_REQUIRE_CERTIFICATE semantic troubles

Categories

(NSS :: Libraries, enhancement, P1)

enhancement

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: nelson, Assigned: nelson)

References

Details

Attachments

(1 file, 1 obsolete file)

The SSL documentation recommends that servers that wish to require SSL
cert-based client authentication should set SSL_REQUIRE_CERTIFICATE 
to PR_FALSE, and then check for the presence of a cert on the SSL socket
after the handshake is complete (e.g. when getting the client's request),
and deny the request at that time if the cert is absent.  

There's a problem with doing that with the current implementation of 
SSL_REQUIRE_CERTIFICATE.  The problem occurs when an SSL client has 
previously established an SSL session with the server, one that did not
include client authentication, and that has not expired.  When the client 
connects to the server again, if SSL_REQUIRE_CERTIFICATE is 0 (PR_FALSE)
the SSL code will simply restart the existing SSL session, and will not
do a new FULL SSL handshake, with the result that the user's cert is not
requested from the client, and the client repeatedly receives whatever
error message the server sends back when client auth is absent.  

In that same situation, if SSL_REQUIRE_CERTIFICATE is 1 (PR_TRUE), SSL
will refuse to restart the old SSL session, and will instead force a 
new FULL SSL handshake, requesting client authentication.  But if the 
client does not have cert, the SSL library will terminate the handshake 
and the TCP connection, which doesn't allow the server to send back a
better error message.  (This is the behavior that the SSL documentation
recommends NOT to do.)

So, what is needed is another setting of SSL_REQUIRE_CERTIFICATE, one 
that behaves partly like PR_TRUE, in that it refuses to restart old
SSL sessions if they didn't do client auth, but also behaves partly 
like PR_FALSE, in that it allows the SSL handshake to continue and
doesn't terminate the TCP connection if the client fails to provide
client auth.  

I can think of a couple of ways to implement this.  

One is to change the behavior of SSL when SSL_REQUIRE_CERTIFICATE is 
PR_FALSE so that it behaves as described in the preceeding paragraph.  
That change would assume that any server that requests SSL client auth 
really is going to require it, and is not going to allow operations 
(other than returning error message) to proceed without a client cert.  

Another is to create a new value for SSL_REQUIRE_CERTIFICATE that is 
neither PR_FALSE nor PR_TRUE.  There actually already exists one such
undocumented value.  SSL_REQUIRE_CERTIFICATE can be set to ((PRBool)2). 
Then it behaves as for PR_TRUE, but only on the first handshake on a 
TCP connection.  It behaves like PR_FALSE on subsequent handshakes.
So, one could propose the new value ((PRBool)3) to have the behavior
described above.

Comments on the choice of these is invited.

Soon, I will attach patches that implement these proposals.
I think some information is missing about the test case for this.

At the time the client establishes its first connection and SSL session with 
the server, the server has  both SSL_REQUIRE_CERTIFICATE set to PR_FALSE and 
SSL_REQUEST_CERTIFICATE set to PR_FALSE, so that the client doesn't get prompted 
for client auth.

Then, the connection times out (eg. HTTP keep-alive).
Then, the server gets reconfigured and now has SSL_REQUIRE_CERTIFICATE set to 
PR_FALSE and SSL_REQUEST_CERTIFICATE set to PR_TRUE for all new sockets.

Then the client connects to the server again, and presents its session ID in the 
clienthelo.
At that point, the server sees that there is no client certificate associated 
with the SSL session. It should force a full handshake to occur in order to get 
the client to prompt the user for a certificate, but still let the connection 
through with no certificate, to let the server check the certificate on 
the connection itself and send an error page if the certificate is missing.

Changed the QA contact to Bishakha.
QA Contact: sonja.mirtitsch → bishakhabanerjee
Priority: -- → P1
Target Milestone: --- → 3.6
I am about to check this in, barring objection.
Comment on attachment 84667 [details] [diff] [review]
Patch implements proposal described above.


>Index: ssl.h
>===================================================================
>RCS file: /cvsroot/mozilla/security/nss/lib/ssl/ssl.h,v
>retrieving revision 1.12
>diff -u -r1.12 ssl.h
>--- ssl.h	8 Nov 2001 02:15:35 -0000	1.12
>+++ ssl.h	22 May 2002 20:00:26 -0000
>@@ -138,6 +138,12 @@
> #define SSL_ALLOWED		 1
> #define SSL_RESTRICTED		 2	      /* only with "Step-Up" certs. */
> 
>+/* Values for "on" with SSL_REQUIRE_CERTIFICATE. */
>+#define SSL_REQUIRE_NEVER           PR_FALSE
>+#define SSL_REQUIRE_ALWAYS          PR_TRUE
>+#define SSL_REQUIRE_FIRST_HANDSHAKE ((PRBool)2)
>+#define SSL_REQUIRE_NO_ERROR        ((PRBool)3)
>+

These should say
#define SSL_REQUIRE_NEVER	    0
#define SSL_REQUIRE_ALWAYS	    1
#define SSL_REQUIRE_FIRST_HANDSHAKE 2
#define SSL_REQUIRE_NO_ERROR	    3

These values are not the PRBool type.
Attachment #84667 - Flags: needs-work+
These values get passed to SSL_OptionSet() as the "on" argument,
which is a PRBool.  That is why they are defined as either PRBools
or cast to PRBools.
Attachment #84667 - Flags: needs-work+
Comment on attachment 84667 [details] [diff] [review]
Patch implements proposal described above.

This is an abuse of the "on" argument for SSL_OptionSet().  Since we
already use the values 0, 1, and 2 as the "on" argument in previous
NSS releases, it is too late to fix this now.

>+/* Values for "on" with SSL_REQUIRE_CERTIFICATE. */
>+#define SSL_REQUIRE_NEVER           PR_FALSE
>+#define SSL_REQUIRE_ALWAYS          PR_TRUE
>+#define SSL_REQUIRE_FIRST_HANDSHAKE ((PRBool)2)
>+#define SSL_REQUIRE_NO_ERROR        ((PRBool)3)

I would change PR_FALSE and PR_TRUE to ((PRBool)0) and ((PRBool)1).
It is strange to see PR_FALSE and PR_TRUE used not as the values of
a boolean.
I agree that this patch is an abuse of PRBool.	There are other possible
solutions that do not abuse it, but would be MUCH bigger changes to the 
SSL code.  I was opting for an expedient fix that minimized bloat and 
risk due to large changes to the SSL code for a point release.	Agreed?
Attachment #84667 - Attachment is obsolete: true
Comment on attachment 84718 [details] [diff] [review]
Implements Wan-Teh's suggestion

r=wtc.

Julien, Christian, could you review Nelson's proposed
interface?
Attachment #84718 - Flags: review+
The interface is fine with me. I assume the Enterprise server would use the 
SSL_REQUIRE_NO_ERROR setting, right?
Yes.  However, we still need to test that using SSL_REQUIRE_NO_ERROR will
solve the server's problem.
The patch looks good.
I will build it and see if it solves the problem with NES 6.1 .
I tested this with NES 6.1 . It does solve the problem.
However, it required a modification of the application. I had to set 
SSL_REQUIRE_CERTIFICATE to SSL_REQUIRE_NO_ERROR to get the behavior I wanted, 
which was to request the client cert upon trying to resume the old SSL session 
without client auth, after the server configuration changed to request a client 
cert.

I am wondering if this shouldn't be the default behavior when a server sets 
SSL_REQUEST_CERTIFICATE to PR_TRUE and SSL_REQUIRE_CERTIFICATE to PR_FALSE, as 
NES 6.1 is doing. Is there any reason for the server not to send the 
certificaterequest alert in that case ?

We need to review the 4 possible values of SSL_REQUIRE_CERTIFICATE, examine what 
type of server application they correspond to, and document them. Perhaps the 
macro names could also be more intuitive, but I don't have any better idea 
right now.

Combined with the fact that there is a separate SSL_REQUEST_CERTIFICATE bit, 
this really makes the SSL client auth API for servers overly complex to 
understand. If this is so hard for us and our internal customers, I can't 
imagine what it is for a mozilla and NSS newbie. We should definitely do it for 
NSS 4.0 at the latest and simplify it.

Now, here is an attempt at summarizing the current behavior. There are 8 
possibilities of bit values. I will describe what happens with new and restart 
handshakes, as well as discuss possible scenarii with server reconfiguration. 
Nelson, please correct me if any of this is wrong.

1) SSL_REQUEST_CERTIFICATE=PR_FALSE , SSL_REQUIRE_CERTIFICATE=xx : if 
SSL_REQUEST_CERTIFICATE is PR_FALSE, SSL_REQUIRE_CERTIFICATE is ignored. So, for 
these four cases, the server never asks the client to present its certificate 
during either a new session or a session resumption, for new handshakes or for 
restarts. This means there will never be a certificate in the SSL session for a 
new handshake. There may be a certificate in the SSL session if this is a 
restart handhsake and the SSL session was created when the server was in a 
different configuration and got a certificate.

2) SSL_REQUEST_CERTIFICATE=PR_TRUE, SSL_REQUIRE_CERTIFICATE=SSL_REQUIRE_NEVER :
The server will request the certificate during the first handshake, without 
requiring it. This will allow communication to continue without client auth, 
perhaps falling back to another application-specific authentication method such 
as HTTP authentication, or providing an application error notification instead 
of an SSL error.

For restarts, if the SSL session did not have a client certificate associated 
with it, then the session is restarted without a certificate request and 
communication is also allowed to continue.

Note that this could mean that no certificate request was ever sent to the 
client for the lifetime of the SSL session, if the SSL session was created when 
the server was configured with SSL_REQUEST_CERTIFICATE=PR_FALSE. 

3) SSL_REQUEST_CERTIFICATE=PR_TRUE, SSL_REQUIRE_CERTIFICATE=SSL_REQUIRE_ALWAYS :
For every new SSL handshake, the server will ask the client for a certificate, 
and an SSL error will result if the client does not present a valid certificate, 
without any chance for further application communication.

For restarts, if there isn't already a certificate, the SSL session is deleted 
and a new full handshake is done as described above; otherwise the session is 
restarted.

4) SSL_REQUEST_CERTIFICATE=PR_TRUE, 
SSL_REQUIRE_CERTIFICATE=SSL_REQUIRE_FIRST_HANDSHAKE :

For the first handshake, this will behave like case 3) .
For a restart handshake, it will behave like case 2).

If you take into account the possibility that the server configuration was 
changed between the time the SSL session was created and then restarted, then 
you can see that this is a "dangerous" setting. I believe its intent is to make 
sure that client auth is enforced, but without incurring the performance impact 
of doing client auth on restart handshakes. The problem is that an SSL session 
could be created with the server in a different state, without a client 
certificate. Then the server configuration could be changed to this one, and 
there would be no client certificate available. This means this setting should 
never be used in servers that dynamically reconfigure themselves.

5) SSL_REQUEST_CERTIFICATE=PR_TRUE, SSL_REQUIRE_CERTIFICATE=SSL_REQUIRE_NO_ERROR 
:

For new handshakes, this behaves like 2) .
For restart handshakes, if the SSL session does not already have a certificate, 
then the session is deleted, and a new handshake is done, which behaves like 2).
Effectively, this means all handshakes are full, and we are not doing any 
restarts. But there are never any SSL errors due to lack of a client 
authentication.

Here are some sample uses of client authentication in server applications, from 
simplest to most complicated.

I The entire site uses SSL without any client authentication

This maps to the case described above under 1).

II The site entire site is protected by SSL client authentication

a. Client authentication is requested at the SSL level, and enforced at the 
application level with an error page. The application must invalidate the 
session after sending the error page in order to make sure the client can try 
again.

b. There is also a fallback to application authentication. Client authentication 
is requested at the SSL level, and if a client certificate is not available, the 
application falls back to its own method of authentication. If the application 
authentication fails, the application must invalidate the SSL session after 
sending the error page, in order to make sure the client can try again. If the 
application authentication succeeds, it leaves the SSL session valid, and 
restart handshakes must be honored, without any further certificate requests.

Both of these map to the case described above under 2).

III The entire site is protected by SSL client authentication, which is required 
and enforced at the SSL level.

This maps to the case described above under 3).

IV The site is mixed unprotected/protected by client authentication. Most of the 
site is unprotected. So this maps to case described above under 1) for all first 
handshakes - we don't want to even request the client certificate for most 
content even if the user has one. For protected content, new handshakes will be 
done with configuration 5).

V The site is mixed unprotected/protected by client authentication. The majority 
of the site is protected. In this case it makes sense to request the client 
certificate but not require it for first handshakes. This maps to case 5) above 
for first handshakes. For protected content, new handshakes need not be done, 
the client certificate can just be looked up in the socket by the application if 
available, and access denied if unavailable.
However the exception to this rule is if the server was reconfigured and the 
session was created with a configuration different than 5), and never had a 
certificate request. In that case we would want to do a new handshake and ask 
the client again. However, there is no way in the API to know if there was a 
certificate request on the SSL session, so the application can't do that.

I think it would be worthwile to add a field in the SSL session noting whether 
we attempted a cert request, regardless of whether it succeeded or failed. I 
think we should add the following logic :

If no certificate request was done in the initial handshake (ie. 
SSL_REQUEST_CERTIFICATE was PR_FALSE), and we are doing a restart, but 
SSL_REQUEST_CERTIFICATE is now PR_TRUE, then we should force a new full 
handshake for all values of SSL_REQUIRE_CERTIFICATE - but only 
SSL_REQUIRE_ALWAYS would result in an SSL error if the client does not send a 
certificate.
I have checked in the patch above onto the trunk.  
Is there also some branch on which it should be checked in?
Blocks: 153380
No.  3.6 is on the trunk right now.  We don't
need this in 3.5 (which is to support the
Netscape 7.0 release).
OK, then I am marking this fixed. 

Julien has suggested that the value 3 ought to have a better name than
the one I gave it, and I don't disagree, but I haven't got a better one.
If you'd prefer another name, please suggest it here.  
Status: NEW → RESOLVED
Closed: 22 years ago
Resolution: --- → FIXED
The mozilla documentation in the online SSL reference manual needs to be
updated for this change.  That used to be the exclusive domain of a 
tech writer and I had no CVS access to the CVS repository that held the
documentation.   If that repository is now accessible to me, please 
email me directions on how to access it.  Thanks.
The value 2 in the initializer for ssl_defaults in sslsock.c
should be replaced by the new symbolic constant
(SSL_REQUIRE_FIRST_HANDSHAKE).
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: