From 07405e4dbd1e2df6583bb571a6230da78788c19b Mon Sep 17 00:00:00 2001 From: standa Date: Thu, 26 Feb 2015 15:23:50 +0100 Subject: [PATCH] SNI check with NameVirtualHosts --- docs/mod_nss.html | 10 ++++++ mod_nss.c | 3 ++ mod_nss.h | 18 ++++++++++ nss_engine_config.c | 11 +++++++ nss_engine_init.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++----- nss_engine_kernel.c | 51 ++++++++++++++++++++++++++++ nss_util.c | 19 +++++++++++ 7 files changed, 199 insertions(+), 8 deletions(-) Index: mod_nss-1.0.8/docs/mod_nss.html =================================================================== --- mod_nss-1.0.8.orig/docs/mod_nss.html +++ mod_nss-1.0.8/docs/mod_nss.html @@ -1079,6 +1079,16 @@ components of the client certificate, th
NSSRequire

+NSSSNI
+
+Enables or disables Server Name Identification(SNI) extension check for +SSL. This option is turn on by default. SNI vhost_id gets from HTTPS header. +
+
+Example
+
+NSSSNI off
+
NSSProxyEngine

Enables or disables mod_nss HTTPS support for mod_proxy.
Index: mod_nss-1.0.8/mod_nss.c =================================================================== --- mod_nss-1.0.8.orig/mod_nss.c +++ mod_nss-1.0.8/mod_nss.c @@ -85,6 +85,9 @@ static const command_rec nss_config_cmds SSL_CMD_SRV(FIPS, FLAG, "FIPS 140-1 mode " "(`on', `off')") + SSL_CMD_SRV(SNI, FLAG, + "SNI" + "(`on', `off')") SSL_CMD_ALL(CipherSuite, TAKE1, "Comma-delimited list of permitted SSL Ciphers, + to enable, - to disable " "(`[+-]XXX,...,[+-]XXX' - see manual)") Index: mod_nss-1.0.8/mod_nss.h =================================================================== --- mod_nss-1.0.8.orig/mod_nss.h +++ mod_nss-1.0.8/mod_nss.h @@ -308,6 +308,7 @@ struct SSLSrvConfigRec { const char *ocsp_name; BOOL ocsp; BOOL enabled; + BOOL sni; BOOL proxy_enabled; const char *vhost_id; int vhost_id_len; @@ -343,6 +344,20 @@ typedef struct PRInt32 version; /* protocol version valid for this cipher */ } cipher_properties; +typedef struct { + const char *vhost_id[70]; + const char *nick[30]; +} vhostNick[500]; + +typedef struct { + enum { + PW_NONE = 0, + PW_FROMFILE = 1, + PW_PLAINTEXT = 2, + PW_EXTERNAL = 3 + } source; + char *data; +} secuPWData; /* Compatibility between Apache 2.0.x and 2.2.x. The numeric version of * the version first appeared in Apache 2.0.56-dev. I picked 2.0.55 as it * is the last version without this define. This is used for more than just @@ -384,6 +399,7 @@ void *nss_config_perdir_merge(apr_pool_t void *nss_config_server_create(apr_pool_t *p, server_rec *s); void *nss_config_server_merge(apr_pool_t *p, void *basev, void *addv); const char *nss_cmd_NSSFIPS(cmd_parms *, void *, int); +const char *nss_cmd_NSSSNI(cmd_parms *, void *, int); const char *nss_cmd_NSSEngine(cmd_parms *, void *, int); const char *nss_cmd_NSSOCSP(cmd_parms *, void *, int); const char *nss_cmd_NSSOCSPDefaultResponder(cmd_parms *, void *, int); @@ -471,6 +487,8 @@ apr_file_t *nss_util_ppopen(server_rec void nss_util_ppclose(server_rec *, apr_pool_t *, apr_file_t *); char *nss_util_readfilter(server_rec *, apr_pool_t *, const char *, const char * const *); +char *getSECItemData(char *data, int len); +char *getSplitURL(char *url); /* ssl_io_buffer_fill fills the setaside buffering of the HTTP request * to allow an SSL renegotiation to take place. */ int nss_io_buffer_fill(request_rec *r); Index: mod_nss-1.0.8/nss_engine_config.c =================================================================== --- mod_nss-1.0.8.orig/nss_engine_config.c +++ mod_nss-1.0.8/nss_engine_config.c @@ -135,6 +135,7 @@ static SSLSrvConfigRec *nss_config_serve sc->ocsp_name = NULL; sc->fips = UNSET; sc->enabled = UNSET; + sc->sni = TRUE; sc->proxy_enabled = UNSET; sc->vhost_id = NULL; /* set during module init */ sc->vhost_id_len = 0; /* set during module init */ @@ -214,6 +215,7 @@ void *nss_config_server_merge(apr_pool_t cfgMerge(ocsp_name, NULL); cfgMergeBool(fips); cfgMergeBool(enabled); + cfgMergeBool(sni); cfgMergeBool(proxy_enabled); cfgMergeBool(proxy_ssl_check_peer_cn); @@ -320,6 +322,15 @@ const char *nss_cmd_NSSFIPS(cmd_parms *c return NULL; } + +const char *nss_cmd_NSSSNI(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->sni = flag ? TRUE : FALSE; + + return NULL; +} const char *nss_cmd_NSSOCSP(cmd_parms *cmd, void *dcfg, int flag) { Index: mod_nss-1.0.8/nss_engine_init.c =================================================================== --- mod_nss-1.0.8.orig/nss_engine_init.c +++ mod_nss-1.0.8/nss_engine_init.c @@ -28,12 +28,17 @@ static SECStatus ownHandshakeCallback(PR static SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg); static CERTCertificate* FindServerCertFromNickname(const char* name, const CERTCertList* clist); SECStatus nss_AuthCertificate(void *arg, PRFileDesc *socket, PRBool checksig, PRBool isServer); +PRInt32 ownSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr, + PRUint32 sniNameArrSize, void *arg); /* * Global variables defined in this file. */ char* INTERNAL_TOKEN_NAME = "internal "; +vhostNick vhostNickSNI; +int vhostNickSize = 0; + cipher_properties ciphers_def[ciphernum] = { /* SSL2 cipher suites */ @@ -382,6 +387,11 @@ int nss_init_Module(apr_pool_t *p, apr_p sc->vhost_id = nss_util_vhostid(p, s); sc->vhost_id_len = strlen(sc->vhost_id); + if (sc->server->nickname != NULL && sc->vhost_id != NULL) { + strcpy(vhostNickSNI[vhostNickSize].vhost_id, sc->vhost_id); + strcpy(vhostNickSNI[vhostNickSize].nick, sc->server->nickname); + vhostNickSize++; + } /* Fix up stuff that may not have been set */ if (sc->fips == UNSET) { sc->fips = FALSE; @@ -534,7 +544,7 @@ int nss_init_Module(apr_pool_t *p, apr_p ap_log_error(APLOG_MARK, APLOG_INFO, 0, base_server, "Init: Initializing (virtual) servers for SSL"); - CERTCertList* clist = PK11_ListCerts(PK11CertListUser, NULL); + CERTCertList* clist = PK11_ListCerts(PK11CertListUserUnique, NULL); for (s = base_server; s; s = s->next) { sc = mySrvConfig(s); @@ -547,7 +557,7 @@ int nss_init_Module(apr_pool_t *p, apr_p /* * Read the server certificate and key */ - nss_init_ConfigureServer(s, p, ptemp, sc, clist); + nss_init_ConfigureServer(s, p, ptemp, sc, clist); } if (clist) { @@ -1233,13 +1243,21 @@ static void nss_init_certificate(server_ break; } - secstatus = SSL_ConfigSecureServer(model, *servercert, *serverkey, *KEAtype); + secstatus = SSL_ConfigSecureServer(model, *servercert, *serverkey, *KEAtype); if (secstatus != SECSuccess) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "SSL error configuring server: '%s'", nickname); nss_log_nss_error(APLOG_MARK, APLOG_ERR, s); nss_die(); - } + } + + /* SNI */ + if (SSL_SNISocketConfigHook(model, (SSLSNISocketConfig) ownSSLSNISocketConfig, (void*) s) != SECSuccess) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "SSL_SNISocketConfigHook failed"); + nss_log_nss_error(APLOG_MARK, APLOG_ERR, s); + nss_die(); + } } @@ -1308,6 +1326,7 @@ static void nss_init_server_certs(server nss_log_nss_error(APLOG_MARK, APLOG_ERR, s); nss_die(); } + } static void nss_init_proxy_ctx(server_rec *s, @@ -1374,7 +1393,6 @@ void nss_init_Child(apr_pool_t *p, serve /* If any servers have SSL, we want sslenabled set so we * can perform further initialization */ - if (sc->enabled == UNSET) { sc->enabled = FALSE; } @@ -1404,11 +1422,12 @@ void nss_init_Child(apr_pool_t *p, serve nss_init_SSLLibrary(base_server); /* Configure all virtual servers */ - CERTCertList* clist = PK11_ListCerts(PK11CertListUser, NULL); + CERTCertList* clist = PK11_ListCerts(PK11CertListUserUnique, NULL); for (s = base_server; s; s = s->next) { sc = mySrvConfig(s); - if (sc->server->servercert == NULL && NSS_IsInitialized()) - nss_init_ConfigureServer(s, p, mc->ptemp, sc, clist); + if (sc->server->servercert == NULL && NSS_IsInitialized()) { + nss_init_ConfigureServer(s, p, mc->ptemp, sc, clist); + } } if (clist) { CERT_DestroyCertList(clist); @@ -1741,3 +1760,63 @@ int nss_parse_ciphers(server_rec *s, cha return 0; } + +PRInt32 ownSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr, + PRUint32 sniNameArrSize, void *arg) +{ + server_rec *s = (server_rec *)arg; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "start function ownSSLSNISocketConfig for SNI"); + + secuPWData *pwdata; + CERTCertificate * cert = NULL; + SECKEYPrivateKey * privKey = NULL; + char *nickName = NULL; + char *vhost = NULL; + int i; + + PORT_Assert(fd && sniNameArr); + if (!fd || !sniNameArr) { + return SSL_SNI_SEND_ALERT; + } + vhost = getSECItemData((char *) sniNameArr->data, sniNameArr->len); + + for(i = 0; isni parameter gets vhost from HTTPS header + */ + SSLSrvConfigRec *sc = mySrvConfig(r->server); + + SECItem *hostInfo = NULL; + hostInfo = SSL_GetNegotiatedHostInfo(ssl); + if (hostInfo != NULL && sc->sni) { + if (ap_is_initial_req(r) && (hostInfo->len != 0)) { + char *servername = NULL; + char *host, *scope_id; + apr_port_t port; + apr_status_t rv; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "SNI hostInfo hostInfo->data:%s and hostInfo->len:%d" + , hostInfo->data, hostInfo->len); + + servername = getSECItemData((char *) hostInfo->data, hostInfo->len); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "SNI hostInfo servername:%s, lenght:%d" + , servername, (unsigned)strlen(servername)); + + 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); + return HTTP_BAD_REQUEST; + } + + rv = apr_parse_addr_port(&host, &scope_id, &port, r->hostname, r->pool); + if (rv != APR_SUCCESS || scope_id) { + 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); + + SECITEM_FreeItem(hostInfo, PR_TRUE); + servername = NULL; + return HTTP_BAD_REQUEST; + } else { + SECITEM_FreeItem(hostInfo, PR_TRUE); + servername = NULL; + } + } + } + /* * Log information about incoming HTTPS requests */ if (r->server->loglevel >= APLOG_INFO && ap_is_initial_req(r)) { Index: mod_nss-1.0.8/nss_util.c =================================================================== --- mod_nss-1.0.8.orig/nss_util.c +++ mod_nss-1.0.8/nss_util.c @@ -100,3 +100,22 @@ char *nss_util_readfilter(server_rec *s, return buf; } + +char *getSECItemData(char *data, int len) { + + data[len]='\0'; + + return data; +} + +char *getSplitURL(char *url) { + + int iter = 0; + + while(url[iter] != '\0' && url[iter] != ':'){ + url[iter++]; + } + url[iter]='\0'; + + return url; +}