summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2016-02-29 22:33:23 -0500
committerRob Crittenden <rcritten@redhat.com>2016-03-01 11:42:27 -0500
commit105d65bfedfa0e381dcebd197ef67aab799fc8b1 (patch)
tree87bb5d4c0fb34884a7f47efeb8c86192859bb534
parent8e8befca612a8f70b9d47de5393c134aecf81494 (diff)
downloadmod_nss-105d65bfedfa0e381dcebd197ef67aab799fc8b1.tar.gz
mod_nss-105d65bfedfa0e381dcebd197ef67aab799fc8b1.tar.xz
mod_nss-105d65bfedfa0e381dcebd197ef67aab799fc8b1.zip
Check filesystem permissions on NSS database at startup
See if the configured user has read access to the NSS database during initialization so the server can gracefully shutdown rather than ending up in a forking loop because the database is owned by root and is therefore unreadable once Apache starts forking. Adds a new configuration option, NSSSkipPermissionCheck <on/off>, to skip this check in case something goes wrong. https://fedorahosted.org/mod_nss/ticket/3
-rw-r--r--docs/mod_nss.html12
-rw-r--r--mod_nss.c3
-rw-r--r--mod_nss.h3
-rw-r--r--nss_engine_config.c11
-rw-r--r--nss_engine_init.c122
5 files changed, 124 insertions, 27 deletions
diff --git a/docs/mod_nss.html b/docs/mod_nss.html
index c84f938..ec03a07 100644
--- a/docs/mod_nss.html
+++ b/docs/mod_nss.html
@@ -440,6 +440,18 @@ reads that many bytes, otherwise it reads until the program exits.<br>
NSSRandomSeed startup /dev/urandom 512<br>
NSSRandomSeed startup /usr/bin/makerandom</code><br>
<br>
+<big><big>NSSSkipPermissionCheck</big></big><br>
+<br>
+The NSS database will be checked to ensure that the user configured
+to run Apache as has owner or group read access to the database
+configured in <code>NSSCertificateDatabase</code>. This check
+can be disabled by setting <code>NSSSkipPermissionCheck</code>
+to <code>on</code>. The default is <code>off</code><br>
+<br>
+<span style="font-weight: bold;">Example</span><br>
+<br>
+<code>NSSSkipPermissionCheck on</code><br>
+<br>
<big><big>NSSEngine</big></big><br>
<br>
Enables or disables the SSL protocol. This is usually used within a
diff --git a/mod_nss.c b/mod_nss.c
index b3ea6f0..38098c8 100644
--- a/mod_nss.c
+++ b/mod_nss.c
@@ -54,6 +54,9 @@ static const command_rec nss_config_cmds[] = {
SSL_CMD_SRV(SessionCacheSize, TAKE1,
"SSL Session Cache size "
"(`N' - number of entries)")
+ SSL_CMD_SRV(SkipPermissionCheck, FLAG,
+ "Skip checking the NSS database read permissions"
+ "(`on', `off')")
SSL_CMD_SRV(PassPhraseDialog, TAKE1,
"SSL dialog mechanism for the pass phrase query "
"(`builtin', `file:/path/to/file`")
diff --git a/mod_nss.h b/mod_nss.h
index ac56ddb..4604ff9 100644
--- a/mod_nss.h
+++ b/mod_nss.h
@@ -256,6 +256,8 @@ typedef struct {
const char *pphrase_dialog_path;
const char *pphrase_dialog_helper;
+ BOOL skip_permission_check;
+
apr_proc_t proc;
apr_procattr_t *procattr;
@@ -418,6 +420,7 @@ const char *nss_cmd_NSSSessionCacheSize(cmd_parms *cmd, void *dcfg, const char *
const char *nss_cmd_NSSPassPhraseDialog(cmd_parms *cmd, void *dcfg, const char *arg);
const char *nss_cmd_NSSPassPhraseHelper(cmd_parms *cmd, void *dcfg, const char *arg);
const char *nss_cmd_NSSRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
+const char *nss_cmd_NSSSkipPermissionCheck(cmd_parms *cmd, void *dcfg, int flag);
const char *nss_cmd_NSSSessionTickets(cmd_parms *cmd, void *dcfg, int flag);
#ifdef ENABLE_SERVER_DHE
const char *nss_cmd_NSSServerDHE(cmd_parms *cmd, void *dcfg, int flag);
diff --git a/nss_engine_config.c b/nss_engine_config.c
index c0c7155..4adff52 100644
--- a/nss_engine_config.c
+++ b/nss_engine_config.c
@@ -54,6 +54,7 @@ SSLModConfigRec *nss_config_global_create(server_rec *s)
mc->aRandSeed = apr_array_make(pool, 4,
sizeof(ssl_randseed_t));
mc->semid = 0;
+ mc->skip_permission_check = PR_FALSE;
apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY,
apr_pool_cleanup_null,
@@ -803,6 +804,16 @@ const char *nss_cmd_NSSRandomSeed(cmd_parms *cmd,
return NULL;
}
+const char *nss_cmd_NSSSkipPermissionCheck(cmd_parms *cmd,
+ void *dcfg, int flag)
+{
+ SSLModConfigRec *mc = myModConfig(cmd->server);
+
+ mc->skip_permission_check = flag ? PR_TRUE: PR_FALSE;
+
+ return NULL;
+}
+
const char *nss_cmd_NSSSessionTickets(cmd_parms *cmd,
void *dcfg, int flag)
{
diff --git a/nss_engine_init.c b/nss_engine_init.c
index 81b2434..2a6ce58 100644
--- a/nss_engine_init.c
+++ b/nss_engine_init.c
@@ -27,6 +27,8 @@
#include "ocsp.h"
#include "keyhi.h"
#include "cert.h"
+#include <sys/types.h>
+#include <pwd.h>
static SECStatus ownBadCertHandler(void *arg, PRFileDesc * socket);
static SECStatus ownHandshakeCallback(PRFileDesc * socket, void *arg);
@@ -49,6 +51,32 @@ static char *version_components[] = {
NULL
};
+/* See if a uid or gid can read a file at a given path. Ignore world
+ * read permissions.
+ *
+ * Return 0 on failure or file doesn't exist
+ * Return 1 on success
+ */
+static int check_path(uid_t uid, gid_t gid, char *filepath, apr_pool_t *p)
+{
+ apr_finfo_t finfo;
+ int rv;
+
+ if ((rv = apr_stat(&finfo, filepath, APR_FINFO_PROT | APR_FINFO_OWNER,
+ p)) == APR_SUCCESS) {
+ if (((uid == finfo.user) &&
+ ((finfo.protection & APR_FPROT_UREAD))) ||
+ ((gid == finfo.group) &&
+ ((finfo.protection & APR_FPROT_GREAD)))
+ )
+ {
+ return 1;
+ }
+ return 0;
+ }
+ return 0;
+}
+
static char *nss_add_version_component(apr_pool_t *p,
server_rec *s,
char *name)
@@ -130,6 +158,68 @@ static void nss_init_SSLLibrary(server_rec *base_server, apr_pool_t *p)
}
}
+ /* Assuming everything is ok so far, check the cert database permissions
+ * for the server user before Apache starts forking. We die now or
+ * get stuck in an endless loop not able to read the NSS database.
+ */
+ if (mc->nInitCount == 1) {
+ if (mc->skip_permission_check == PR_FALSE) {
+ char filepath[1024];
+ struct passwd *pw = NULL;
+
+ pw = getpwnam(mc->user);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server,
+ "Checking permissions for user %s: uid %d gid %d",
+ mc->user, pw->pw_uid, pw->pw_gid);
+
+ if (strncasecmp(mc->pCertificateDatabase, "sql:", 4) == 0) {
+ apr_snprintf(filepath, 1024, "%s/key4.db",
+ mc->pCertificateDatabase+4);
+ if (!(check_path(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 key "
+ "database %s.", mc->user, filepath);
+ nss_die();
+ }
+ apr_snprintf(filepath, 1024, "%s/cert9.db",
+ mc->pCertificateDatabase+4);
+ if (!(check_path(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 cert "
+ "database %s.", mc->user, filepath);
+ nss_die();
+ }
+ } else {
+ apr_snprintf(filepath, 1024, "%s/key3.db",
+ mc->pCertificateDatabase);
+ if (!(check_path(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 key "
+ "database %s.", mc->user, filepath);
+ nss_die();
+ }
+ apr_snprintf(filepath, 1024, "%s/cert8.db",
+ mc->pCertificateDatabase);
+ if (!(check_path(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 cert "
+ "database %s.", mc->user, filepath);
+ nss_die();
+ }
+ apr_snprintf(filepath, 1024, "%s/secmod.db",
+ mc->pCertificateDatabase);
+ if (!(check_path(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 "
+ "database %s.", mc->user, filepath);
+ nss_die();
+ }
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, base_server,
+ "Permissions check out ok");
+ }
+ }
+
/* We need to be in the same directory as libnssckbi.so to load the
* root certificates properly.
*/
@@ -155,6 +245,7 @@ static void nss_init_SSLLibrary(server_rec *base_server, apr_pool_t *p)
else
return;
}
+
/* Initialize NSS and open the certificate database read-only. */
rv = NSS_Initialize(mc->pCertificateDatabase, mc->pDBPrefix, mc->pDBPrefix, "secmod.db", NSS_INIT_READONLY);
if (chdir(cwd) != 0) {
@@ -168,39 +259,16 @@ static void nss_init_SSLLibrary(server_rec *base_server, apr_pool_t *p)
return;
}
- /* Assuming everything is ok so far, check the cert database password(s). */
if (rv != SECSuccess) {
- apr_finfo_t finfo;
- char keypath[1024];
- int rv;
- uid_t user_id;
- gid_t gid;
-
- user_id = ap_uname2id(mc->user);
- gid = getegid();
-
NSS_Shutdown();
+
ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
"NSS_Initialize failed. Certificate database: %s.", mc->pCertificateDatabase != NULL ? mc->pCertificateDatabase : "not set in configuration");
nss_log_nss_error(APLOG_MARK, APLOG_ERR, base_server);
- apr_snprintf(keypath, 1024, "%s/key3.db", mc->pCertificateDatabase);
- if ((rv = apr_stat(&finfo, keypath, APR_FINFO_PROT | APR_FINFO_OWNER,
- p)) == APR_SUCCESS) {
- if (((user_id == finfo.user) &&
- (!(finfo.protection & APR_FPROT_UREAD))) ||
- ((gid == finfo.group) &&
- (!(finfo.protection & APR_FPROT_GREAD))) ||
- (!(finfo.protection & APR_FPROT_WREAD))
- )
- {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
- "Server user lacks read access to NSS database.");
- }
- } else {
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
- "Does the NSS database exist?");
- }
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, base_server,
+ "Does the NSS database exist?");
+
nss_die();
}