On Sun, Aug 14, 2016 at 04:04:34AM +1000, Joel Sing wrote:
> 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.
> 

It's a big diff and I saw that you're splitting it up, but you can
find a few comments below.  Otherwise it looks OK to me, it is
surprisingly straight forward.

Reyk

> 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 ,

The detailed function prototypes are missing for the tls_conn_ functions...

>  .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"

...somewhere here, eg.

.Ft "const char *"
.Fn tls_conn_servername "struct tls *ctx"

> @@ -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)

What does the RFC mean with IP literal in this context?

Could this also be a literal IPv6 address in URL format (with [], RFC2732)?

Or an IPv6 address with scope id (which needs getaddrinfo() with
AI_NUMERICHOST instead of inet_pton)?

> +             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