Closed Bug 116168 Opened 23 years ago Closed 18 years ago

TLS server name indication extension support in NSS

Categories

(NSS :: Libraries, enhancement, P2)

enhancement

Tracking

(Not tracked)

RESOLVED FIXED
3.11.1

People

(Reporter: julien.pierre, Assigned: nelson)

References

Details

(Keywords: fixed1.8.1)

Attachments

(2 obsolete files)

This is a tracking defect for new DNSName support in NSS. DNSName has not been
finalized yet as a standard but should be within the next few months. It would
be a good goal to support it in NSS 4.0, as it will allow a new level of virtual
server hosting especially for HTTP, resolving the need for a new TCP endpoint
for an SSL virtual server.

The idea behind DNSName is to allow an extra string to be sent by the client in
the Client Helo SSL/TLS message. The server is then free to interpret it.
This bug is to vouch for ways to programmatically do the following in an NSS
application :
a) for a client, to be able to set the string that will be sent. This may
implemented as the same as is done in the seturl option already
b) for a server, to retrieve the string halfway through the handshake. The
server needs to be able to do this right after accept of the NSPR TCP socket,
before it has selected its certificate, cipher, or even the version of SSL (2, 3
or TLS) that it wants to use . Nelson suggested that this should be done through
a callback function.
Priority: -- → P2
Target Milestone: --- → 4.0
Summary: DNSName support → DNSName support in NSS
Blocks: 116169
This feature necessitates a substantial change in the SSL/TLS API for 
servers, and a very minor change for clients.  For clients there is 
simply one more value, the requested server name, to be set on the 
socket through a new optional function call prior to initiating the 
handshake.  

But for servers, the change is larger.  Today, a server application
preconfigures the server socket with one certificate for each of the 
key exchange and/or authentication key types that it wishes to support,
e.g. a cert with an RSA key and another with a DSA key, and the SSL
code chooses from among those according to the key type needed to do 
the handshake.  In the future, the server may have many certs of the
same key type (e.g. certs with RSA keys), each having a different 
host name.  The choice of which cert to use must be based on both key
type and host name required.  

From SSL's perspective, the challenge is simply to choose the right cert, 
but the server application (e.g. https) may have other things that have 
to be associate with it, such as the choice of a document root and security 
policies associated with that virtual server.  

There are at least two ways we can attempt to add this functionality to 
the SSL API, including:

1. The server registers multiple cert/key pairs with the SSL socket prior
to the handshake, and SSL chooses the right pair from among them using
the (optional) DNS name and the key type.  THe server learns which cert
was chosen by calling SSL_LocalCertificate (which is now in NSS 3.4)
after the handshake completes.  

2. The server registers a callback function, rather than any cert/key pairs 
with SSL prior to the handshake.  When the SSL code receives the client
hello, it calls the callback asking for the cert(s) and matching key(s) to 
use.  This could be done a couple of ways:

2a. When the application gets called back with the server's name, it then
registers all the certs for that name (one for each supported kea key type) 
with the socket, using SSL_ConfigSecureServer, much as it does now.  Then
SSL completes the cipher suite selection and cert selection much as it does
now.  This amouns to simply delaying the SSL_ConfigSecureServer call to a
later point in the socket's lifetime.

2b. Alternatively, when the application gets called back, it actually does
the TLS cipher suite negotiation, choosing the cipher suite and key type,
and returns just one cert/key pair, the right pair for the negotiated 
cipher suite.  This scheme gives the server maximum flexibility for 
choosing the certificate, but burdens it with a significant part of the 
TLS protocol processing.

Comments are invited.
Assignee: wtc → nelsonb
Nelson,

Option 1 limits the flexibility of server applications to do what they want
based on the DNSName field value. For example some servers may want to support
dynamic DNSNames as opposed to static. A preconfigured list of DNSNames on the
SSL socket would not allow complex things like regular expression matching,
unless NSS explicitly supported the exact algorithm that the server wants to use
to choose the security policy. I feel that it's up to the server application to
make the security policy choice and that it needs to be esented with the name
sent by the client as it happens.

Not all NSS SSL servers preconfigure the server socket with a single cert.
The requirement exists to do hardware virtual servers today, based on IP
endpoint. For example, a server with multiple IP addresses can listen on address
0.0.0.0 on port 443 . Today iWS/NES 6 will create an NSPR socket for this,
without importing it to SSL, and accept the connection on that socket. They will
then look at the local IP address of the connection, in order to determine which
security policy/indentity to use. Then and only then will they import the
*accepted* connection socket to SSL, using a model socket. This is how web
server 6 works today. It maintains a pool of model SSL sockets that are
preconfigured with certs, ciphers, and selected security protocol. Then the
accepted connection socket gets imported with that model SSL socket, and a
handshake is forced.

I think the application - in this case the web server - should control the list
of identities and how they will be selected. I don't think it can be handled by
NSS. Say for example you have a 0.0.0.0:443 listen socket. You want to support
two DNSName identities for IP address 1.1.1.1 on port 443, and two other DNSName
identities for IP address 1.1.1.2 . If you require a listen socket to be created
through NSS with SSL, then NSS has to support not only the selection of
identities based on DNSName but also based on IP address - which is really of
the application's responsibility. This is an example of why I don't like your
option 1.

You might object to that that you can have a sort of intermediate approach,
where for the hardware virtual server case with multiple IPs, the socket would
still be accepted as NSPR socket like today, then the import to SSL would
happen, with a model socket containing multiple DNSName identities, and further
down after the handshake the selected identity would be retrieved by the
application. I don't like that idea either however as I think it's unnecessary
complication to do this within NSS.

Ideally, I believe the server should maintain a model SSL socket for each of his
security policies, whether they are to be chosen based on an IP:port TCP
endpoint (hardware virtual server case) or on a different DNSName identity. This
will greatly simplify things for the application. The question remains about how
the server retrieves the DNSName identity sent by the client in order to select
its security policy.

This means I like option 2 with the callback approach.

I don't think option 2b is desirable because it requires too much knowledge of
SSL inside details by the application and I don't believe that it would add much
value to it. Basically you would be able to select a different identity for each
cipher suite, but I'm not sure that much granularity is necessary. I think one
identity per key type as is the case today is sufficient.

So I like option 2a.
I think the prototype for the callback should look like this :

PRFileDesc* GetIdentity(char* DNSName, void* appdata);

Basically, the callback would provide the server application with DNSName, as
well as some application specific data (the web server would store the IP:port
the connection was accepted on for example, to know where to branch in its
security policy tree) - and then the server would select a policy and return a
model SSL socket back to the SSL layer. The SSL layer would use the model socket
to select the certificate, private key, and cipher suites for the connection
socket, basically doing the equivalent of an SSL_ImportFD(modelsocket,
connectionsocket). This would allow the SSL handshake to proceed (serverhelo, etc).

My arguments for the above prototype is that we don't want servers to call
things like SSL_ConfigSecureServer and other SSL socket configuration calls for
each connection at runtime when choosing the identity. My tests showed in the
past that making those calls for every connection was extremely expensive,
slowing down the server by more than 50%. This was determined to be due to some
key operations. This is why the model socket approach was chosen to implement
hardware virtual servers in the current web server. I think this is equally
suited for the DNSName case.

The other change that will be required is that the handshake should be able to
begin without the SSL socket being fully configured - ie, no cert, key, or
cipher suite has been selected yet.

So the web server would do something like the following :
fd = PR_Accept( nsprlistensocket ); // same as today

PR_GetSockname(fd, &localip); // same as today, get local connection IP to do
hardware virtual servers

fd = SSL_ImportFD(NULL, fd); // make the socket SSL, but without an identity yet

SSL_OptionSet(fd, SSL_ENABLE_DNSNAME, PR_TRUE); // tells SSL layer it's OK to do
handshake with clienthelo even though no identity has been selected yet

SSL_SetDNSNameCallback(fd, identityfunction, localip); // required if
SSL_ENABLE_DNSNAME is set. So we may actually want to combine the last two lines
of code into one single API

SSL_ForceHandshake(fd); // same as today

PR_Recv(fd, ... ); // begin HTTP processing, which will cause the handshake to occur

The callback function would be something like this :

PRFileDesc* chooseidentity(char* DNSName, localip)
{
   // choose an identity based on local IP and DNSName
   // I'm not going to detail here how web server actually does it
   // because it's rather complex
   return selectedidentity;
}

I forgot to add in the proposal that the SSL layer could pass a NULL DNSName to
the callback if the client doesn't send DNSName (as is the case for all current
clients). In that case the server must choose a default SSL policy based on the
TCP endpoint as it already does today - except currently it is done by an
"connectionsocket = SSL_ImportFD(modelsocket, connectionsocket)" right after the
PR_GetSockName .
I also want to add a clarification on the "identity" model socket that the
callback is required to return. This model socket would have all the needed
certs configured for different keytypes that the server wants to support for the
given DNSName identity (or default identity if DNSName is null).
Changed the QA contact to Bishakha.
QA Contact: sonja.mirtitsch → bishakhabanerjee
FYI, the extension has been renamed to "Server name indicatoin". It is defined
in section 3.1 of draft-ietf-tls-extensions-05 , which is available at
http://www.ietf.org/internet-drafts/draft-ietf-tls-extensions-05.txt .
Summary: DNSName support in NSS → server name indication support in NSS
Summary: server name indication support in NSS → TLS server name indication support in NSS
Depends on: 226271
Summary: TLS server name indication support in NSS → TLS server name indication extension support in NSS
Regarding Perry Loirier's the patch at
https://bugzilla.mozilla.org/attachment.cgi?id=182158

I
You are unconditionally adding the SNI extension to the v3 CLIENT HELO .
We may yet discover through testing that some servers will choke on this
extension. We need to have a bit to turn this on or off, even for v3. This would
be set through SSL_OptionSet . The option name could be SSL_CLIENTHELO_SNI .
I think re-using the URL from SSL_SetURL to determine the value of the SNI
within the CLIENT HELO is fine .

II
You only implemented the client side support - you added the SNI to the CLIENT
HELO . NSS also supports server-side. For server applications, NSS needs an
interface to parse the SNI out of an incoming client hello. We should not
implement the client side of this extension in NSS without also implementing the
server side.

For the server side, we need a new callback . There has been no decision yet on
what that callback interface should be like . See comment #1 and comment #2 for
the start of the discussion .

III

Since there are several new extensions being added to standard in the CLIENT
HELO, the server might choose its identity based on the value of any CLIENT HELO
extensions, not just the SNI. There are a few ways to solve that :

a) have the callback receive all the extensions in an array, and allow the
server to return its new identity altogether in one callback :

SECStatus ExtensionsCallback(SSLExtensions** extensions, CERTCertificate**
newServerIdentity, void* arg);

libssl would provide some helper functions to let apps decode the extensions
within the callback .

b) the server could register a callback for each specific extension :
SECStatus SSL_RegisterExtensionCallback(int extensionID, ExtensionCallback*
function);

SECStatus ExtensionCallback(SSLExtensions* extension,void* arg);

But then a separate callback is needed for the server's identity decision, which
would be called after all the extensions have been parsed :

SECStatus SSL_NewServerIdentityCallback(CERTCertificate** newServerIdentity,
void* arg);

I don't like this, because the multiple callbacks mean the application is
required to use the "void* arg" for state.

I don't know enough about the format of SSL extensions to know how practical it
is for libssl to register callbacks for specific ones . Is there a "type" field
somewhere or is the format completely extension specific ?

Also, I don't know if the same extension is allowed to be passed more than once.
The second interface wouldn't work as well as the first one if this happens.

Note that this callback model may be needed on the client side as well to allow
the client to receive SERVER HELO extensions .
Sorry for not replying sooner to your comments, I've been busy with one thing or
another, but I thought I should quickly respond to these to give people an idea
of whats going on :)

I) Using SNI By default
Ok, I've been trying to do this so far without any "user visible" changes to the
API, but I can easily add something to disable this if it's unwanted in some
situations.  Although I don't understand the NSS API that well, I assumed if you
didn't want SNI, you wouldn't set the URL but a seperate option makes that much
nicer.

II: Client support only
Yes, the current patch only provides support for clients, and only if the client
isn't using v2CompatibleHello's.  The idea was to have small easy to understand
and verify patches.  I'm still working on a patch that can use client side SNI
even when v2CompatibleHello's are in use.  Also, as this is probably my first
patch to get committed, early submission of a preliminary patch means I get
useful feedback like this before I spend too much time building a giant castle
on the wrong hill.

III: SNI callbacks for server side

As for how SNI would work server side, I'd suggest asking Paul Querna who has
been working on this for apache using gnutls, his experience is likely to be
useful here.  Paul, what are your thoughts on a nice server side API for SNI?

RFC 3546, Section 2,3 says "There MUST NOT be more than one extension of the
same type."  Each type is identified by an unsigned 16 bit value.

As the extensions either change the certificate used or modify the security of
the connection (eg truncated HMACs), I'd suggest just having a second version of
the SSL_AuthCertificate callback that also gets passed what extensions are in
use.  IMHO.
In reply to comment 8,
> I assumed if you didn't want SNI, you wouldn't set the URL 

SSL Clients must specify the DNSname in the misnamed "URL" because it is 
necessary for validating the DNSname in the server's cert to avoid MITM attacks.

Let me restate here my remark in bug 116169 comment 19 :

I oppose checking in code that needs to be replaced shortly therafter.  
It appears to me that the proposed patch to implement this single extension 
would need to be entirely replaced as soon as a second extension is added.  
I'd rather see an extensible multi-extension design from the beginning. 
I don't mean to discourage you, Perry, but I don't think we can take the 
patch you attached to bug 116169, for that reason.  It's not a problem,
BTW, to add new NSS API functions as needed.  But once added, they must
be supported effectively forever therafter, so they need to be well thought
out before being introduced.  
QA Contact: bishakhabanerjee → jason.m.reid
This will be included in the upcoming IE 7: 
http://blogs.msdn.com/ie/archive/2005/10/22/483795.aspx
The target milestone for this bug was originally set to NSS 4.0 back in 2001. There is no longer a plan for such a release. Given the growing importance of the TLS SNI extension, I'm resetting the target tentatively to 3.12 .
Target Milestone: 4.0 → 3.12
Nominating to block Firefox 2.  It would be lame if Firefox was the only major browser not supporting TLS SNI and prevented sites that share IPs from using https. (On DreamHost at least, I think the cost of a non-shared IP addresses is the largest part of the cost of using https.)
Flags: blocking1.8.1?
No reviews requested at this time.  Just capturing some hacks.
Attached patch More exploratory work (obsolete) — Splinter Review
Attachment #216510 - Attachment is obsolete: true
QA Contact: jason.m.reid → libraries
Comment on attachment 216703 [details] [diff] [review]
More exploratory work

superseded by patch attached to bug 226271
Attachment #216703 - Attachment is obsolete: true
Blocker for 1.8.1.
Flags: blocking1.8.1? → blocking1.8.1+
This work is now done, and checked in on the trunk and the NSS_3_11_BRANCH.
The work was mostly done as part of bug 226271.  
A new CVS tag needs to be made so that mozilla clients can pick it up. 
I expec that will happen this week.
Status: NEW → RESOLVED
Closed: 18 years ago
Resolution: --- → FIXED
Target Milestone: 3.12 → 3.11.1
Nelson, is this fixed on the 1.8.1 branch as well? If so, can you please add fixed1.8.1 to the keyword list?
The necessary NSS code is also on the MOZILLA_1_8_BRANCH. 
To use it, the browser must disable SSL2-format client hellos.
Bug 116169 tracks that work.
I think it is done, but bug 116169 does not clearly so indicate.
Keywords: fixed1.8.1
Wondering if comment #3 is applicable for me? I do not want to call SSL_ConfigureServer for performance reasons as Julien stated.
Meena,

Only the client side of server name indication was added to NSS. At least that's all that was discussed in this bug. I don't believe the server side work was ever completed.
Thanx. I suspected that code for

"SSL_OptionSet(fd, SSL_ENABLE_DNSNAME, PR_TRUE); // tells SSL layer it's OK to do handshake with clienthello even though no identity has been selected yet"

is not written. Just confirmed. 

I am now planning to call SSL_ReconfigFD as shown in
https://bugzilla.mozilla.org/show_bug.cgi?id=631986 

Hope that SSL_ReconfigFD is not so bad for performance. Especially CERT_DestroyCertificate, CERT_DupCertificate, CERT_DestroyCertificateList,  CERT_DupCertList, etc
Meena: the server side of SNI was implemented in bug 360421.  This
bug is about the client side.

It is best to ask questions about SSL_ReconfigFD in the Mozilla
crypto newsgroup, and cc Alexei Volkov (who wrote the server
side SNI code).
Yes. Alexei only told me yesterday to use SSL_ReconfigFD.
You need to log in before you can comment on or make changes to this bug.