/* Copyright 2001-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mod_nss.h" /* _________________________________________________________________ ** ** Support for Global Configuration ** _________________________________________________________________ */ #define SSL_MOD_CONFIG_KEY "nss_module" SSLModConfigRec *nss_config_global_create(server_rec *s) { apr_pool_t *pool = s->process->pool; SSLModConfigRec *mc; void *vmc; apr_pool_userdata_get(&vmc, SSL_MOD_CONFIG_KEY, pool); if (vmc) { return vmc; /* reused for lifetime of the server */ } /* * allocate an own subpool which survives server restarts */ mc = (SSLModConfigRec *)apr_palloc(pool, sizeof(*mc)); mc->pPool = pool; /* * initialize per-module configuration */ mc->nInitCount = 0; mc->pCertificateDatabase = NULL; mc->pDBPrefix = NULL; mc->session_cache_size = UNSET; mc->session_cache_timeout = UNSET; mc->ssl3_session_cache_timeout = UNSET; mc->pphrase_dialog_helper = NULL; mc->pphrase_dialog_path = NULL; mc->aRandSeed = apr_array_make(pool, 4, sizeof(ssl_randseed_t)); mc->semid = 0; apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, apr_pool_cleanup_null, pool); return mc; } /* _________________________________________________________________ ** ** Configuration handling ** _________________________________________________________________ */ static void modnss_ctx_init(modnss_ctx_t *mctx) { mctx->sc = NULL; /* set during module init */ mctx->as_server = PR_TRUE; mctx->ssl3 = PR_FALSE; mctx->tls = PR_FALSE; mctx->tlsrollback = PR_FALSE; #ifdef SSL_ENABLE_RENEGOTIATION mctx->enablerenegotiation = PR_FALSE; mctx->requiresafenegotiation = PR_FALSE; #endif mctx->enforce = PR_TRUE; mctx->nickname = NULL; #ifdef NSS_ENABLE_ECC mctx->eccnickname = NULL; mctx->eccservercert = NULL; mctx->eccserverkey = NULL; #endif mctx->servercert = NULL; mctx->serverkey = NULL; mctx->model = NULL; mctx->auth.protocols = NULL; mctx->auth.cipher_suite = NULL; mctx->auth.verify_mode = SSL_CVERIFY_UNSET; } static void modnss_ctx_init_proxy(SSLSrvConfigRec *sc, apr_pool_t *p) { modnss_ctx_t *mctx; mctx = sc->proxy = apr_palloc(p, sizeof(*sc->proxy)); modnss_ctx_init(mctx); mctx->as_server = PR_FALSE; } static void modnss_ctx_init_server(SSLSrvConfigRec *sc, apr_pool_t *p) { modnss_ctx_t *mctx; mctx = sc->server = apr_palloc(p, sizeof(*sc->server)); modnss_ctx_init(mctx); mctx->as_server = PR_TRUE; } static SSLSrvConfigRec *nss_config_server_new(apr_pool_t *p) { SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc)); sc->mc = NULL; sc->ocsp = UNSET; sc->ocsp_default = UNSET; sc->ocsp_url = NULL; sc->ocsp_name = NULL; sc->fips = UNSET; sc->enabled = UNSET; sc->sni = TRUE; sc->strict_sni_vhost_check = TRUE; sc->proxy_enabled = UNSET; sc->vhost_id = NULL; /* set during module init */ sc->vhost_id_len = 0; /* set during module init */ sc->proxy = NULL; sc->server = NULL; sc->proxy_ssl_check_peer_cn = TRUE; sc->session_tickets = FALSE; modnss_ctx_init_proxy(sc, p); modnss_ctx_init_server(sc, p); return sc; } /* * Create per-server SSL configuration */ void *nss_config_server_create(apr_pool_t *p, server_rec *s) { SSLSrvConfigRec *sc = nss_config_server_new(p); sc->mc = nss_config_global_create(s); return sc; } #define cfgMerge(el,unset) mrg->el = (add->el == (unset)) ? base->el : add->el #define cfgMergeArray(el) mrg->el = apr_array_append(p, add->el, base->el) #define cfgMergeString(el) cfgMerge(el, NULL) #define cfgMergeBool(el) cfgMerge(el, UNSET) #define cfgMergeInt(el) cfgMerge(el, UNSET) static void modnss_ctx_cfg_merge(modnss_ctx_t *base, modnss_ctx_t *add, modnss_ctx_t *mrg) { cfgMerge(auth.protocols, NULL); cfgMerge(auth.cipher_suite, NULL); cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET); cfgMerge(nickname, NULL); #ifdef NSS_ENABLE_ECC cfgMerge(eccnickname, NULL); #endif cfgMerge(enforce, PR_TRUE); #ifdef SSL_ENABLE_RENEGOTIATION cfgMerge(enablerenegotiation, PR_FALSE); cfgMerge(requiresafenegotiation, PR_FALSE); #endif } static void modnss_ctx_cfg_merge_proxy(modnss_ctx_t *base, modnss_ctx_t *add, modnss_ctx_t *mrg) { modnss_ctx_cfg_merge(base, add, mrg); } static void modnss_ctx_cfg_merge_server(modnss_ctx_t *base, modnss_ctx_t *add, modnss_ctx_t *mrg) { modnss_ctx_cfg_merge(base, add, mrg); } /* * Merge per-server SSL configurations */ void *nss_config_server_merge(apr_pool_t *p, void *basev, void *addv) { SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev; SSLSrvConfigRec *add = (SSLSrvConfigRec *)addv; SSLSrvConfigRec *mrg = nss_config_server_new(p); cfgMerge(mc, NULL); cfgMergeBool(ocsp); cfgMergeBool(ocsp_default); cfgMerge(ocsp_url, NULL); cfgMerge(ocsp_name, NULL); cfgMergeBool(fips); cfgMergeBool(enabled); cfgMergeBool(sni); cfgMergeBool(strict_sni_vhost_check); cfgMergeBool(proxy_enabled); cfgMergeBool(proxy_ssl_check_peer_cn); cfgMergeBool(session_tickets); modnss_ctx_cfg_merge_proxy(base->proxy, add->proxy, mrg->proxy); modnss_ctx_cfg_merge_server(base->server, add->server, mrg->server); return mrg; } /* * Create per-directory SSL configuration */ void *nss_config_perdir_create(apr_pool_t *p, char *dir) { SSLDirConfigRec *dc = apr_palloc(p, sizeof(*dc)); dc->bSSLRequired = FALSE; dc->aRequirement = apr_array_make(p, 4, sizeof(nss_require_t)); dc->nOptions = SSL_OPT_NONE|SSL_OPT_RELSET; dc->nOptionsAdd = SSL_OPT_NONE; dc->nOptionsDel = SSL_OPT_NONE; dc->szCipherSuite = NULL; dc->nVerifyClient = SSL_CVERIFY_UNSET; dc->szUserName = NULL; dc->nRenegBufferSize = UNSET; return dc; } const char *nss_cmd_NSSRequireSSL(cmd_parms *cmd, void *dcfg) { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; dc->bSSLRequired = TRUE; return NULL; } const char *nss_cmd_NSSRequire(cmd_parms *cmd, void *dcfg, const char *arg) { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; nss_expr *expr; nss_require_t *require; if (!(expr = nss_expr_comp(cmd->pool, (char *)arg))) { return apr_pstrcat(cmd->pool, "NSSRequire: ", nss_expr_get_error(), NULL); } require = apr_array_push(dc->aRequirement); require->cpExpr = apr_pstrdup(cmd->pool, arg); require->mpExpr = expr; return NULL; } const char *nss_cmd_NSSRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg) { SSLDirConfigRec *dc = dcfg; int val; val = atoi(arg); if (val < 0) { return apr_pstrcat(cmd->pool, "Invalid size for NSSRenegBufferSize: ", arg, NULL); } dc->nRenegBufferSize = val; return NULL; } void *nss_config_perdir_merge(apr_pool_t *p, void *basev, void *addv) { SSLDirConfigRec *base = (SSLDirConfigRec *)basev; SSLDirConfigRec *add = (SSLDirConfigRec *)addv; SSLDirConfigRec *mrg = (SSLDirConfigRec *)apr_palloc(p, sizeof(*mrg)); cfgMerge(bSSLRequired, FALSE); cfgMergeArray(aRequirement); if (add->nOptions & SSL_OPT_RELSET) { mrg->nOptionsAdd = (base->nOptionsAdd & ~(add->nOptionsDel)) | add->nOptionsAdd; mrg->nOptionsDel = (base->nOptionsDel & ~(add->nOptionsAdd)) | add->nOptionsDel; mrg->nOptions = (base->nOptions & ~(mrg->nOptionsDel)) | mrg->nOptionsAdd; } else { mrg->nOptions = add->nOptions; mrg->nOptionsAdd = add->nOptionsAdd; mrg->nOptionsDel = add->nOptionsDel; } cfgMergeString(szCipherSuite); cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET); cfgMergeString(szUserName); cfgMergeInt(nRenegBufferSize); return mrg; } const char *nss_cmd_NSSEngine(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->enabled = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSFIPS(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->fips = flag ? TRUE : FALSE; 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_NSSStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->strict_sni_vhost_check = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSOCSP(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->ocsp = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->ocsp_default = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSOCSPDefaultURL(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->ocsp_url = arg; return NULL; } const char *nss_cmd_NSSOCSPDefaultName(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->ocsp_name = arg; return NULL; } const char *nss_cmd_NSSCertificateDatabase(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); mc->pCertificateDatabase = arg; return NULL; } const char *nss_cmd_NSSDBPrefix(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); mc->pDBPrefix = arg; return NULL; } const char *nss_cmd_NSSCipherSuite(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; if (cmd->path) { dc->szCipherSuite = arg; } else { sc->server->auth.cipher_suite = arg; } return NULL; } static const char *nss_cmd_verify_parse(cmd_parms *parms, const char *arg, nss_verify_t *id) { if (strcEQ(arg, "none") || strcEQ(arg, "off")) { *id = SSL_CVERIFY_NONE; } else if (strcEQ(arg, "optional")) { *id = SSL_CVERIFY_OPTIONAL; } else if (strcEQ(arg, "require") || strcEQ(arg, "on")) { *id = SSL_CVERIFY_REQUIRE; } else if (strcEQ(arg, "optional_no_ca")) { return apr_pstrcat(parms->temp_pool, parms->cmd->name, "SSL_CVERIFY_OPTIONAL_NO_CA is not supported", NULL); } else { return apr_pstrcat(parms->temp_pool, parms->cmd->name, ": Invalid argument '", arg, "'", NULL); } return NULL; } const char *nss_cmd_NSSVerifyClient(cmd_parms *cmd, void *dcfg, const char *arg) { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; SSLSrvConfigRec *sc = mySrvConfig(cmd->server); nss_verify_t mode = SSL_CVERIFY_UNSET; const char *err; if ((err = nss_cmd_verify_parse(cmd, arg, &mode))) { return err; } if (cmd->path) { dc->nVerifyClient = mode; } else { sc->server->auth.verify_mode = mode; } return NULL; } const char *nss_cmd_NSSProtocol(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->auth.protocols = arg; return NULL; } const char *nss_cmd_NSSNickname(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->nickname = arg; return NULL; } #ifdef SSL_ENABLE_RENEGOTIATION const char *nss_cmd_NSSRenegotiation(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->enablerenegotiation = flag ? PR_TRUE : PR_FALSE; return NULL; } const char *nss_cmd_NSSRequireSafeNegotiation(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->requiresafenegotiation = flag ? PR_TRUE : PR_FALSE; return NULL; } #endif #ifdef NSS_ENABLE_ECC const char *nss_cmd_NSSECCNickname(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->eccnickname = arg; return NULL; } #endif const char *nss_cmd_NSSProxyEngine(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->proxy_enabled = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSProxyProtocol(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->proxy->auth.protocols = arg; return NULL; } const char *nss_cmd_NSSProxyCipherSuite(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->proxy->auth.cipher_suite = arg; return NULL; } const char *nss_cmd_NSSProxyNickname(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->proxy->nickname = arg; return NULL; } const char *nss_cmd_NSSProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->proxy_ssl_check_peer_cn = flag ? TRUE : FALSE; return NULL; } const char *nss_cmd_NSSEnforceValidCerts(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->server->enforce = flag ? PR_TRUE : PR_FALSE; return NULL; } const char *nss_cmd_NSSSessionCacheTimeout(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); /* Deprecated. Store a value, if any, just to complain about it later. */ mc->session_cache_timeout = atoi(arg); return NULL; } const char *nss_cmd_NSSSession3CacheTimeout(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); mc->ssl3_session_cache_timeout = atoi(arg); if (mc->ssl3_session_cache_timeout < 0) { return "NSSSession3CacheTimeout: Invalid argument"; } return NULL; } const char *nss_cmd_NSSSessionCacheSize(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); mc->session_cache_size = atoi(arg); if (mc->session_cache_size < 0) { return "NSSSessionCacheTimeout: Invalid argument"; } return NULL; } const char *nss_cmd_NSSPassPhraseDialog(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); int arglen = strlen(arg); if (strcEQ(arg, "builtin")) { mc->pphrase_dialog_type = SSL_PPTYPE_BUILTIN; mc->pphrase_dialog_path = NULL; } else if (((arglen > 5) && strEQn(arg, "file:", 5)) || ((arglen > 6) && strEQn(arg, "defer:", 6))) { apr_finfo_t finfo; apr_status_t rc; if (strEQn(arg, "file:", 5)) { mc->pphrase_dialog_type = SSL_PPTYPE_FILE; mc->pphrase_dialog_path = ap_server_root_relative(cmd->pool, arg+5); } else { mc->pphrase_dialog_type = SSL_PPTYPE_DEFER; mc->pphrase_dialog_path = ap_server_root_relative(cmd->pool, arg+6); } if (!mc->pphrase_dialog_path) return apr_pstrcat(cmd->pool, "Invalid NSSPassPhraseDialog file: path ", arg+5, NULL); rc = apr_stat(&finfo, mc->pphrase_dialog_path, APR_FINFO_TYPE|APR_FINFO_SIZE, cmd->pool); if ((rc != APR_SUCCESS) || (finfo.filetype != APR_REG)) { return apr_pstrcat(cmd->pool, "NSSPassPhraseDialog: file '", mc->pphrase_dialog_path, "' does not exist", NULL); } } return NULL; } const char *nss_cmd_NSSPassPhraseHelper(cmd_parms *cmd, void *dcfg, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); if (access(arg, R_OK|X_OK) != -1) { mc->pphrase_dialog_helper = arg; } else { return apr_pstrcat(cmd->pool, "NSSPassPhraseHelper: ", arg, " does not exist or is not executable.", NULL); } return NULL; } const char *nss_cmd_NSSRandomSeed(cmd_parms *cmd, void *dcfg, const char *arg1, const char *arg2, const char *arg3) { SSLModConfigRec *mc = myModConfig(cmd->server); const char *err; ssl_randseed_t *seed; int arg2len = strlen(arg2); if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { return err; } /* Only run through this once. Otherwise the random seed sources are * pushed into the array for each server start (and we are guaranteed 2) */ if (mc->nInitCount >= 1) { return NULL; } seed = apr_array_push(mc->aRandSeed); if (strcEQ(arg1, "startup")) { seed->nCtx = SSL_RSCTX_STARTUP; } else if (strcEQ(arg1, "connect")) { return apr_pstrcat(cmd->pool, "NSSRandomSeed: " "mod_nss doesn't do per-connection random seeding", NULL); } else { return apr_pstrcat(cmd->pool, "NSSRandomSeed: " "invalid context: `", arg1, "'", NULL); } if ((arg2len > 5) && strEQn(arg2, "file:", 5)) { seed->nSrc = SSL_RSSRC_FILE; seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5); } else if ((arg2len > 5) && strEQn(arg2, "exec:", 5)) { seed->nSrc = SSL_RSSRC_EXEC; seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5); } else if ((arg2len > 6) && strEQn(arg2, "defer:", 6)) { seed->nSrc = SSL_RSSRC_FILE; seed->cpPath = ap_server_root_relative(mc->pPool, arg2+5); } else if (strcEQ(arg2, "builtin")) { seed->nSrc = SSL_RSSRC_BUILTIN; seed->cpPath = NULL; } else { seed->nSrc = SSL_RSSRC_FILE; seed->cpPath = ap_server_root_relative(mc->pPool, arg2); } if (seed->nSrc != SSL_RSSRC_BUILTIN) { apr_finfo_t finfo; if (!seed->cpPath) { return apr_pstrcat(cmd->pool, "Invalid NSSRandomSeed path ", arg2, NULL); } if (apr_stat(&finfo, seed->cpPath, APR_FINFO_TYPE|APR_FINFO_SIZE, cmd->pool) != 0) { return apr_pstrcat(cmd->pool, "NSSRandomSeed: source path '", seed->cpPath, "' does not exist", NULL); } } if (!arg3) { seed->nBytes = 0; /* read whole file */ } else { if (seed->nSrc == SSL_RSSRC_BUILTIN) { return "NSSRandomSeed: byte specification not " "allowed for builtin seed source"; } seed->nBytes = atoi(arg3); if (seed->nBytes < 0) { return "NSSRandomSeed: invalid number of bytes specified"; } } return NULL; } const char *nss_cmd_NSSSessionTickets(cmd_parms *cmd, void *dcfg, int flag) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); sc->session_tickets = flag ? PR_TRUE : PR_FALSE; return NULL; } const char *nss_cmd_NSSUserName(cmd_parms *cmd, void *dcfg, const char *arg) { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; dc->szUserName = arg; return NULL; } const char *nss_cmd_NSSOptions(cmd_parms *cmd, void *dcfg, const char *arg) { SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; nss_opt_t opt; int first = TRUE; char action, *w; while (*arg) { w = ap_getword_conf(cmd->pool, &arg); action = NUL; if ((*w == '+') || (*w == '-')) { action = *(w++); } else if (first) { dc->nOptions = SSL_OPT_NONE; first = FALSE; } if (strcEQ(w, "StdEnvVars")) { opt = SSL_OPT_STDENVVARS; } else if (strcEQ(w, "CompatEnvVars")) { opt = SSL_OPT_COMPATENVVARS; } else if (strcEQ(w, "ExportCertData")) { opt = SSL_OPT_EXPORTCERTDATA; } else if (strcEQ(w, "FakeBasicAuth")) { opt = SSL_OPT_FAKEBASICAUTH; } else if (strcEQ(w, "StrictRequire")) { opt = SSL_OPT_STRICTREQUIRE; } else if (strcEQ(w, "OptRenegotiate")) { opt = SSL_OPT_OPTRENEGOTIATE; } else { return apr_pstrcat(cmd->pool, "NSSOptions: Illegal option '", w, "'", NULL); } if (action == '-') { dc->nOptionsAdd &= ~opt; dc->nOptionsDel |= opt; dc->nOptions &= ~opt; } else if (action == '+') { dc->nOptionsAdd |= opt; dc->nOptionsDel &= ~opt; dc->nOptions |= opt; } else { dc->nOptions = opt; dc->nOptionsAdd = opt; dc->nOptionsDel = SSL_OPT_NONE; } } return NULL; } const char *set_user(cmd_parms *cmd, void *dummy, const char *arg) { SSLModConfigRec *mc = myModConfig(cmd->server); mc->user = arg; return NULL; }