summaryrefslogtreecommitdiffstats
path: root/nss_engine_init.c
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2015-09-24 17:13:20 -0400
committerRob Crittenden <rcritten@redhat.com>2015-10-02 16:51:56 -0400
commit00fe09480dfd28674661830d8a045e0f560bbe51 (patch)
treef13ebc99b09ee029ddbc75bba74aa60cb3e9dc66 /nss_engine_init.c
parentea7584f67ef0e522254c3806cc0356e89594c711 (diff)
downloadmod_nss-00fe09480dfd28674661830d8a045e0f560bbe51.tar.gz
mod_nss-00fe09480dfd28674661830d8a045e0f560bbe51.tar.xz
mod_nss-00fe09480dfd28674661830d8a045e0f560bbe51.zip
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 <stokos@suse.de>
Diffstat (limited to 'nss_engine_init.c')
-rw-r--r--nss_engine_init.c193
1 files changed, 182 insertions, 11 deletions
diff --git a/nss_engine_init.c b/nss_engine_init.c
index 4d82f53..df84a9f 100644
--- a/nss_engine_init.c
+++ b/nss_engine_init.c
@@ -29,6 +29,7 @@ static SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg);
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 nssSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr, PRUint32 sniNameArrSize, void *arg);
/*
* Global variables defined in this file.
@@ -89,6 +90,7 @@ static void nss_init_SSLLibrary(server_rec *base_server)
int fipsenabled = FALSE;
int ocspenabled = FALSE;
int ocspdefault = FALSE;
+ int snienabled = FALSE;
char *dbdir = NULL;
const char * ocspurl = NULL;
const char * ocspname = NULL;
@@ -118,6 +120,10 @@ static void nss_init_SSLLibrary(server_rec *base_server)
return;
}
}
+
+ if (sc->sni == TRUE) {
+ snienabled = TRUE;
+ }
}
/* We need to be in the same directory as libnssckbi.so to load the
@@ -243,6 +249,14 @@ static void nss_init_SSLLibrary(server_rec *base_server)
}
}
+ if (snienabled) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server,
+ "SNI is enabled");
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server,
+ "SNI is disabled");
+ }
+
/*
* Seed the Pseudo Random Number Generator (PRNG)
* only need ptemp here; nothing inside allocated from the pool
@@ -262,6 +276,8 @@ int nss_init_Module(apr_pool_t *p, apr_pool_t *plog,
int fipsenabled = FALSE;
int threaded = 0;
struct semid_ds status;
+ char *split_vhost_id = NULL;
+ char *last1;
mc->nInitCount++;
@@ -325,6 +341,14 @@ int nss_init_Module(apr_pool_t *p, apr_pool_t *plog,
sc->vhost_id = nss_util_vhostid(p, s);
sc->vhost_id_len = strlen(sc->vhost_id);
+ if (sc->sni && sc->server->nickname != NULL && sc->vhost_id != NULL) {
+ split_vhost_id = apr_strtok(sc->vhost_id, ":", &last1);
+ ap_str_tolower(split_vhost_id);
+ addHashVhostNick(split_vhost_id, (char *)sc->server->nickname);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "SNI: %s -> %s", split_vhost_id, (char *)sc->server->nickname);
+ }
+
/* Fix up stuff that may not have been set */
if (sc->fips == UNSET) {
sc->fips = FALSE;
@@ -477,7 +501,7 @@ int nss_init_Module(apr_pool_t *p, apr_pool_t *plog,
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);
@@ -1033,12 +1057,20 @@ static void nss_init_certificate(server_rec *s, const char *nickname,
SSLKEAType *KEAtype,
PRFileDesc *model,
int enforce,
+ int sni,
const CERTCertList* clist)
{
+ SSLModConfigRec *mc = myModConfig(s);
SECCertTimeValidity certtimestatus;
SECStatus secstatus;
PK11SlotInfo* slot = NULL;
+ CERTCertNicknames *certNickDNS = NULL;
+ char **nnptr = NULL;
+ int nn = 0;
+ apr_array_header_t *names = NULL;
+ apr_array_header_t *wild_names = NULL;
+ int i, j;
if (nickname == NULL) {
return;
@@ -1105,17 +1137,55 @@ static void nss_init_certificate(server_rec *s, const char *nickname,
*KEAtype = NSS_FindCertKEAType(*servercert);
+ /* add ServerAlias entries to hash */
+ names = s->names;
+ if (names) {
+ char **name = (char **)names->elts;
+ for (i = 0; i < names->nelts; ++i) {
+ ap_str_tolower(name[i]);
+ addHashVhostNick(name[i], (char *)nickname);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "SNI ServerAlias: %s -> %s", name[i], nickname);
+ }
+ }
+
+ /* add ServerAlias entries with wildcards */
+ wild_names = s->wild_names;
+ if (wild_names) {
+ char **wild_name = (char **)wild_names->elts;
+ for (j = 0; j < wild_names->nelts; ++j) {
+ ap_str_tolower(wild_name[j]);
+ addHashVhostNick(wild_name[j], (char *)nickname);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "SNI wildcard: %s -> %s", wild_name[i], nickname);
+ }
+ }
+
+ /* get valid DNS names from certificate to hash */
+ certNickDNS = CERT_GetValidDNSPatternsFromCert(*servercert);
+
+ if (certNickDNS) {
+ nnptr = certNickDNS->nicknames;
+ nn = certNickDNS->numnicknames;
+
+ while ( nn > 0 ) {
+ ap_str_tolower(*nnptr);
+ addHashVhostNick(*nnptr, (char *)nickname);
+ nnptr++;
+ nn--;
+ }
+ }
+
/* Subject/hostname check */
secstatus = CERT_VerifyCertName(*servercert, s->server_hostname);
if (secstatus != SECSuccess) {
char *cert_dns = CERT_GetCommonName(&(*servercert)->subject);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
- "Misconfiguration of certificate's CN and virtual name."
- " The certificate CN has %s. We expected %s as virtual"
- " name.", cert_dns, s->server_hostname);
+ "Misconfiguration of certificate's CN and virtual name."
+ " The certificate CN has %s. We expected %s as virtual"
+ " name.", cert_dns, s->server_hostname);
PORT_Free(cert_dns);
}
-
/*
* Check for certs that are expired or not yet valid and WARN about it.
* No need to refuse working - the client gets a warning.
@@ -1140,13 +1210,23 @@ static void nss_init_certificate(server_rec *s, const char *nickname,
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();
}
+
+ if (PR_TRUE == sni) {
+ if (SSL_SNISocketConfigHook(model, (SSLSNISocketConfig) nssSSLSNISocketConfig, (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();
+ }
+ }
}
@@ -1192,11 +1272,13 @@ static void nss_init_server_certs(server_rec *s,
nss_init_certificate(s, mctx->nickname, &mctx->servercert,
&mctx->serverkey, &mctx->serverKEAType,
- mctx->model, mctx->enforce, clist);
+ mctx->model, mctx->enforce, mctx->sc->sni,
+ clist);
#ifdef NSS_ENABLE_ECC
nss_init_certificate(s, mctx->eccnickname, &mctx->eccservercert,
&mctx->eccserverkey, &mctx->eccserverKEAType,
- mctx->model, mctx->enforce, clist);
+ mctx->model, mctx->enforce, mctx->sc->sni,
+ clist);
#endif
}
@@ -1215,6 +1297,7 @@ static void nss_init_server_certs(server_rec *s,
nss_log_nss_error(APLOG_MARK, APLOG_ERR, s);
nss_die();
}
+
}
static void nss_init_proxy_ctx(server_rec *s,
@@ -1281,7 +1364,6 @@ void nss_init_Child(apr_pool_t *p, server_rec *base_server)
/* If any servers have SSL, we want sslenabled set so we
* can perform further initialization
*/
-
if (sc->enabled == UNSET) {
sc->enabled = FALSE;
}
@@ -1311,11 +1393,12 @@ void nss_init_Child(apr_pool_t *p, server_rec *base_server)
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())
+ if (sc->server->servercert == NULL && NSS_IsInitialized()) {
nss_init_ConfigureServer(s, p, mc->ptemp, sc, clist);
+ }
}
if (clist) {
CERT_DestroyCertList(clist);
@@ -1346,6 +1429,11 @@ apr_status_t nss_init_ModuleKill(void *data)
if (mc->nInitCount == 1)
nss_init_ChildKill(base_server);
+ if (mp) {
+ apr_pool_destroy(mp);
+ mp = NULL;
+ }
+
/* NSS_Shutdown() gets called in nss_init_ChildKill */
return APR_SUCCESS;
}
@@ -1398,6 +1486,11 @@ apr_status_t nss_init_ChildKill(void *data)
}
}
+ if (mp) {
+ apr_pool_destroy(mp);
+ mp = NULL;
+ }
+
if (shutdown) {
/* Clear any client-side session cache data */
SSL_ClearSessionCache();
@@ -1597,3 +1690,81 @@ SECStatus NSSHandshakeCallback(PRFileDesc *socket, void *arg)
{
return SECSuccess;
}
+
+/*
+ * Callback made during SSL request to see if SNI was requested and
+ * pair it with a configured nickname.
+ */
+PRInt32 nssSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr,
+ PRUint32 sniNameArrSize, void *arg)
+{
+ server_rec *s = (server_rec *)arg;
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "nssSSLSNISocketConfig");
+
+ void *pinArg;
+ CERTCertificate *cert = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ char *nickName = NULL;
+ char *vhost = NULL;
+ apr_pool_t *str_p;
+
+ PORT_Assert(fd && sniNameArr);
+ if (!fd || !sniNameArr) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ apr_pool_create(&str_p, NULL);
+ vhost = apr_pstrndup(str_p, (char *) sniNameArr->data, sniNameArr->len);
+
+ /* rfc6125 - Checking of Traditional Domain Names */
+ ap_str_tolower(vhost);
+
+ nickName = searchHashVhostbyNick(vhost);
+ if (nickName == NULL) {
+ /* search for wildcard_names in serverAlises */
+ nickName = searchHashVhostbyNick_match(vhost);
+ if (nickName == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "SNI: Search for %s failed. Unrecognized name.", vhost);
+ goto loser;
+ }
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,"SNI: Found nickname %s for vhost: %s", nickName, vhost);
+
+ pinArg = SSL_RevealPinArg(fd);
+
+ /* if pinArg is NULL, then we would not get the key and
+ * return an error status. */
+ cert = PK11_FindCertFromNickname(nickName, &pinArg);
+ if (cert == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "Failed to find certificate for nickname: %s", nickName);
+ goto loser;
+ }
+ privKey = PK11_FindKeyByAnyCert(cert, &pinArg);
+ if (privKey == NULL) {
+ goto loser;
+ }
+
+ SSLKEAType certKEA = NSS_FindCertKEAType(cert);
+
+ if (SSL_ConfigSecureServer(fd, cert, privKey, certKEA) != SECSuccess) {
+ goto loser; /* Send alert */
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+ "SNI: Successfully paired vhost %s with nickname: %s", vhost, nickName);
+
+ return 0;
+
+loser:
+ if (privKey)
+ SECKEY_DestroyPrivateKey(privKey);
+ if (cert)
+ CERT_DestroyCertificate(cert);
+ apr_pool_destroy(str_p);
+
+ return SSL_SNI_SEND_ALERT;
+}