I'm using OpenSSL 0.9.8q in FreeBSD-8.2. I have 3 virtual hosts on my system and want to implement SNI to serve for all 3 of them in one server.
I have 3 separate certificates one for each, and in my ssl-server code I have to somehow find out what is the domain-name of client's request, and use the appropriate certificate file based on that. For this I wrote a function named get_ssl_servername_cb
and passed it as callback function to SSL_CTX_set_tlsext_servername_callback
. This way, in callback function I can get the the domain-name of the client's request.
But my problem is, this callback function is being executed after execution of SSL_accept
function, but I have to choose and use the appropriate certificate before using SSL_new
command, which is way before execution of SSL_accept
.
So my question is, how can I use SSL_CTX_set_tlsext_servername_callback
function for SNI?
When you start your server, you provide a default
SSL_CTX
. This is used for non-SNI clients, like SSLv3 clients and TLS clients that don't utilize SNI (like Windows XP). This is needed because the callback is not invoked in this situation.Here are some examples to tickle the behavior using OpenSSL's
s_client
. To simulate a non-SNI client so that yourget_ssl_servername_cb
is not called, issue:openssl s_client -connect localhost:8443 -ssl3
# SNI added at TLSv1openssl s_client -connect localhost:8443 -tls1
# Windows XP clientTo simulate a SNI client so that your
get_ssl_servername_cb
is called, issue:openssl s_client -connect localhost:8443 -tls1 -servername localhost
You can also avoid the certificate verification errors by adding
-CAfile
. This is from one of my test scripts (for testing DSS/DSA certificates onlocalhost
):See the OpenSSL source code at
<openssl dir>/apps/s_server.c
; or see How to implement Server Name Indication(SNI) on OpenSSL in C or C++?.In your
get_ssl_servername_cb
(set withSSL_CTX_set_tlsext_servername_callback
), you examine the server name. One of two situations occur: you already have aSSL_CTX
for the server's name, or you need to create aSSL_CTX
for server's name.Once you fetch the
SSL_CTX
from cache or create a newSSL_CTX
, you then useSSL_set_SSL_CTX
to swap in the context. There's an example of swapping in the new context in the OpenSSL source files. See the code fors_server.c
(in<openssl dir>/apps/s_server.c
). Follow the trail ofctx2
,Here's what it looks like in one of my projects.
IsDomainInDefaultCert
determines if the requested server name is provided by the default server certificate. If not,GetServerContext
fetches the neededSSL_CTX
.GetServerContext
pulls the needed certificate out of an app-level cache; or creates it and puts it in the app-level cache (GetServerContext
also asserts one reference count on theSSL_CTX
so the OpenSSL library does not delete it from under the app).In the code above,
ad
andarg
are unused parameters. I don't know whatad
does because I don't use it.arg
can be used to pass in a context to the callback. I don't usearg
either, buts_server.c
uses it to print some debug information (thearg
is a pointer to aBIO
s tied tostderr
(and a few others), IIRC).For completeness,
SSL_CTX
are reference counted and they can be re-used. A newly createdSSL_CTX
has a count of 1, which is delegated to the OpenSSL internal caching mechanism. When you hand theSSL_CTX
to aSSL
object, the count increments to 2. When theSSL
object callsSSL_CTX_free
on theSSL_CTX
, the function will decrement the reference count. If the context is expired and the reference count is 1, then the OpenSSL library will delete it from its internal cache.