From 00fe09480dfd28674661830d8a045e0f560bbe51 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Thu, 24 Sep 2015 17:13:20 -0400 Subject: Add support for Server Name Indication (SNI) Uses a hash table to pair up server names and nicknames and a lookup is done during the handshake to determine which nickname to be used, and therefore which VirtualHost. Based heavily on patch from Stanislav Tokos --- nss_engine_kernel.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'nss_engine_kernel.c') diff --git a/nss_engine_kernel.c b/nss_engine_kernel.c index 721eedb..9ce1411 100644 --- a/nss_engine_kernel.c +++ b/nss_engine_kernel.c @@ -25,6 +25,8 @@ int nss_hook_ReadReq(request_rec *r) { SSLConnRec *sslconn = myConnConfig(r->connection); PRFileDesc *ssl = sslconn ? sslconn->ssl : NULL; + SECItem *hostInfo = NULL; + SSLSrvConfigRec *sc = mySrvConfig(r->server); if (!sslconn) { return DECLINED; @@ -71,6 +73,74 @@ int nss_hook_ReadReq(request_rec *r) return DECLINED; } + /* + * SNI is on by default. You can switch SNI off by setting + * NSSSNI off. + */ + + if (sc->sni) { + hostInfo = SSL_GetNegotiatedHostInfo(ssl); + if (hostInfo != NULL) { + if (ap_is_initial_req(r) && (hostInfo->len != 0)) { + char *servername = NULL; + char *host, *scope_id; + apr_port_t port; + apr_status_t rv; + apr_pool_t *s_p; + + hostInfo->data[hostInfo->len] = '\0'; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "SNI request for %s", (char *)hostInfo->data); + + apr_pool_create(&s_p, NULL); + servername = apr_pstrndup(s_p, (char *) hostInfo->data, hostInfo->len); + + if (!r->hostname) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Hostname %s provided via SNI, but no hostname" + " provided in HTTP request", servername); + apr_pool_destroy(s_p); + return HTTP_BAD_REQUEST; + } + + rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool); + if (rv != APR_SUCCESS || scope_id) { + apr_pool_destroy(s_p); + return HTTP_BAD_REQUEST; + } + + if (strcasecmp(host, servername)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Hostname %s provided via SNI and hostname %s provided" + " via HTTP are different", servername, host); + + apr_pool_destroy(s_p); + return HTTP_BAD_REQUEST; + } + apr_pool_destroy(s_p); + } + } else if (((sc->strict_sni_vhost_check) + || (mySrvConfig(r->server))->strict_sni_vhost_check) + && r->connection->vhost_lookup_data) { + /* + * We are using a name based configuration here, but no hostname + * was provided via SNI. Don't allow that if are requested to do + * strict checking. Check whether this strict checking was set + * up either in the server config we used for handshaking or in + * our current server. This should avoid insecure configuration + * by accident. + */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "No hostname was provided via SNI for a name based" + " virtual host"); + apr_table_setn(r->notes, "error-notes", + "Reason: The client software did not provide a " + "hostname using Server Name Indication (SNI), " + "which is required to access this server.
\n"); + return HTTP_FORBIDDEN; + } + } + /* * Log information about incoming HTTPS requests */ @@ -815,6 +885,8 @@ int nss_hook_Fixup(request_rec *r) int i; CERTCertificate *cert; CERTCertificateList *chain = NULL; + SECItem *hostInfo = NULL; + const char *servername; /* * Check to see if SSL is on @@ -840,6 +912,13 @@ int nss_hook_Fixup(request_rec *r) /* the always present HTTPS (=HTTP over SSL) flag! */ apr_table_setn(env, "HTTPS", "on"); + /* add content of SNI TLS extension (if supplied with ClientHello) */ + hostInfo = SSL_GetNegotiatedHostInfo(ssl); + if (hostInfo) { + servername = apr_pstrndup(r->pool, (char *) hostInfo->data, hostInfo->len); + apr_table_set(env, "SSL_TLS_SNI", servername); + } + /* standard SSL environment variables */ if (dc->nOptions & SSL_OPT_STDENVVARS) { for (i = 0; nss_hook_Fixup_vars[i]; i++) { -- cgit