From 9d52c3cedd6a1fd21cb83ffe51d112f027315565 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Wed, 17 Jan 2018 16:33:07 -0500
Subject: [PATCH] Try to auto-detect the NSS database format if not specified

If no database prefix, sql or dbm, is specified then look in
the database dir to try to figure out the type based on the
files (cert9 vs cert8).

Prefer the sql/cert9 format over dbm/cert8.
---
 mod_nss.h           |  9 +++++++
 nss_engine_config.c |  1 +
 nss_engine_init.c   | 28 +++++++++++---------
 nss_util.c          | 64 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 13 deletions(-)

Index: mod_nss-1.0.14/mod_nss.h
===================================================================
--- mod_nss-1.0.14.orig/mod_nss.h	2018-09-18 14:43:17.614228408 +0200
+++ mod_nss-1.0.14/mod_nss.h	2018-09-18 14:43:25.842276060 +0200
@@ -58,6 +58,7 @@
 #include <ssl.h>
 #include <nss.h>
 #include <sslproto.h>
+#include <utilparst.h>
 
 /* Apache ships its autoconf-generated config.h which defines these
  * as empty. We want the mod_nss version in our own config.h so
@@ -161,6 +162,12 @@ ap_set_module_config(c->conn_config, &ns
 #endif
 
 /*
+ * NSS database prefixes
+ */
+#define SQLDB "sql:"
+#define LEGACY "dbm:"
+
+/*
  * Define the SSL options
  */
 #define SSL_OPT_NONE           (0)
@@ -246,6 +253,7 @@ typedef struct {
     apr_pool_t     *ptemp; /* pointer to ptemp passed in during init */
     const char     *pCertificateDatabase;
     const char     *pDBPrefix;
+    NSSDBType      dbtype;
 
     /* config for SSL session cache */
     int             session_cache_size;
@@ -498,6 +506,7 @@ void addHashVhostNick(char *vhost_id, ch
 int          nss_io_buffer_fill(request_rec *r, apr_size_t maxlen);
 
 int nss_rand_seed(server_rec *s, apr_pool_t *p, ssl_rsctx_t nCtx, char *prefix);
+NSSDBType detect_dbtype(const char *dbdir, apr_pool_t *p);
 
 /* Pass Phrase Handling */
 SECStatus nss_Init_Tokens(server_rec *s);
Index: mod_nss-1.0.14/nss_engine_config.c
===================================================================
--- mod_nss-1.0.14.orig/nss_engine_config.c	2018-09-18 14:43:17.614228408 +0200
+++ mod_nss-1.0.14/nss_engine_config.c	2018-09-18 14:43:25.842276060 +0200
@@ -46,6 +46,7 @@ SSLModConfigRec *nss_config_global_creat
     mc->nInitCount                  = 0;
     mc->pCertificateDatabase        = NULL;
     mc->pDBPrefix                   = NULL;
+    mc->dbtype                      = NSS_DB_TYPE_NONE;
     mc->session_cache_size          = UNSET;
     mc->session_cache_timeout       = UNSET;
     mc->ssl3_session_cache_timeout  = UNSET;
Index: mod_nss-1.0.14/nss_engine_init.c
===================================================================
--- mod_nss-1.0.14.orig/nss_engine_init.c	2018-09-18 14:43:23.282261233 +0200
+++ mod_nss-1.0.14/nss_engine_init.c	2018-09-18 14:43:25.846276083 +0200
@@ -174,8 +174,15 @@ static void nss_init_SSLLibrary(server_r
         }
     }
 
+    if (mc->nInitCount == 1) {
+        mc->dbtype = detect_dbtype(mc->pCertificateDatabase, p);
+    }
+
+    /* Strip the known prefixes */
     if (strncasecmp(mc->pCertificateDatabase, "sql:", 4) == 0)
         dbdir = (char *)mc->pCertificateDatabase + 4;
+    else if (strncasecmp(mc->pCertificateDatabase, "dbm:", 4) == 0)
+        dbdir = (char *)mc->pCertificateDatabase + 4;
     else
         dbdir = (char *)mc->pCertificateDatabase;
 
@@ -185,7 +192,7 @@ static void nss_init_SSLLibrary(server_r
      */
     if (mc->nInitCount == 1) {
         if (mc->skip_permission_check == PR_FALSE) {
-            char filepath[1024];
+            char filepath[APR_PATH_MAX];
             struct passwd *pw = NULL;
 
             pw = getpwnam(mc->user);
@@ -200,9 +207,8 @@ static void nss_init_SSLLibrary(server_r
                 nss_die();
             }
 
-            if (strncasecmp(mc->pCertificateDatabase, "sql:", 4) == 0) {
-                apr_snprintf(filepath, 1024, "%s/key4.db",
-                             mc->pCertificateDatabase+4);
+            if (mc->dbtype == NSS_DB_TYPE_SQL) {
+                apr_snprintf(filepath, APR_PATH_MAX, "%s/key4.db", dbdir);
                 if (!(check_path(mc->user, pw->pw_uid, pw->pw_gid, filepath,
                       p))) {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
@@ -210,8 +216,7 @@ static void nss_init_SSLLibrary(server_r
                         "database %s.", mc->user, filepath);
                     nss_die();
                 }
-                apr_snprintf(filepath, 1024, "%s/cert9.db",
-                             mc->pCertificateDatabase+4);
+                apr_snprintf(filepath, APR_PATH_MAX, "%s/cert9.db", dbdir);
                 if (!(check_path(mc->user, pw->pw_uid, pw->pw_gid, filepath,
                       p))) {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
@@ -219,9 +224,8 @@ static void nss_init_SSLLibrary(server_r
                         "database %s.", mc->user, filepath);
                     nss_die();
                 }
-            } else {
-                apr_snprintf(filepath, 1024, "%s/key3.db",
-                             mc->pCertificateDatabase);
+            } else { /* legacy/dbm type */
+                apr_snprintf(filepath, APR_PATH_MAX, "%s/key3.db", dbdir);
                 if (!(check_path(mc->user, pw->pw_uid, pw->pw_gid, filepath,
                       p))) {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
@@ -229,8 +233,7 @@ static void nss_init_SSLLibrary(server_r
                         "database %s.", mc->user, filepath);
                     nss_die();
                 }
-                apr_snprintf(filepath, 1024, "%s/cert8.db",
-                             mc->pCertificateDatabase);
+                apr_snprintf(filepath, APR_PATH_MAX, "%s/cert8.db", dbdir);
                 if (!(check_path(mc->user, pw->pw_uid, pw->pw_gid, filepath,
                       p))) {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
@@ -238,8 +241,7 @@ static void nss_init_SSLLibrary(server_r
                         "database %s.", mc->user, filepath);
                     nss_die();
                 }
-                apr_snprintf(filepath, 1024, "%s/secmod.db",
-                             mc->pCertificateDatabase);
+                apr_snprintf(filepath, APR_PATH_MAX, "%s/secmod.db", dbdir);
                 if (!(check_path(mc->user, pw->pw_uid, pw->pw_gid, filepath, p))) {
                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
                         "Server user %s lacks read access to NSS secmod "
Index: mod_nss-1.0.14/nss_util.c
===================================================================
--- mod_nss-1.0.14.orig/nss_util.c	2018-09-18 14:43:17.614228408 +0200
+++ mod_nss-1.0.14/nss_util.c	2018-09-18 14:43:25.846276083 +0200
@@ -260,3 +260,67 @@ SECItem_to_ipaddr(apr_pool_t *p, SECItem
 
     return apr_pstrdup(p, buf);
 }
+
+static int
+file_exists(char *filepath, apr_pool_t *p)
+{
+    apr_finfo_t finfo;
+
+    if ((apr_stat(&finfo, filepath, APR_FINFO_PROT | APR_FINFO_OWNER, p))
+        == APR_SUCCESS) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+/* Detect whether a given NSS database directory is a sql or dbm
+ * type. Return the type found.
+ *
+ * sql is given priority
+ *
+ * NOTE: This uses some private constants from NSS.
+ *
+ * Other types (extern, multiaccess) are not handled.
+ */
+NSSDBType
+detect_dbtype(const char *dbdir, apr_pool_t *p)
+{
+    NSSDBType dbtype = NSS_DB_TYPE_NONE;
+    char filepath[APR_PATH_MAX];
+
+    if (PORT_Strncmp(dbdir, SQLDB, 4) == 0) {
+        dbtype = NSS_DB_TYPE_SQL;
+    } else if (PORT_Strncmp(dbdir, LEGACY, 4) == 0) {
+        dbtype = NSS_DB_TYPE_LEGACY;
+    } else {
+        char *envtype;
+
+        envtype = PR_GetEnvSecure("NSS_DEFAULT_DB_TYPE");
+        if (NULL != envtype) {
+            if (!(PORT_Strncmp(envtype, SQLDB, 4))) {
+                dbtype = NSS_DB_TYPE_SQL;
+            } else if (!(PORT_Strncmp(envtype, LEGACY, 4))) {
+                dbtype = NSS_DB_TYPE_LEGACY;
+            }
+        }
+    }
+
+    /* If a prefix was set in path or environment we're done */
+    if (dbtype != NSS_DB_TYPE_NONE) {
+         return dbtype;
+    }
+
+    /* Look in the directory to see what files are available */
+    apr_snprintf(filepath, APR_PATH_MAX, "%s/cert9.db", dbdir);
+    if (file_exists(filepath, p)) {
+         dbtype = NSS_DB_TYPE_SQL;
+    } else {
+        apr_snprintf(filepath, APR_PATH_MAX, "%s/cert8.db", dbdir);
+        if (file_exists(filepath, p)) {
+             dbtype = NSS_DB_TYPE_LEGACY;
+        }
+    }
+
+    return dbtype;
+}
