7

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?

jww
  • 97,681
  • 90
  • 411
  • 885
M.V.
  • 85
  • 1
  • 4
  • 1
    possible duplicate of [How to implement Server Name Indication(SNI) on OpenSSL in C or C++?](http://stackoverflow.com/questions/5113333/how-to-implement-server-name-indicationsni-on-openssl-in-c-or-c) – jww Mar 13 '14 at 10:20
  • actually I read that thread, but the answer there wasn't clear enough for me, and I couldn't make it work with that. – M.V. Mar 15 '14 at 08:21
  • Yeah, you're right. It is lite on information. – jww Mar 15 '14 at 14:48

1 Answers1

12

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.

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 your get_ssl_servername_cb is not called, issue:

  • openssl s_client -connect localhost:8443 -ssl3 # SNI added at TLSv1
  • openssl s_client -connect localhost:8443 -tls1 # Windows XP client

To 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 on localhost):

printf "GET / HTTP/1.1\r\n\r\n" | /usr/local/ssl/bin/openssl s_client \
    -connect localhost:8443 -tls1 -servername localhost \
    -CAfile pki/signing-dss-cert.pem 

so my question is, how can I use "SSL_CTX_set_tlsext_servername_callback" function for SNI?

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 with SSL_CTX_set_tlsext_servername_callback), you examine the server name. One of two situations occur: you already have a SSL_CTX for the server's name, or you need to create a SSL_CTX for server's name.

Once you fetch the SSL_CTX from cache or create a new SSL_CTX, you then use SSL_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 for s_server.c (in <openssl dir>/apps/s_server.c). Follow the trail of ctx2,

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 needed SSL_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 the SSL_CTX so the OpenSSL library does not delete it from under the app).

static int ServerNameCallback(SSL *ssl, int *ad, void *arg)
{
    UNUSED(ad);
    UNUSED(arg);

    ASSERT(ssl);
    if (ssl == NULL)
        return SSL_TLSEXT_ERR_NOACK;

    const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
    ASSERT(servername && servername[0]);
    if (!servername || servername[0] == '\0')
        return SSL_TLSEXT_ERR_NOACK;

    /* Does the default cert already handle this domain? */
    if (IsDomainInDefCert(servername))
        return SSL_TLSEXT_ERR_OK;

    /* Need a new certificate for this domain */
    SSL_CTX* ctx = GetServerContext(servername);
    ASSERT(ctx != NULL);
    if (ctx == NULL)
        return SSL_TLSEXT_ERR_NOACK;   

    /* Useless return value */
    SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
    ASSERT(v == ctx);
    if (v != ctx)   
        return SSL_TLSEXT_ERR_NOACK;

    return SSL_TLSEXT_ERR_OK;
}

In the code above, ad and arg are unused parameters. I don't know what ad does because I don't use it. arg can be used to pass in a context to the callback. I don't use arg either, but s_server.c uses it to print some debug information (the arg is a pointer to a BIOs tied to stderr (and a few others), IIRC).


For completeness, SSL_CTX are reference counted and they can be re-used. A newly created SSL_CTX has a count of 1, which is delegated to the OpenSSL internal caching mechanism. When you hand the SSL_CTX to a SSL object, the count increments to 2. When the SSL object calls SSL_CTX_free on the SSL_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.

Community
  • 1
  • 1
jww
  • 97,681
  • 90
  • 411
  • 885
  • thank you for your perfect and complete answer. I managed to use SNI based on your answer. that was great :) – M.V. Mar 15 '14 at 08:31
  • Thank you very much for all this! What should I make of the comment that is attached to the [other answer](http://stackoverflow.com/a/5113466/1613573) regarding thread (non-)safety of `SSL_set_SSL_CTX`? I have a multi-threaded server and would like to swap contexts exactly as you have shown. My server uses one thread per client connection, and each client can use a different host name. Hence, swapping of contexts can occur in any of the threads. Can changing the context in this way lead to any problems, i.e., in what sense can other threads be affected by this operation? Thank you very much! – mat Jul 25 '16 at 14:36
  • I have now implemented SNI successfully, also based on your answer, so first of all, thank you very much for this detailed description and careful explanation! I am now looking into some of the details of your answer, and I think in your example, `GetServerContext` should **not** increment the reference count by one (as you say in the text): This will be done by OpenSSL in `SSL_set_SSL_CTX`. If your own `GetServerContext` also increases the reference count, you will have one too many. This can prevent OpenSSL from reclaiming the context when it is actually no longer needed. What do you think? – mat Nov 23 '16 at 21:27
  • @mat - Its been a while since I worked with the code so I'm working from a fading memory... I seem to recall I needed to maintain a list of contexts. To know when I could remove the context from my list, I needed to keep a count. `SSL_CTX` will still be referenced counted By OpenSSL, and the OpenSSL counts are incremented and decremented as expected. However, you don't have access to the OpenSSL internal reference count, so you need to keep an external one. – jww Nov 23 '16 at 21:33
  • OK I see, this is an interesting use case. What is currently at odds with this external count is the statement "so the OpenSSL library does not delete it from under the app". This cannot happen in this example (I think), and if it could, then increasing an external reference count could not prevent it, since OpenSSL would not know about it. I am not trying to nitpick on your excellent answer, I only want to ascertain that I get the details right, and simply add these considerations and discuss them a bit with you to clarify the somewhat more subtle aspects of your answer. – mat Nov 23 '16 at 21:49
  • @mat - Yeah, my use case was an interception proxy, so OpenSSL server contexts could be reused for multiple clients. Feel free to make an edit once you get into your implementation. I'll be happy approve it. – jww Nov 23 '16 at 21:59
  • Thank you, but I can impossibly edit this text, my companion during many hours of studying OpenSSL in the past few months ;-) – mat Nov 23 '16 at 22:07
  • For a similar implementation samples, check out https://cpp.hotexamples.com/examples/-/-/SSL_set_SSL_CTX/cpp-ssl_set_ssl_ctx-function-examples.html – Arnaud Bouchez Sep 22 '22 at 10:07