For those who are interested, the following diff adds server side support
for SNI to libtls. There are three additional functions:

tls_config_add_keypair_file()
tls_config_add_keypair_mem()
tls_conninfo_servername()

The first two allow you to add additional certificates/private keys that will
be used if the client sends a TLS servername extension that matches one
of the SANs. The third function returns the TLS servername extension that
the client specified.

Index: tls.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls.c,v
retrieving revision 1.45
diff -u -p -r1.45 tls.c
--- tls.c       13 Aug 2016 13:05:51 -0000      1.45
+++ tls.c       13 Aug 2016 17:58:02 -0000
@@ -177,6 +177,24 @@ tls_set_errorx(struct tls *ctx, const ch
        return (rv);
 }
 
+struct tls_sni_ctx *
+tls_sni_ctx_new(void)
+{
+       return (calloc(1, sizeof(struct tls_sni_ctx)));
+}
+
+void
+tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx)
+{
+       if (sni_ctx == NULL)
+               return;
+
+       SSL_CTX_free(sni_ctx->ssl_ctx);
+       X509_free(sni_ctx->ssl_cert);
+
+       free(sni_ctx);
+}
+
 struct tls *
 tls_new(void)
 {
@@ -207,7 +225,7 @@ tls_configure(struct tls *ctx, struct tl
 }
 
 int
-tls_configure_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
+tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
     struct tls_keypair *keypair, int required)
 {
        EVP_PKEY *pkey = NULL;
@@ -274,27 +292,27 @@ tls_configure_keypair(struct tls *ctx, S
 }
 
 int
-tls_configure_ssl(struct tls *ctx)
+tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx)
 {
-       SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
-       SSL_CTX_set_mode(ctx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+       SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+       SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 
-       SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
-       SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv3);
+       SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2);
+       SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3);
 
-       SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
-       SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
-       SSL_CTX_clear_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);
+       SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1);
+       SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
+       SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
 
        if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_0) == 0)
-               SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1);
+               SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1);
        if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_1) == 0)
-               SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_1);
+               SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1);
        if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0)
-               SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_TLSv1_2);
+               SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2);
 
        if (ctx->config->alpn != NULL) {
-               if (SSL_CTX_set_alpn_protos(ctx->ssl_ctx, ctx->config->alpn,
+               if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn,
                    ctx->config->alpn_len) != 0) {
                        tls_set_errorx(ctx, "failed to set alpn");
                        goto err;
@@ -302,7 +320,7 @@ tls_configure_ssl(struct tls *ctx)
        }
 
        if (ctx->config->ciphers != NULL) {
-               if (SSL_CTX_set_cipher_list(ctx->ssl_ctx,
+               if (SSL_CTX_set_cipher_list(ssl_ctx,
                    ctx->config->ciphers) != 1) {
                        tls_set_errorx(ctx, "failed to set ciphers");
                        goto err;
@@ -310,7 +328,7 @@ tls_configure_ssl(struct tls *ctx)
        }
 
        if (ctx->config->verify_time == 0) {
-               X509_VERIFY_PARAM_set_flags(ctx->ssl_ctx->param,
+               X509_VERIFY_PARAM_set_flags(ssl_ctx->param,
                    X509_V_FLAG_NO_CHECK_TIME);
        }
 
@@ -321,13 +339,13 @@ tls_configure_ssl(struct tls *ctx)
 }
 
 int
-tls_configure_ssl_verify(struct tls *ctx, int verify)
+tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify)
 {
        size_t ca_len = ctx->config->ca_len;
        char *ca_mem = ctx->config->ca_mem;
        char *ca_free = NULL;
 
-       SSL_CTX_set_verify(ctx->ssl_ctx, verify, NULL);
+       SSL_CTX_set_verify(ssl_ctx, verify, NULL);
 
        /* If no CA has been specified, attempt to load the default. */
        if (ctx->config->ca_mem == NULL && ctx->config->ca_path == NULL) {
@@ -342,19 +360,17 @@ tls_configure_ssl_verify(struct tls *ctx
                        tls_set_errorx(ctx, "ca too long");
                        goto err;
                }
-               if (SSL_CTX_load_verify_mem(ctx->ssl_ctx, ca_mem,
-                   ca_len) != 1) {
+               if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) {
                        tls_set_errorx(ctx, "ssl verify memory setup failure");
                        goto err;
                }
-       } else if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, NULL,
+       } else if (SSL_CTX_load_verify_locations(ssl_ctx, NULL,
            ctx->config->ca_path) != 1) {
                tls_set_errorx(ctx, "ssl verify locations failure");
                goto err;
        }
        if (ctx->config->verify_depth >= 0)
-               SSL_CTX_set_verify_depth(ctx->ssl_ctx,
-                   ctx->config->verify_depth);
+               SSL_CTX_set_verify_depth(ssl_ctx, ctx->config->verify_depth);
 
        free(ca_free);
 
@@ -378,6 +394,8 @@ tls_free(struct tls *ctx)
 void
 tls_reset(struct tls *ctx)
 {
+       struct tls_sni_ctx *sni, *nsni;
+
        SSL_CTX_free(ctx->ssl_ctx);
        SSL_free(ctx->ssl_conn);
        X509_free(ctx->ssl_peer_cert);
@@ -399,6 +417,12 @@ tls_reset(struct tls *ctx)
        tls_free_conninfo(ctx->conninfo);
        free(ctx->conninfo);
        ctx->conninfo = NULL;
+
+       for (sni = ctx->sni_ctx; sni != NULL; sni = nsni) {
+               nsni = sni->next;
+               tls_sni_ctx_free(sni);
+       }
+       ctx->sni_ctx = NULL;
 }
 
 int
Index: tls.h
===================================================================
RCS file: /cvs/src/lib/libtls/tls.h,v
retrieving revision 1.33
diff -u -p -r1.33 tls.h
--- tls.h       12 Aug 2016 15:10:59 -0000      1.33
+++ tls.h       13 Aug 2016 17:58:02 -0000
@@ -52,6 +52,11 @@ const char *tls_error(struct tls *_ctx);
 struct tls_config *tls_config_new(void);
 void tls_config_free(struct tls_config *_config);
 
+int tls_config_add_keypair_file(struct tls_config *_config,
+    const char *_cert_file, const char *_key_file);
+int tls_config_add_keypair_mem(struct tls_config *_config, const uint8_t 
*_cert,
+    size_t _cert_len, const uint8_t *_key, size_t _key_len);
+
 int tls_config_set_alpn(struct tls_config *_config, const char *_alpn);
 int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file);
 int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path);
@@ -119,6 +124,7 @@ time_t      tls_peer_cert_notafter(struct tls
 
 const char *tls_conn_alpn_selected(struct tls *_ctx);
 const char *tls_conn_cipher(struct tls *_ctx);
+const char *tls_conn_servername(struct tls *_ctx);
 const char *tls_conn_version(struct tls *_ctx);
 
 uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
Index: tls_client.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_client.c,v
retrieving revision 1.33
diff -u -p -r1.33 tls_client.c
--- tls_client.c        28 Apr 2016 17:05:59 -0000      1.33
+++ tls_client.c        13 Aug 2016 17:58:02 -0000
@@ -193,9 +193,10 @@ tls_connect_fds(struct tls *ctx, int fd_
                goto err;
        }
 
-       if (tls_configure_ssl(ctx) != 0)
+       if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0)
                goto err;
-       if (tls_configure_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 0) 
!= 0)
+       if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx,
+           ctx->config->keypair, 0) != 0)
                goto err;
 
        if (ctx->config->verify_name) {
@@ -204,9 +205,9 @@ tls_connect_fds(struct tls *ctx, int fd_
                        goto err;
                }
        }
-
        if (ctx->config->verify_cert &&
-           (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1))
+           (tls_configure_ssl_verify(ctx, ctx->ssl_ctx,
+            SSL_VERIFY_PEER) == -1))
                goto err;
 
        if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
Index: tls_config.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_config.c,v
retrieving revision 1.27
diff -u -p -r1.27 tls_config.c
--- tls_config.c        13 Aug 2016 13:15:53 -0000      1.27
+++ tls_config.c        13 Aug 2016 17:58:02 -0000
@@ -227,6 +227,18 @@ tls_config_free(struct tls_config *confi
        free(config);
 }
 
+static void
+tls_config_keypair_add(struct tls_config *config, struct tls_keypair *keypair)
+{
+       struct tls_keypair *kp;
+
+       kp = config->keypair;
+       while (kp->next != NULL)
+               kp = kp->next;
+
+       kp->next = keypair;
+}
+
 const char *
 tls_config_error(struct tls_config *config)
 {
@@ -367,6 +379,50 @@ tls_config_set_alpn(struct tls_config *c
 {
        return tls_config_parse_alpn(config, alpn, &config->alpn,
            &config->alpn_len);
+}
+
+int
+tls_config_add_keypair_file(struct tls_config *config,
+    const char *cert_file, const char *key_file)
+{
+       struct tls_keypair *keypair;
+
+       if ((keypair = tls_keypair_new()) == NULL)
+               return (-1);
+       if (tls_keypair_set_cert_file(keypair, &config->error, cert_file) != 0)
+               goto err;
+       if (tls_keypair_set_key_file(keypair, &config->error, key_file) != 0)
+               goto err;
+
+       tls_config_keypair_add(config, keypair);
+
+       return (0);
+
+ err:
+       tls_keypair_free(keypair);
+       return (-1);
+}
+
+int
+tls_config_add_keypair_mem(struct tls_config *config, const uint8_t *cert,
+    size_t cert_len, const uint8_t *key, size_t key_len)
+{
+       struct tls_keypair *keypair;
+
+       if ((keypair = tls_keypair_new()) == NULL)
+               return (-1);
+       if (tls_keypair_set_cert_mem(keypair, cert, cert_len) != 0)
+               goto err;
+       if (tls_keypair_set_key_mem(keypair, key, key_len) != 0)
+               goto err;
+
+       tls_config_keypair_add(config, keypair);
+
+       return (0);
+
+ err:
+       tls_keypair_free(keypair);
+       return (-1);
 }
 
 int
Index: tls_conninfo.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_conninfo.c,v
retrieving revision 1.8
diff -u -p -r1.8 tls_conninfo.c
--- tls_conninfo.c      12 Aug 2016 15:10:59 -0000      1.8
+++ tls_conninfo.c      13 Aug 2016 17:58:02 -0000
@@ -171,8 +171,10 @@ tls_conninfo_alpn_proto(struct tls *ctx)
 }
 
 int
-tls_get_conninfo(struct tls *ctx) {
+tls_get_conninfo(struct tls *ctx)
+{
        const char * tmp;
+
        if (ctx->ssl_peer_cert != NULL) {
                if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1)
                        goto err;
@@ -185,17 +187,26 @@ tls_get_conninfo(struct tls *ctx) {
                    &ctx->conninfo->notafter) == -1)
                        goto err;
        }
-       if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL)
-               goto err;
-       ctx->conninfo->version = strdup(tmp);
-       if (ctx->conninfo->version == NULL)
+
+       if (tls_conninfo_alpn_proto(ctx) == -1)
                goto err;
+
        if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL)
                goto err;
        ctx->conninfo->cipher = strdup(tmp);
        if (ctx->conninfo->cipher == NULL)
                goto err;
-       if (tls_conninfo_alpn_proto(ctx) == -1)
+
+       if (ctx->servername != NULL) {
+               if ((ctx->conninfo->servername =
+                   strdup(ctx->servername)) == NULL)
+                       goto err;
+       }
+
+       if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL)
+               goto err;
+       ctx->conninfo->version = strdup(tmp);
+       if (ctx->conninfo->version == NULL)
                goto err;
 
        return (0);
@@ -205,21 +216,26 @@ err:
 }
 
 void
-tls_free_conninfo(struct tls_conninfo *conninfo) {
-       if (conninfo != NULL) {
-               free(conninfo->alpn);
-               conninfo->alpn = NULL;
-               free(conninfo->hash);
-               conninfo->hash = NULL;
-               free(conninfo->subject);
-               conninfo->subject = NULL;
-               free(conninfo->issuer);
-               conninfo->issuer = NULL;
-               free(conninfo->version);
-               conninfo->version = NULL;
-               free(conninfo->cipher);
-               conninfo->cipher = NULL;
-       }
+tls_free_conninfo(struct tls_conninfo *conninfo)
+{
+       if (conninfo == NULL)
+               return;
+
+       free(conninfo->alpn);
+       conninfo->alpn = NULL;
+       free(conninfo->cipher);
+       conninfo->cipher = NULL;
+       free(conninfo->servername);
+       conninfo->servername = NULL;
+       free(conninfo->version);
+       conninfo->version = NULL;
+
+       free(conninfo->hash);
+       conninfo->hash = NULL;
+       free(conninfo->issuer);
+       conninfo->issuer = NULL;
+       free(conninfo->subject);
+       conninfo->subject = NULL;
 }
 
 const char *
@@ -238,6 +254,14 @@ tls_conn_cipher(struct tls *ctx)
        return (ctx->conninfo->cipher);
 }
 
+const char *
+tls_conn_servername(struct tls *ctx)
+{
+       if (ctx->conninfo == NULL)
+               return (NULL);
+       return (ctx->conninfo->servername);
+}
+ 
 const char *
 tls_conn_version(struct tls *ctx)
 {
Index: tls_init.3
===================================================================
RCS file: /cvs/src/lib/libtls/tls_init.3,v
retrieving revision 1.65
diff -u -p -r1.65 tls_init.3
--- tls_init.3  12 Aug 2016 15:10:59 -0000      1.65
+++ tls_init.3  13 Aug 2016 17:58:02 -0000
@@ -24,6 +24,8 @@
 .Nm tls_config_new ,
 .Nm tls_config_free ,
 .Nm tls_config_parse_protocols ,
+.Nm tls_config_add_keypair_file ,
+.Nm tls_config_add_keypair_mem ,
 .Nm tls_config_set_alpn ,
 .Nm tls_config_set_ca_file ,
 .Nm tls_config_set_ca_path ,
@@ -57,6 +59,7 @@
 .Nm tls_peer_cert_notafter ,
 .Nm tls_conn_alpn_selected ,
 .Nm tls_conn_cipher ,
+.Nm tls_conn_servername ,
 .Nm tls_conn_version ,
 .Nm tls_load_file ,
 .Nm tls_client ,
@@ -90,6 +93,10 @@
 .Ft "int"
 .Fn tls_config_parse_protocols "uint32_t *protocols" "const char *protostr"
 .Ft "int"
+.Fn tls_config_add_keypair_file "struct tls_config *config" "const char 
*cert_file" "const char *key_file"
+.Ft "int"
+.Fn tls_config_add_keypair_mem "struct tls_config *config" "const uint8_t 
*cert" "size_t cert_len" "const uint8_t *key" "size_t key_len"
+.Ft "int"
 .Fn tls_config_set_alpn "struct tls_config *config" "const char *alpn"
 .Ft "int"
 .Fn tls_config_set_ca_file "struct tls_config *config" "const char *ca_file"
@@ -301,6 +308,16 @@ The following functions modify a configu
 Configuration options may apply to only clients or only servers or both.
 .Bl -bullet -offset four
 .It
+.Fn tls_config_add_keypair_file
+adds an additional public certificate and private key from the specified files.
+This will be used as an alternative certificate for Server Name Indication.
+.Em (Server)
+.It
+.Fn tls_config_set_keypair_mem
+adds an additional public certificate and private key from memory.
+This will be used as an alternative certificate for Server Name Indication.
+.Em (Server)
+.It
 .Fn tls_config_set_alpn
 sets the ALPN protocols that are supported.
 The alpn string is a comma separated list of protocols, in order of preference.
@@ -425,13 +442,41 @@ a certificate.
 enables client certificate verification, without requiring the client
 to send a certificate.
 .Em (Server)
+.El
+.Pp
+The following functions return information about a TLS connection.
+These functions will only succeed after the handshake is complete.
+Connection information may apply to only clients or only servers or both.
+.Bl -bullet -offset four
+.It
+.Fn tls_conn_alpn_selected
+returns a string that specifies the ALPN protocol selected for use with the 
peer
+connected to
+.Ar ctx .
+If no protocol was selected then NULL is returned.
+.Em (Server and Client)
+.It
+.Fn tls_conn_cipher
+returns a string corresponding to the cipher suite negotiated with the peer
+connected to
+.Ar ctx .
+.Em (Server and client)
+.It
+.Fn tls_conn_servername
+returns a string corresponding to the servername that the client requested
+via a TLS Server Name Indication extension, when connecting via
+.Ar ctx .
+.Em (Server)
+.It
+.Fn tls_conn_version
+returns a string corresponding to a TLS version negotiated with the peer
+connected to
+.Ar ctx .
 .It
 .Fn tls_peer_cert_provided
 checks if the peer of
 .Ar ctx
 has provided a certificate.
-.Fn tls_peer_cert_provided
-can only succeed after the handshake is complete.
 .Em (Server and client)
 .It
 .Fn tls_peer_cert_contains_name
@@ -440,24 +485,18 @@ checks if the peer of a TLS
 has provided a certificate that contains a
 SAN or CN that matches
 .Ar name .
-.Fn tls_peer_cert_contains_name
-can only succeed after the handshake is complete.
 .Em (Server and client)
 .It
 .Fn tls_peer_cert_subject
 returns a string
 corresponding to the subject of the peer certificate from
 .Ar ctx .
-.Fn tls_peer_cert_subject
-will only succeed after the handshake is complete.
 .Em (Server and client)
 .It
 .Fn tls_peer_cert_issuer
 returns a string
 corresponding to the issuer of the peer certificate from
 .Ar ctx .
-.Fn tls_peer_cert_issuer
-will only succeed after the handshake is complete.
 .Em (Server and client)
 .It
 .Fn tls_peer_cert_hash
@@ -479,43 +518,17 @@ printf "SHA256:${h}\\n"
 returns the time corresponding to the start of the validity period of
 the peer certificate from
 .Ar ctx .
-.Fn tls_peer_cert_notbefore
-will only succeed after the handshake is complete.
 .Em (Server and client)
 .It
 .Fn tls_peer_cert_notafter
 returns the time corresponding to the end of the validity period of
 the peer certificate from
 .Ar ctx .
-.Fn tls_peer_cert_notafter
-will only succeed after the handshake is complete.
 .Em (Server and client)
-.It
-.Fn tls_conn_alpn_selected
-returns a string that specifies the ALPN protocol selected for use with the 
peer
-connected to
-.Ar ctx .
-If no protocol was selected then NULL is returned.
-.Fn tls_conn_alpn_selected
-will only succeed after the handshake is complete.
-.Em (Server and Client)
-.It
-.Fn tls_conn_cipher
-returns a string
-corresponding to the cipher suite negotiated with the peer
-connected to
-.Ar ctx .
-.Fn tls_conn_cipher
-will only succeed after the handshake is complete.
-.Em (Server and client)
-.It
-.Fn tls_conn_version
-returns a string
-corresponding to a TLS version negotiated with the peer
-connected to
-.Ar ctx .
-.Fn tls_conn_version
-will only succeed after the handshake is complete.
+.El
+.Pp
+The following are TLS related utility functions.
+.Bl -bullet -offset four
 .It
 .Fn tls_load_file
 loads a certificate or key from disk into memory to be loaded with
Index: tls_internal.h
===================================================================
RCS file: /cvs/src/lib/libtls/tls_internal.h,v
retrieving revision 1.36
diff -u -p -r1.36 tls_internal.h
--- tls_internal.h      13 Aug 2016 13:05:51 -0000      1.36
+++ tls_internal.h      13 Aug 2016 17:58:02 -0000
@@ -73,13 +73,15 @@ struct tls_config {
 
 struct tls_conninfo {
        char *alpn;
+       char *cipher;
+       char *servername;
+       char *version;
+
+       char *hash;
        char *issuer;
        char *subject;
-       char *hash;
        char *serial;
-       char *fingerprint;
-       char *version;
-       char *cipher;
+
        time_t notbefore;
        time_t notafter;
 };
@@ -91,6 +93,13 @@ struct tls_conninfo {
 #define TLS_EOF_NO_CLOSE_NOTIFY        (1 << 0)
 #define TLS_HANDSHAKE_COMPLETE (1 << 1)
 
+struct tls_sni_ctx {
+       struct tls_sni_ctx *next;
+
+       SSL_CTX *ssl_ctx;
+       X509 *ssl_cert;
+};
+
 struct tls {
        struct tls_config *config;
        struct tls_error error;
@@ -103,20 +112,28 @@ struct tls {
 
        SSL *ssl_conn;
        SSL_CTX *ssl_ctx;
+
+       struct tls_sni_ctx *sni_ctx;
+
        X509 *ssl_peer_cert;
 
        struct tls_conninfo *conninfo;
 };
 
+struct tls_sni_ctx *tls_sni_ctx_new(void);
+void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx);
+
 struct tls *tls_new(void);
 struct tls *tls_server_conn(struct tls *ctx);
 
+int tls_check_servername(struct tls *ctx, X509 *cert, const char *servername);
 int tls_check_name(struct tls *ctx, X509 *cert, const char *servername);
-int tls_configure_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
-    struct tls_keypair *keypair, int required);
 int tls_configure_server(struct tls *ctx);
-int tls_configure_ssl(struct tls *ctx);
-int tls_configure_ssl_verify(struct tls *ctx, int verify);
+
+int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx);
+int tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx,
+    struct tls_keypair *keypair, int required);
+int tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify);
 
 int tls_handshake_client(struct tls *ctx);
 int tls_handshake_server(struct tls *ctx);
Index: tls_server.c
===================================================================
RCS file: /cvs/src/lib/libtls/tls_server.c,v
retrieving revision 1.22
diff -u -p -r1.22 tls_server.c
--- tls_server.c        12 Aug 2016 15:10:59 -0000      1.22
+++ tls_server.c        13 Aug 2016 17:58:02 -0000
@@ -15,6 +15,10 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+
 #include <openssl/ec.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
@@ -49,8 +53,9 @@ tls_server_conn(struct tls *ctx)
 }
 
 static int
-tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
-    const unsigned char *in, unsigned int inlen, void *arg)
+tls_server_alpn_callback(SSL *ssl, const unsigned char **out,
+    unsigned char *outlen, const unsigned char *in, unsigned int inlen,
+    void *arg)
 {
        struct tls *ctx = arg;
 
@@ -62,53 +67,150 @@ tls_server_alpn_cb(SSL *ssl, const unsig
        return (SSL_TLSEXT_ERR_NOACK);
 }
 
-int
-tls_configure_server(struct tls *ctx)
+static int
+tls_servername_callback(SSL *ssl, int *al, void *arg)
+{
+       struct tls *ctx = (struct tls *)arg;
+       struct tls_sni_ctx *sni_ctx;
+       union tls_addr addrbuf;
+       struct tls *conn_ctx;
+       const char *name;
+
+       if ((conn_ctx = SSL_get_app_data(ssl)) == NULL)
+               goto err;
+
+       if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) == 
NULL) {
+               /*
+                * The servername callback gets called even when there is no
+                * TLS servername extension provided by the client. Sigh!
+                */
+               return (SSL_TLSEXT_ERR_NOACK);
+       }
+
+       /* Per RFC 6066 section 3: ensure that name is not an IP literal. */
+       if (inet_pton(AF_INET, name, &addrbuf) == 1 ||
+            inet_pton(AF_INET6, name, &addrbuf) == 1)
+               goto err;
+
+       free((char *)conn_ctx->servername);
+       if ((conn_ctx->servername = strdup(name)) == NULL)
+               goto err;
+
+       /* Find appropriate SSL context for requested servername. */
+       for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) {
+               if (tls_check_name(ctx, sni_ctx->ssl_cert, name) == 0) {
+                       SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx);
+                       return (SSL_TLSEXT_ERR_OK);
+               }
+       }
+
+       /* No match, use the existing context/certificate. */
+       return (SSL_TLSEXT_ERR_OK);
+
+ err:
+       /*
+        * There is no way to tell libssl that an internal failure occurred.
+        * The only option we have is to return a fatal alert.
+        */
+       *al = TLS1_AD_INTERNAL_ERROR;
+       return (SSL_TLSEXT_ERR_ALERT_FATAL);
+}
+
+static int
+tls_keypair_load_cert(struct tls_keypair *keypair, struct tls_error *error,
+    X509 **cert)
+{
+       char *errstr = "unknown";
+       BIO *cert_bio = NULL;
+       int ssl_err;
+
+       X509_free(*cert);
+       *cert = NULL;
+
+       if (keypair->cert_mem == NULL) {
+               tls_error_set(error, "keypair has no certificate");
+               goto err;
+       }
+       if ((cert_bio = BIO_new_mem_buf(keypair->cert_mem,
+           keypair->cert_len)) == NULL) {
+               tls_error_set(error, "failed to create certificate bio");
+               goto err;
+       }
+       if ((*cert = PEM_read_bio_X509(cert_bio, NULL, NULL, NULL)) == NULL) {
+               if ((ssl_err = ERR_peek_error()) != 0)
+                   errstr = ERR_error_string(ssl_err, NULL);
+               tls_error_set(error, "failed to load certificate: %s", errstr);
+               goto err;
+       }
+
+       BIO_free(cert_bio);
+
+       return (0);
+
+ err:
+       BIO_free(cert_bio);
+
+       return (-1);
+}
+
+static int
+tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx,
+    struct tls_keypair *keypair)
 {
-       EC_KEY *ecdh_key;
        unsigned char sid[SSL_MAX_SSL_SESSION_ID_LENGTH];
+       EC_KEY *ecdh_key;
 
-       if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
+       if ((*ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
                tls_set_errorx(ctx, "ssl context failure");
                goto err;
        }
 
-       if (tls_configure_ssl(ctx) != 0)
+       if (SSL_CTX_set_tlsext_servername_callback(*ssl_ctx,
+           tls_servername_callback) != 1) {
+               tls_set_error(ctx, "failed to set servername callback");
+               goto err;
+       }
+       if (SSL_CTX_set_tlsext_servername_arg(*ssl_ctx, ctx) != 1) {
+               tls_set_error(ctx, "failed to set servername callback");
+               goto err;
+       }
+
+       if (tls_configure_ssl(ctx, *ssl_ctx) != 0)
                goto err;
-       if (tls_configure_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 1) 
!= 0)
+       if (tls_configure_ssl_keypair(ctx, *ssl_ctx, keypair, 1) != 0)
                goto err;
        if (ctx->config->verify_client != 0) {
                int verify = SSL_VERIFY_PEER;
                if (ctx->config->verify_client == 1)
                        verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-               if (tls_configure_ssl_verify(ctx, verify) == -1)
+               if (tls_configure_ssl_verify(ctx, *ssl_ctx, verify) == -1)
                        goto err;
        }
 
        if (ctx->config->alpn != NULL)
-               SSL_CTX_set_alpn_select_cb(ctx->ssl_ctx, tls_server_alpn_cb,
+               SSL_CTX_set_alpn_select_cb(*ssl_ctx, tls_server_alpn_callback,
                    ctx);
 
        if (ctx->config->dheparams == -1)
-               SSL_CTX_set_dh_auto(ctx->ssl_ctx, 1);
+               SSL_CTX_set_dh_auto(*ssl_ctx, 1);
        else if (ctx->config->dheparams == 1024)
-               SSL_CTX_set_dh_auto(ctx->ssl_ctx, 2);
+               SSL_CTX_set_dh_auto(*ssl_ctx, 2);
 
        if (ctx->config->ecdhecurve == -1) {
-               SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1);
+               SSL_CTX_set_ecdh_auto(*ssl_ctx, 1);
        } else if (ctx->config->ecdhecurve != NID_undef) {
                if ((ecdh_key = EC_KEY_new_by_curve_name(
                    ctx->config->ecdhecurve)) == NULL) {
                        tls_set_errorx(ctx, "failed to set ECDHE curve");
                        goto err;
                }
-               SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
-               SSL_CTX_set_tmp_ecdh(ctx->ssl_ctx, ecdh_key);
+               SSL_CTX_set_options(*ssl_ctx, SSL_OP_SINGLE_ECDH_USE);
+               SSL_CTX_set_tmp_ecdh(*ssl_ctx, ecdh_key);
                EC_KEY_free(ecdh_key);
        }
 
        if (ctx->config->ciphers_server == 1)
-               SSL_CTX_set_options(ctx->ssl_ctx,
+               SSL_CTX_set_options(*ssl_ctx,
                    SSL_OP_CIPHER_SERVER_PREFERENCE);
 
        /*
@@ -117,10 +219,59 @@ tls_configure_server(struct tls *ctx)
         * session ID context that is valid during run time.
         */
        arc4random_buf(sid, sizeof(sid));
-       if (!SSL_CTX_set_session_id_context(ctx->ssl_ctx, sid, sizeof(sid))) {
-               tls_set_errorx(ctx, "failed to set session id context");
+       if (SSL_CTX_set_session_id_context(*ssl_ctx, sid,
+           sizeof(sid)) != 1) {
+               tls_set_error(ctx, "failed to set session id context");
                goto err;
        }
+
+       return (0);
+
+  err:
+       SSL_CTX_free(*ssl_ctx);
+       *ssl_ctx = NULL;
+
+       return (-1);
+}
+
+static int
+tls_configure_server_sni(struct tls *ctx)
+{
+       struct tls_sni_ctx **sni_ctx;
+       struct tls_keypair *kp;
+
+       if (ctx->config->keypair->next == NULL)
+               return (0);
+
+       /* Set up additional SSL contexts for SNI. */
+       sni_ctx = &ctx->sni_ctx;
+       for (kp = ctx->config->keypair->next; kp != NULL; kp = kp->next) {
+               if ((*sni_ctx = tls_sni_ctx_new()) == NULL) {
+                       tls_set_errorx(ctx, "out of memory");
+                       goto err;
+               }
+               if (tls_configure_server_ssl(ctx, &(*sni_ctx)->ssl_ctx, kp) == 
-1)
+                       goto err;
+               if (tls_keypair_load_cert(kp, &ctx->error,
+                   &(*sni_ctx)->ssl_cert) == -1)
+                       goto err;
+               sni_ctx = &(*sni_ctx)->next;
+       }
+
+       return (0);
+
+ err:
+       return (-1);
+}
+
+int
+tls_configure_server(struct tls *ctx)
+{
+       if (tls_configure_server_ssl(ctx, &ctx->ssl_ctx,
+           ctx->config->keypair) == -1)
+               goto err;
+       if (tls_configure_server_sni(ctx) == -1)
+               goto err;
 
        return (0);
 

Reply via email to