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