From: Stanislav Tokos <[email protected]>

Introduces a new NSSSNI configuration directive.
---
 docs/mod_nss.html   |  13 ++++-
 mod_nss.c           |   3 ++
 mod_nss.h           |  19 ++++++++
 nss_engine_config.c |  11 +++++
 nss_engine_init.c   | 138 +++++++++++++++++++++++++++++++++++++++++++++++++---
 nss_engine_kernel.c |  53 ++++++++++++++++++++
 nss_util.c          |  44 +++++++++++++++++
 7 files changed, 273 insertions(+), 8 deletions(-)

diff --git a/docs/mod_nss.html b/docs/mod_nss.html
index 19d8fef..4566875 100644
--- a/docs/mod_nss.html
+++ b/docs/mod_nss.html
@@ -184,7 +184,8 @@ following line to httpd.conf (location relative to 
httpd.conf):<br>
 </code><br>
 This has Apache load the mod_nss configuration file, <code>nss.conf</code>.
 It is here that you will setup your VirtualServer entries to and
-configure your SSL servers.<br>
+configure your SSL servers. If you have a certificate with the Subject
+Alternative Names then you will set up these names like ServerAlias for your 
virtual host.<br>
 
 <h1><a name="Generation"></a>Certificate Generation</h1>
 A ksh script, <code>gencert</code>, is included to automatically
@@ -1043,6 +1044,16 @@ components of the client certificate, the remote IP 
address, etc.<br>
 <code>NSSRequire<br>
 </code><br>
 <br>
+<big><big>NSSSNI</big></big><br>
+<br>
+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.
+<br>
+<br>
+<span style="font-weight: bold;">Example</span><br>
+<br>
+<code>NSSSNI off</code><br>
+<br>
 <big><big>NSSRenegBufferSize</big></big><br>
 <br>
 Configure the amount of memory that will be used for buffering the
diff --git a/mod_nss.c b/mod_nss.c
index 5530721..28c9658 100644
--- a/mod_nss.c
+++ b/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)")
diff --git a/mod_nss.h b/mod_nss.h
index ba081cc..790cc81 100644
--- a/mod_nss.h
+++ b/mod_nss.h
@@ -311,6 +311,7 @@ struct SSLSrvConfigRec {
     const char      *ocsp_name;
     BOOL             ocsp;
     BOOL             enabled;
+    BOOL             sni;
     BOOL             proxy_enabled;
     const char      *vhost_id;
     int              vhost_id_len;
@@ -341,6 +342,20 @@ typedef struct {
  * for cipher definitions see nss_engine_cipher.h
  */
 
+typedef struct {
+    enum {
+        PW_NONE = 0,
+        PW_FROMFILE = 1,
+        PW_PLAINTEXT = 2,
+        PW_EXTERNAL = 3
+    } source;
+    char *data;
+} secuPWData;
+
+/* pool and hash which will contain ServerName and NSSNickname */
+apr_pool_t *mp;
+apr_hash_t *ht;
+
 /* 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
@@ -373,6 +388,7 @@ void *nss_config_perdir_merge(apr_pool_t *p, void *basev, 
void *addv);
 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);
@@ -463,6 +479,9 @@ apr_file_t  *nss_util_ppopen(server_rec *, apr_pool_t *, 
const char *,
 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 *searchHashVhostNick(char *vhost_id);
+char *searchHashVhostNick_match(char *vhost_id);
+void addHashVhostNick(char *vhost_id, char *nickname);
 /* 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, apr_size_t maxlen);
diff --git a/nss_engine_config.c b/nss_engine_config.c
index 8d4421a..3e24148 100644
--- a/nss_engine_config.c
+++ b/nss_engine_config.c
@@ -134,6 +134,7 @@ static SSLSrvConfigRec *nss_config_server_new(apr_pool_t *p)
     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 *p, void *basev, 
void *addv) {
     cfgMerge(ocsp_name, NULL);
     cfgMergeBool(fips);
     cfgMergeBool(enabled);
+    cfgMergeBool(sni);
     cfgMergeBool(proxy_enabled);
     cfgMergeBool(proxy_ssl_check_peer_cn);
     cfgMergeBool(session_tickets);
@@ -343,6 +345,15 @@ const char *nss_cmd_NSSFIPS(cmd_parms *cmd, void *dcfg, 
int flag)
     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)
 {
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
diff --git a/nss_engine_init.c b/nss_engine_init.c
index 7a8eae8..53bd10c 100644
--- a/nss_engine_init.c
+++ b/nss_engine_init.c
@@ -29,6 +29,8 @@ 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 ownSSLSNISocketConfig(PRFileDesc *fd, const SECItem *sniNameArr,
+                             PRUint32 sniNameArrSize, void *arg);
 
 /*
  * Global variables defined in this file.
@@ -261,6 +263,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++;
 
@@ -324,6 +328,12 @@ 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->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);
+        }
+
         /* Fix up stuff that may not have been set */
         if (sc->fips == UNSET) {
             sc->fips = FALSE;
@@ -476,7 +486,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);
@@ -1038,6 +1048,12 @@ static void nss_init_certificate(server_rec *s, const 
char *nickname,
     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;
@@ -1104,14 +1120,49 @@ static void nss_init_certificate(server_rec *s, const 
char *nickname,
 
     *KEAtype = NSS_FindCertKEAType(*servercert);
 
+    /* get 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);
+        }
+    }
+
+    /* get 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);
+        }
+    }
+
+    /* 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);
     }
 
@@ -1146,6 +1197,14 @@ static void nss_init_certificate(server_rec *s, const 
char *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();
+    }
 }
 
 
@@ -1310,11 +1369,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())
-            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);
@@ -1596,3 +1656,67 @@ SECStatus NSSHandshakeCallback(PRFileDesc *socket, void 
*arg)
 {
     return SECSuccess;
 }
+
+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;
+    apr_pool_t *str_p;
+
+    PORT_Assert(fd && sniNameArr);
+    if (!fd || !sniNameArr) {
+      nss_die();
+    }
+    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 = searchHashVhostNick(vhost);
+    if (nickName == NULL)  {
+      /* search wild_names in serverAlises */
+      nickName = searchHashVhostNick_match(vhost);
+      if (nickName == NULL) {
+       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,"Search [val = %s] failed, 
unrecognized name.", vhost);
+       nss_die();
+      }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,"Search passed [value = %s] for 
key:%s", nickName, vhost);
+
+    pwdata = SSL_RevealPinArg(fd);
+
+    /* if pwdata is NULL, then we would not get the key and
+     * return an error status. */
+    cert = PK11_FindCertFromNickname(nickName, &pwdata);
+    if (cert == NULL) {
+      nss_die();
+    }
+    privKey = PK11_FindKeyByAnyCert(cert, &pwdata);
+    if (privKey == NULL) {
+      nss_die();
+    }
+    SSLKEAType  certKEA = NSS_FindCertKEAType(cert);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                "start configure vhost:%s", vhost);
+    if (SSL_ConfigSecureServer(fd, cert, privKey, certKEA) != SECSuccess) {
+      nss_die();
+    }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+                "successfull setting vhost with nick:%s", nickName);
+    SECKEY_DestroyPrivateKey(privKey);
+    CERT_DestroyCertificate(cert);
+    apr_pool_destroy(str_p);
+    return 0;
+
+}
diff --git a/nss_engine_kernel.c b/nss_engine_kernel.c
index 721eedb..bbc71fe 100644
--- a/nss_engine_kernel.c
+++ b/nss_engine_kernel.c
@@ -72,6 +72,59 @@ int nss_hook_ReadReq(request_rec *r)
     }
 
     /*
+     * SNI check is default on. In same cases you switch of by NSSSNI off
+     * sc->sni 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;
+       apr_pool_t *s_p;
+
+       ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                "SNI hostInfo  hostInfo->data:%s and hostInfo->len:%d"
+                    ,(char *) hostInfo->data, hostInfo->len);
+
+       apr_pool_create(&s_p, NULL);
+       servername = apr_pstrndup(s_p, (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);
+           apr_pool_destroy(s_p);
+           return HTTP_BAD_REQUEST;
+         } else {
+           SECITEM_FreeItem(hostInfo, PR_TRUE);
+           apr_pool_destroy(s_p);
+         }
+      }
+    }
+    /*
      * Log information about incoming HTTPS requests
      */
     if (r->server->log.level >= APLOG_INFO && ap_is_initial_req(r)) {
diff --git a/nss_util.c b/nss_util.c
index c8dc74f..fef3313 100644
--- a/nss_util.c
+++ b/nss_util.c
@@ -100,3 +100,47 @@ char *nss_util_readfilter(server_rec *s, apr_pool_t *p, 
const char *cmd,
 
     return buf;
 }
+
+static void initializeHashVhostNick() {
+       apr_pool_create(&mp, NULL);
+       ht = apr_hash_make(mp);
+}
+
+char *searchHashVhostNick(char *vhost_id) {
+    char *searchVal = NULL;
+
+    searchVal = apr_hash_get(ht, vhost_id, APR_HASH_KEY_STRING);
+
+    return searchVal;
+}
+
+char *searchHashVhostNick_match(char *vhost_id)
+{
+  char *searchValReg = NULL;
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(NULL, ht); hi; hi = apr_hash_next(hi)) {
+    const char *k = NULL;
+    const char *v = NULL;
+
+    apr_hash_this(hi, (const void**)&k, NULL, (void**)&v);
+    if (!ap_strcasecmp_match(vhost_id, k)) {
+      searchValReg = apr_hash_get(ht, k, APR_HASH_KEY_STRING);
+      return searchValReg;
+    }
+  }
+  return NULL;
+}
+
+void addHashVhostNick(char *vhost_id, char *nickname) {
+
+    if (ht == NULL) {
+      initializeHashVhostNick();
+    }
+
+    if(searchHashVhostNick(vhost_id) == NULL) {
+      apr_hash_set(ht, apr_pstrdup(mp, vhost_id), APR_HASH_KEY_STRING,
+                      apr_pstrdup(mp, nickname));
+    }
+    return;
+}
+
-- 
2.1.4

_______________________________________________
Mod_nss-list mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/mod_nss-list

Reply via email to