From f89edc012de43049aedd6f8f8689a5774cb76408 Mon Sep 17 00:00:00 2001 From: jsing Date: Mon, 22 Aug 2016 14:51:37 +0000 Subject: [PATCH] Create contexts for server side SNI - these include the additional SSL_CTX that is required for certificate switching with libssl and the certificate itself so that we can match against the subject and SANs. Hook up the servername callback and switch to the appropriate SSL_CTX if we find a matching certificate. ok beck@ --- lib/libtls/tls.c | 28 +++++++- lib/libtls/tls_internal.h | 15 ++++- lib/libtls/tls_server.c | 134 +++++++++++++++++++++++++++++++++++++- 3 files changed, 174 insertions(+), 3 deletions(-) diff --git a/lib/libtls/tls.c b/lib/libtls/tls.c index bf0e1f769fe..df610fe2384 100644 --- a/lib/libtls/tls.c +++ b/lib/libtls/tls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls.c,v 1.46 2016/08/15 14:04:23 jsing Exp $ */ +/* $OpenBSD: tls.c,v 1.47 2016/08/22 14:51:37 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -177,6 +177,24 @@ tls_set_errorx(struct tls *ctx, const char *fmt, ...) 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) { @@ -376,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); @@ -397,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 diff --git a/lib/libtls/tls_internal.h b/lib/libtls/tls_internal.h index bbd231e00ee..428e29c8577 100644 --- a/lib/libtls/tls_internal.h +++ b/lib/libtls/tls_internal.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_internal.h,v 1.39 2016/08/15 15:44:58 jsing Exp $ */ +/* $OpenBSD: tls_internal.h,v 1.40 2016/08/22 14:51:37 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing @@ -91,6 +91,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,11 +110,17 @@ 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); diff --git a/lib/libtls/tls_server.c b/lib/libtls/tls_server.c index 40096ae99f7..044678c705e 100644 --- a/lib/libtls/tls_server.c +++ b/lib/libtls/tls_server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tls_server.c,v 1.24 2016/08/18 15:52:03 jsing Exp $ */ +/* $OpenBSD: tls_server.c,v 1.25 2016/08/22 14:51:37 jsing Exp $ */ /* * Copyright (c) 2014 Joel Sing * @@ -15,6 +15,10 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include + #include #include #include @@ -62,6 +66,92 @@ tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, return (SSL_TLSEXT_ERR_NOACK); } +static int +tls_servername_cb(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) @@ -76,6 +166,16 @@ tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx, goto err; } + if (SSL_CTX_set_tlsext_servername_callback(*ssl_ctx, + tls_servername_cb) != 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 arg"); + goto err; + } + if (tls_configure_ssl(ctx, *ssl_ctx) != 0) goto err; if (tls_configure_ssl_keypair(ctx, *ssl_ctx, keypair, 1) != 0) @@ -134,12 +234,44 @@ tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx, 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); -- 2.20.1