diff options
Diffstat (limited to 'modules/ssl/ssl_engine_config.c')
-rw-r--r-- | modules/ssl/ssl_engine_config.c | 2145 |
1 files changed, 2145 insertions, 0 deletions
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c new file mode 100644 index 0000000..6c10bb5 --- /dev/null +++ b/modules/ssl/ssl_engine_config.c @@ -0,0 +1,2145 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +/* _ _ + * _ __ ___ ___ __| | ___ ___| | mod_ssl + * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL + * | | | | | | (_) | (_| | \__ \__ \ | + * |_| |_| |_|\___/ \__,_|___|___/___/_| + * |_____| + * ssl_engine_config.c + * Apache Configuration Directives + */ + /* ``Damned if you do, + damned if you don't.'' + -- Unknown */ +#include "ssl_private.h" +#include "util_mutex.h" +#include "ap_provider.h" + +/* _________________________________________________________________ +** +** Support for Global Configuration +** _________________________________________________________________ +*/ + +#define SSL_MOD_CONFIG_KEY "ssl_module" + +SSLModConfigRec *ssl_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; + mc->bFixed = FALSE; + + /* + * initialize per-module configuration + */ + mc->sesscache_mode = SSL_SESS_CACHE_OFF; + mc->sesscache = NULL; + mc->pMutex = NULL; + mc->aRandSeed = apr_array_make(pool, 4, + sizeof(ssl_randseed_t)); + mc->tVHostKeys = apr_hash_make(pool); + mc->tPrivateKey = apr_hash_make(pool); +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) + mc->szCryptoDevice = NULL; +#endif +#ifdef HAVE_OCSP_STAPLING + mc->stapling_cache = NULL; + mc->stapling_cache_mutex = NULL; + mc->stapling_refresh_mutex = NULL; +#endif + + apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, + apr_pool_cleanup_null, + pool); + + return mc; +} + +void ssl_config_global_fix(SSLModConfigRec *mc) +{ + mc->bFixed = TRUE; +} + +BOOL ssl_config_global_isfixed(SSLModConfigRec *mc) +{ + return mc->bFixed; +} + +/* _________________________________________________________________ +** +** Configuration handling +** _________________________________________________________________ +*/ + +#ifdef HAVE_SSL_CONF_CMD +static apr_status_t modssl_ctx_config_cleanup(void *ctx) +{ + SSL_CONF_CTX_free(ctx); + return APR_SUCCESS; +} +#endif + +static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p) +{ + mctx->sc = NULL; /* set during module init */ + + mctx->ssl_ctx = NULL; /* set during module init */ + + mctx->pks = NULL; + mctx->pkp = NULL; + +#ifdef HAVE_TLS_SESSION_TICKETS + mctx->ticket_key = NULL; +#endif + + mctx->protocol = SSL_PROTOCOL_DEFAULT; + mctx->protocol_set = 0; + + mctx->pphrase_dialog_type = SSL_PPTYPE_UNSET; + mctx->pphrase_dialog_path = NULL; + + mctx->cert_chain = NULL; + + mctx->crl_path = NULL; + mctx->crl_file = NULL; + mctx->crl_check_mask = UNSET; + + mctx->auth.ca_cert_path = NULL; + mctx->auth.ca_cert_file = NULL; + mctx->auth.cipher_suite = NULL; + mctx->auth.verify_depth = UNSET; + mctx->auth.verify_mode = SSL_CVERIFY_UNSET; + mctx->auth.tls13_ciphers = NULL; + + mctx->ocsp_mask = UNSET; + mctx->ocsp_force_default = UNSET; + mctx->ocsp_responder = NULL; + mctx->ocsp_resptime_skew = UNSET; + mctx->ocsp_resp_maxage = UNSET; + mctx->ocsp_responder_timeout = UNSET; + mctx->ocsp_use_request_nonce = UNSET; + mctx->proxy_uri = NULL; + +/* Set OCSP Responder Certificate Verification variable */ + mctx->ocsp_noverify = UNSET; +/* Set OCSP Responder File variables */ + mctx->ocsp_verify_flags = 0; + mctx->ocsp_certs_file = NULL; + mctx->ocsp_certs = NULL; + +#ifdef HAVE_OCSP_STAPLING + mctx->stapling_enabled = UNSET; + mctx->stapling_resptime_skew = UNSET; + mctx->stapling_resp_maxage = UNSET; + mctx->stapling_cache_timeout = UNSET; + mctx->stapling_return_errors = UNSET; + mctx->stapling_fake_trylater = UNSET; + mctx->stapling_errcache_timeout = UNSET; + mctx->stapling_responder_timeout = UNSET; + mctx->stapling_force_url = NULL; +#endif + +#ifdef HAVE_SRP + mctx->srp_vfile = NULL; + mctx->srp_unknown_user_seed = NULL; + mctx->srp_vbase = NULL; +#endif +#ifdef HAVE_SSL_CONF_CMD + mctx->ssl_ctx_config = SSL_CONF_CTX_new(); + apr_pool_cleanup_register(p, mctx->ssl_ctx_config, + modssl_ctx_config_cleanup, + apr_pool_cleanup_null); + SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_FILE); + SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_SERVER); + SSL_CONF_CTX_set_flags(mctx->ssl_ctx_config, SSL_CONF_FLAG_CERTIFICATE); + mctx->ssl_ctx_param = apr_array_make(p, 5, sizeof(ssl_ctx_param_t)); +#endif + + mctx->ssl_check_peer_cn = UNSET; + mctx->ssl_check_peer_name = UNSET; + mctx->ssl_check_peer_expire = UNSET; +} + +static void modssl_ctx_init_server(SSLSrvConfigRec *sc, + apr_pool_t *p) +{ + modssl_ctx_t *mctx; + + mctx = sc->server = apr_palloc(p, sizeof(*sc->server)); + + modssl_ctx_init(mctx, p); + + mctx->pks = apr_pcalloc(p, sizeof(*mctx->pks)); + + mctx->pks->cert_files = apr_array_make(p, 3, sizeof(char *)); + mctx->pks->key_files = apr_array_make(p, 3, sizeof(char *)); + +#ifdef HAVE_TLS_SESSION_TICKETS + mctx->ticket_key = apr_pcalloc(p, sizeof(*mctx->ticket_key)); +#endif +} + +static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) +{ + SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc)); + + sc->mc = NULL; + sc->enabled = SSL_ENABLED_UNSET; + sc->vhost_id = NULL; /* set during module init */ + sc->vhost_id_len = 0; /* set during module init */ + sc->session_cache_timeout = UNSET; + sc->cipher_server_pref = UNSET; + sc->insecure_reneg = UNSET; +#ifdef HAVE_TLSEXT + sc->strict_sni_vhost_check = SSL_ENABLED_UNSET; +#endif +#ifdef HAVE_FIPS + sc->fips = UNSET; +#endif +#ifndef OPENSSL_NO_COMP + sc->compression = UNSET; +#endif + sc->session_tickets = UNSET; + + modssl_ctx_init_server(sc, p); + + return sc; +} + +/* + * Create per-server SSL configuration + */ +void *ssl_config_server_create(apr_pool_t *p, server_rec *s) +{ + SSLSrvConfigRec *sc = ssl_config_server_new(p); + + sc->mc = ssl_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, base->el, add->el) +#define cfgMergeString(el) cfgMerge(el, NULL) +#define cfgMergeBool(el) cfgMerge(el, UNSET) +#define cfgMergeInt(el) cfgMerge(el, UNSET) + +/* + * Merge per-server SSL configurations + */ + +static void modssl_ctx_cfg_merge(apr_pool_t *p, + modssl_ctx_t *base, + modssl_ctx_t *add, + modssl_ctx_t *mrg) +{ + if (add->protocol_set) { + mrg->protocol = add->protocol; + } + else { + mrg->protocol = base->protocol; + } + + cfgMerge(pphrase_dialog_type, SSL_PPTYPE_UNSET); + cfgMergeString(pphrase_dialog_path); + + cfgMergeString(cert_chain); + + cfgMerge(crl_path, NULL); + cfgMerge(crl_file, NULL); + cfgMergeInt(crl_check_mask); + + cfgMergeString(auth.ca_cert_path); + cfgMergeString(auth.ca_cert_file); + cfgMergeString(auth.cipher_suite); + cfgMergeInt(auth.verify_depth); + cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET); + cfgMergeString(auth.tls13_ciphers); + + cfgMergeInt(ocsp_mask); + cfgMergeBool(ocsp_force_default); + cfgMerge(ocsp_responder, NULL); + cfgMergeInt(ocsp_resptime_skew); + cfgMergeInt(ocsp_resp_maxage); + cfgMergeInt(ocsp_responder_timeout); + cfgMergeBool(ocsp_use_request_nonce); + cfgMerge(proxy_uri, NULL); + +/* Set OCSP Responder Certificate Verification directive */ + cfgMergeBool(ocsp_noverify); +/* Set OCSP Responder File directive for importing */ + cfgMerge(ocsp_certs_file, NULL); + +#ifdef HAVE_OCSP_STAPLING + cfgMergeBool(stapling_enabled); + cfgMergeInt(stapling_resptime_skew); + cfgMergeInt(stapling_resp_maxage); + cfgMergeInt(stapling_cache_timeout); + cfgMergeBool(stapling_return_errors); + cfgMergeBool(stapling_fake_trylater); + cfgMergeInt(stapling_errcache_timeout); + cfgMergeInt(stapling_responder_timeout); + cfgMerge(stapling_force_url, NULL); +#endif + +#ifdef HAVE_SRP + cfgMergeString(srp_vfile); + cfgMergeString(srp_unknown_user_seed); +#endif + +#ifdef HAVE_SSL_CONF_CMD + cfgMergeArray(ssl_ctx_param); +#endif + + cfgMergeBool(ssl_check_peer_cn); + cfgMergeBool(ssl_check_peer_name); + cfgMergeBool(ssl_check_peer_expire); +} + +static void modssl_ctx_cfg_merge_certkeys_array(apr_pool_t *p, + apr_array_header_t *base, + apr_array_header_t *add, + apr_array_header_t *mrg) +{ + int i; + + /* + * pick up to CERTKEYS_IDX_MAX+1 entries from "add" (in which case they + * they "knock out" their corresponding entries in "base", emulating + * the behavior with cfgMergeString in releases up to 2.4.7) + */ + for (i = 0; i < add->nelts && i <= CERTKEYS_IDX_MAX; i++) { + APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *); + } + + /* add remaining ones from "base" */ + while (i < base->nelts) { + APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(base, i, const char *); + i++; + } + + /* and finally, append the rest of "add" (if there are any) */ + for (i = CERTKEYS_IDX_MAX+1; i < add->nelts; i++) { + APR_ARRAY_PUSH(mrg, const char *) = APR_ARRAY_IDX(add, i, const char *); + } +} + +static void modssl_ctx_cfg_merge_server(apr_pool_t *p, + modssl_ctx_t *base, + modssl_ctx_t *add, + modssl_ctx_t *mrg) +{ + modssl_ctx_cfg_merge(p, base, add, mrg); + + /* + * For better backwards compatibility with releases up to 2.4.7, + * merging global and vhost-level SSLCertificateFile and + * SSLCertificateKeyFile directives needs special treatment. + * See also PR 56306 and 56353. + */ + modssl_ctx_cfg_merge_certkeys_array(p, base->pks->cert_files, + add->pks->cert_files, + mrg->pks->cert_files); + modssl_ctx_cfg_merge_certkeys_array(p, base->pks->key_files, + add->pks->key_files, + mrg->pks->key_files); + + cfgMergeString(pks->ca_name_path); + cfgMergeString(pks->ca_name_file); + +#ifdef HAVE_TLS_SESSION_TICKETS + cfgMergeString(ticket_key->file_path); +#endif +} + +void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) +{ + SSLSrvConfigRec *base = (SSLSrvConfigRec *)basev; + SSLSrvConfigRec *add = (SSLSrvConfigRec *)addv; + SSLSrvConfigRec *mrg = ssl_config_server_new(p); + + cfgMerge(mc, NULL); + cfgMerge(enabled, SSL_ENABLED_UNSET); + cfgMergeInt(session_cache_timeout); + cfgMergeBool(cipher_server_pref); + cfgMergeBool(insecure_reneg); +#ifdef HAVE_TLSEXT + cfgMerge(strict_sni_vhost_check, SSL_ENABLED_UNSET); +#endif +#ifdef HAVE_FIPS + cfgMergeBool(fips); +#endif +#ifndef OPENSSL_NO_COMP + cfgMergeBool(compression); +#endif + cfgMergeBool(session_tickets); + + modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server); + + return mrg; +} + +/* + * Create per-directory SSL configuration + */ + +static void modssl_ctx_init_proxy(SSLDirConfigRec *dc, + apr_pool_t *p) +{ + modssl_ctx_t *mctx; + + mctx = dc->proxy = apr_palloc(p, sizeof(*dc->proxy)); + + modssl_ctx_init(mctx, p); + + mctx->pkp = apr_palloc(p, sizeof(*mctx->pkp)); + + mctx->pkp->cert_file = NULL; + mctx->pkp->cert_path = NULL; + mctx->pkp->ca_cert_file = NULL; + mctx->pkp->certs = NULL; + mctx->pkp->ca_certs = NULL; +} + +void *ssl_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(ssl_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->nVerifyDepth = UNSET; + + dc->szUserName = NULL; + + dc->nRenegBufferSize = UNSET; + + dc->proxy_enabled = UNSET; + modssl_ctx_init_proxy(dc, p); + dc->proxy_post_config = FALSE; + + return dc; +} + +/* + * Merge per-directory SSL configurations + */ + +static void modssl_ctx_cfg_merge_proxy(apr_pool_t *p, + modssl_ctx_t *base, + modssl_ctx_t *add, + modssl_ctx_t *mrg) +{ + modssl_ctx_cfg_merge(p, base, add, mrg); + + cfgMergeString(pkp->cert_file); + cfgMergeString(pkp->cert_path); + cfgMergeString(pkp->ca_cert_file); + cfgMergeString(pkp->certs); + cfgMergeString(pkp->ca_certs); +} + +void *ssl_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); + cfgMergeInt(nVerifyDepth); + + cfgMergeString(szUserName); + + cfgMergeInt(nRenegBufferSize); + + mrg->proxy_post_config = add->proxy_post_config; + if (!mrg->proxy_post_config) { + cfgMergeBool(proxy_enabled); + modssl_ctx_init_proxy(mrg, p); + modssl_ctx_cfg_merge_proxy(p, base->proxy, add->proxy, mrg->proxy); + + /* Since ssl_proxy_section_post_config() hook won't be called if there + * is no SSLProxy* in this dir config, the ssl_ctx may still be NULL + * here at runtime. Merging it is either a no-op (NULL => NULL) because + * we are still before post config, or we really want to reuse the one + * from the upper/server context (outside of <Proxy> sections). + */ + cfgMerge(proxy->ssl_ctx, NULL); + } + else { + /* The post_config hook has already merged and initialized the + * proxy context, use it. + */ + mrg->proxy_enabled = add->proxy_enabled; + mrg->proxy = add->proxy; + } + + return mrg; +} + +/* Simply merge conf with base into conf, no third party. */ +void ssl_config_proxy_merge(apr_pool_t *p, + SSLDirConfigRec *base, + SSLDirConfigRec *conf) +{ + if (conf->proxy_enabled == UNSET) { + conf->proxy_enabled = base->proxy_enabled; + } + modssl_ctx_cfg_merge_proxy(p, base->proxy, conf->proxy, conf->proxy); +} + +/* + * Configuration functions for particular directives + */ + +const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + int arglen = strlen(arg); + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + if (strcEQ(arg, "builtin")) { + sc->server->pphrase_dialog_type = SSL_PPTYPE_BUILTIN; + sc->server->pphrase_dialog_path = NULL; + } + else if ((arglen > 5) && strEQn(arg, "exec:", 5)) { + sc->server->pphrase_dialog_type = SSL_PPTYPE_FILTER; + sc->server->pphrase_dialog_path = + ap_server_root_relative(cmd->pool, arg+5); + if (!sc->server->pphrase_dialog_path) { + return apr_pstrcat(cmd->pool, + "Invalid SSLPassPhraseDialog exec: path ", + arg+5, NULL); + } + if (!ssl_util_path_check(SSL_PCM_EXISTS, + sc->server->pphrase_dialog_path, + cmd->pool)) + { + return apr_pstrcat(cmd->pool, + "SSLPassPhraseDialog: file '", + sc->server->pphrase_dialog_path, + "' does not exist", NULL); + } + + } + else if ((arglen > 1) && (arg[0] == '|')) { + sc->server->pphrase_dialog_type = SSL_PPTYPE_PIPE; + sc->server->pphrase_dialog_path = arg + 1; + } + else { + return "SSLPassPhraseDialog: Invalid argument"; + } + + return NULL; +} + +#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) +const char *ssl_cmd_SSLCryptoDevice(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLModConfigRec *mc = myModConfig(cmd->server); + const char *err; + ENGINE *e; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + if (strcEQ(arg, "builtin")) { + mc->szCryptoDevice = NULL; + } + else if ((e = ENGINE_by_id(arg))) { + mc->szCryptoDevice = arg; + ENGINE_free(e); + } + else { + err = "SSLCryptoDevice: Invalid argument; must be one of: " + "'builtin' (none)"; + e = ENGINE_get_first(); + while (e) { + err = apr_pstrcat(cmd->pool, err, ", '", ENGINE_get_id(e), + "' (", ENGINE_get_name(e), ")", NULL); + /* Iterate; this call implicitly decrements the refcount + * on the 'old' e, per the docs in engine.h. */ + e = ENGINE_get_next(e); + } + return err; + } + + return NULL; +} +#endif + +const char *ssl_cmd_SSLRandomSeed(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; + } + + if (ssl_config_global_isfixed(mc)) { + return NULL; + } + + seed = apr_array_push(mc->aRandSeed); + + if (strcEQ(arg1, "startup")) { + seed->nCtx = SSL_RSCTX_STARTUP; + } + else if (strcEQ(arg1, "connect")) { + seed->nCtx = SSL_RSCTX_CONNECT; + } + else { + return apr_pstrcat(cmd->pool, "SSLRandomSeed: " + "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 > 4) && strEQn(arg2, "egd:", 4)) { +#ifdef HAVE_RAND_EGD + seed->nSrc = SSL_RSSRC_EGD; + seed->cpPath = ap_server_root_relative(mc->pPool, arg2+4); +#else + return apr_pstrcat(cmd->pool, "Invalid SSLRandomSeed entropy source `", + arg2, "': This version of " MODSSL_LIBRARY_NAME + " does not support the Entropy Gathering Daemon " + "(EGD).", NULL); +#endif + } + 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) { + if (!seed->cpPath) { + return apr_pstrcat(cmd->pool, + "Invalid SSLRandomSeed path ", + arg2, NULL); + } + if (!ssl_util_path_check(SSL_PCM_EXISTS, seed->cpPath, cmd->pool)) { + return apr_pstrcat(cmd->pool, + "SSLRandomSeed: source path '", + seed->cpPath, "' does not exist", NULL); + } + } + + if (!arg3) { + seed->nBytes = 0; /* read whole file */ + } + else { + if (seed->nSrc == SSL_RSSRC_BUILTIN) { + return "SSLRandomSeed: byte specification not " + "allowed for builtin seed source"; + } + + seed->nBytes = atoi(arg3); + + if (seed->nBytes < 0) { + return "SSLRandomSeed: invalid number of bytes specified"; + } + } + + return NULL; +} + +const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + if (!strcasecmp(arg, "On")) { + sc->enabled = SSL_ENABLED_TRUE; + return NULL; + } + else if (!strcasecmp(arg, "Off")) { + sc->enabled = SSL_ENABLED_FALSE; + return NULL; + } + else if (!strcasecmp(arg, "Optional")) { + sc->enabled = SSL_ENABLED_OPTIONAL; + return NULL; + } + + return "Argument must be On, Off, or Optional"; +} + +const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag) +{ +#ifdef HAVE_FIPS + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); +#endif + const char *err; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + +#ifdef HAVE_FIPS + if ((sc->fips != UNSET) && (sc->fips != (BOOL)(flag ? TRUE : FALSE))) + return "Conflicting SSLFIPS options, cannot be both On and Off"; + sc->fips = flag ? TRUE : FALSE; +#else + if (flag) + return "SSLFIPS invalid, rebuild httpd and openssl compiled for FIPS"; +#endif + + return NULL; +} + +const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd, + void *dcfg, + const char *arg1, const char *arg2) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + if (arg2 == NULL) { + arg2 = arg1; + arg1 = "SSL"; + } + + if (!strcmp("SSL", arg1)) { + /* always disable null and export ciphers */ + arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL); + if (cmd->path) { + dc->szCipherSuite = arg2; + } + else { + sc->server->auth.cipher_suite = arg2; + } + return NULL; + } +#if SSL_HAVE_PROTOCOL_TLSV1_3 + else if (!strcmp("TLSv1.3", arg1)) { + if (cmd->path) { + return "TLSv1.3 ciphers cannot be set inside a directory context"; + } + sc->server->auth.tls13_ciphers = arg2; + return NULL; + } +#endif + return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL); +} + +#define SSL_FLAGS_CHECK_FILE \ + (SSL_PCM_EXISTS|SSL_PCM_ISREG|SSL_PCM_ISNONZERO) + +#define SSL_FLAGS_CHECK_DIR \ + (SSL_PCM_EXISTS|SSL_PCM_ISDIR) + +static const char *ssl_cmd_check_file(cmd_parms *parms, + const char **file) +{ + const char *filepath = ap_server_root_relative(parms->pool, *file); + + if (!filepath) { + return apr_pstrcat(parms->pool, parms->cmd->name, + ": Invalid file path ", *file, NULL); + } + *file = filepath; + + if (ssl_util_path_check(SSL_FLAGS_CHECK_FILE, *file, parms->pool)) { + return NULL; + } + + return apr_pstrcat(parms->pool, parms->cmd->name, + ": file '", *file, + "' does not exist or is empty", NULL); + +} + +const char *ssl_cmd_SSLCompression(cmd_parms *cmd, void *dcfg, int flag) +{ +#if !defined(OPENSSL_NO_COMP) + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); +#ifndef SSL_OP_NO_COMPRESSION + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err) + return "This version of OpenSSL does not support enabling " + "SSLCompression within <VirtualHost> sections."; +#endif + if (flag) { + /* Some (packaged) versions of OpenSSL do not support + * compression by default. Enabling this directive would not + * have the desired effect, so fail with an error. */ + STACK_OF(SSL_COMP) *meths = SSL_COMP_get_compression_methods(); + + if (sk_SSL_COMP_num(meths) == 0) { + return "This version of OpenSSL does not have any compression methods " + "available, cannot enable SSLCompression."; + } + } + sc->compression = flag ? TRUE : FALSE; + return NULL; +#else + return "Setting Compression mode unsupported; not implemented by the SSL library"; +#endif +} + +const char *ssl_cmd_SSLHonorCipherOrder(cmd_parms *cmd, void *dcfg, int flag) +{ +#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->cipher_server_pref = flag?TRUE:FALSE; + return NULL; +#else + return "SSLHonorCipherOrder unsupported; not implemented by the SSL library"; +#endif +} + +const char *ssl_cmd_SSLSessionTickets(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); +#ifndef SSL_OP_NO_TICKET + return "This version of OpenSSL does not support using " + "SSLSessionTickets."; +#endif + sc->session_tickets = flag ? TRUE : FALSE; + return NULL; +} + +const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag) +{ +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->insecure_reneg = flag?TRUE:FALSE; + return NULL; +#else + return "The SSLInsecureRenegotiation directive is not available " + "with this SSL library"; +#endif +} + + +static const char *ssl_cmd_check_dir(cmd_parms *parms, + const char **dir) +{ + const char *dirpath = ap_server_root_relative(parms->pool, *dir); + + if (!dirpath) { + return apr_pstrcat(parms->pool, parms->cmd->name, + ": Invalid dir path ", *dir, NULL); + } + *dir = dirpath; + + if (ssl_util_path_check(SSL_FLAGS_CHECK_DIR, *dir, parms->pool)) { + return NULL; + } + + return apr_pstrcat(parms->pool, parms->cmd->name, + ": directory '", *dir, + "' does not exist", NULL); + +} + +const char *ssl_cmd_SSLCertificateFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + *(const char **)apr_array_push(sc->server->pks->cert_files) = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + *(const char **)apr_array_push(sc->server->pks->key_files) = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->cert_chain = arg; + + return NULL; +} + +#ifdef HAVE_TLS_SESSION_TICKETS +const char *ssl_cmd_SSLSessionTicketKeyFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->ticket_key->file_path = arg; + + return NULL; +} +#endif + +#define NO_PER_DIR_SSL_CA \ + "Your SSL library does not have support for per-directory CA" + +const char *ssl_cmd_SSLCACertificatePath(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + if (cmd->path) { + return NO_PER_DIR_SSL_CA; + } + + /* XXX: bring back per-dir */ + sc->server->auth.ca_cert_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCACertificateFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + /*SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;*/ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + if (cmd->path) { + return NO_PER_DIR_SSL_CA; + } + + /* XXX: bring back per-dir */ + sc->server->auth.ca_cert_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCADNRequestPath(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + sc->server->pks->ca_name_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCADNRequestFile(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->pks->ca_name_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCARevocationPath(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + sc->server->crl_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLCARevocationFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->crl_file = arg; + + return NULL; +} + +static const char *ssl_cmd_crlcheck_parse(cmd_parms *parms, + const char *arg, + int *mask) +{ + const char *w; + + w = ap_getword_conf(parms->temp_pool, &arg); + if (strcEQ(w, "none")) { + *mask = SSL_CRLCHECK_NONE; + } + else if (strcEQ(w, "leaf")) { + *mask = SSL_CRLCHECK_LEAF; + } + else if (strcEQ(w, "chain")) { + *mask = SSL_CRLCHECK_CHAIN; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", w, "'", + NULL); + } + + while (*arg) { + w = ap_getword_conf(parms->temp_pool, &arg); + if (strcEQ(w, "no_crl_for_cert_ok")) { + *mask |= SSL_CRLCHECK_NO_CRL_FOR_CERT_OK; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", w, "'", + NULL); + } + } + + return NULL; +} + +const char *ssl_cmd_SSLCARevocationCheck(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + return ssl_cmd_crlcheck_parse(cmd, arg, &sc->server->crl_check_mask); +} + +static const char *ssl_cmd_verify_parse(cmd_parms *parms, + const char *arg, + ssl_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")) { + *id = SSL_CVERIFY_OPTIONAL_NO_CA; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", arg, "'", + NULL); + } + + return NULL; +} + +const char *ssl_cmd_SSLVerifyClient(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + ssl_verify_t mode = SSL_CVERIFY_NONE; + const char *err; + + if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) { + return err; + } + + if (cmd->path) { + dc->nVerifyClient = mode; + } + else { + sc->server->auth.verify_mode = mode; + } + + return NULL; +} + +static const char *ssl_cmd_verify_depth_parse(cmd_parms *parms, + const char *arg, + int *depth) +{ + if ((*depth = atoi(arg)) >= 0) { + return NULL; + } + + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", arg, "'", + NULL); +} + +const char *ssl_cmd_SSLVerifyDepth(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + int depth; + const char *err; + + if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) { + return err; + } + + if (cmd->path) { + dc->nVerifyDepth = depth; + } + else { + sc->server->auth.verify_depth = depth; + } + + return NULL; +} + +const char *ssl_cmd_SSLSessionCache(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLModConfigRec *mc = myModConfig(cmd->server); + const char *err, *sep, *name; + long enabled_flags; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + /* The OpenSSL session cache mode must have both the flags + * SSL_SESS_CACHE_SERVER and SSL_SESS_CACHE_NO_INTERNAL set if a + * session cache is configured; NO_INTERNAL prevents the + * OpenSSL-internal session cache being used in addition to the + * "external" (mod_ssl-provided) cache, which otherwise causes + * additional memory consumption. */ + enabled_flags = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL; + + if (strcEQ(arg, "none")) { + /* Nothing to do; session cache will be off. */ + } + else if (strcEQ(arg, "nonenotnull")) { + /* ### Having a separate mode for this seems logically + * unnecessary; the stated purpose of sending non-empty + * session IDs would be better fixed in OpenSSL or simply + * doing it by default if "none" is used. */ + mc->sesscache_mode = enabled_flags; + } + else { + /* Argument is of form 'name:args' or just 'name'. */ + sep = ap_strchr_c(arg, ':'); + if (sep) { + name = apr_pstrmemdup(cmd->pool, arg, sep - arg); + sep++; + } + else { + name = arg; + } + + /* Find the provider of given name. */ + mc->sesscache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, + name, + AP_SOCACHE_PROVIDER_VERSION); + if (mc->sesscache) { + /* Cache found; create it, passing anything beyond the colon. */ + mc->sesscache_mode = enabled_flags; + err = mc->sesscache->create(&mc->sesscache_context, sep, + cmd->temp_pool, cmd->pool); + } + else { + apr_array_header_t *name_list; + const char *all_names; + + /* Build a comma-separated list of all registered provider + * names: */ + name_list = ap_list_provider_names(cmd->pool, + AP_SOCACHE_PROVIDER_GROUP, + AP_SOCACHE_PROVIDER_VERSION); + all_names = apr_array_pstrcat(cmd->pool, name_list, ','); + + err = apr_psprintf(cmd->pool, "'%s' session cache not supported " + "(known names: %s). Maybe you need to load the " + "appropriate socache module (mod_socache_%s?).", + name, all_names, name); + } + } + + if (err) { + return apr_psprintf(cmd->pool, "SSLSessionCache: %s", err); + } + + return NULL; +} + +const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->session_cache_timeout = atoi(arg); + + if (sc->session_cache_timeout < 0) { + return "SSLSessionCacheTimeout: Invalid argument"; + } + + return NULL; +} + +const char *ssl_cmd_SSLOptions(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + ssl_opt_t opt; + int first = TRUE; + char action, *w; + + while (*arg) { + w = ap_getword_conf(cmd->temp_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, "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 if (strcEQ(w, "LegacyDNStringFormat")) { + opt = SSL_OPT_LEGACYDNFORMAT; + } + else { + return apr_pstrcat(cmd->pool, + "SSLOptions: 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 *ssl_cmd_SSLRequireSSL(cmd_parms *cmd, void *dcfg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->bSSLRequired = TRUE; + + return NULL; +} + +const char *ssl_cmd_SSLRequire(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + ap_expr_info_t *info = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t)); + ssl_require_t *require; + const char *errstring; + + info->flags = AP_EXPR_FLAG_SSL_EXPR_COMPAT; + info->filename = cmd->directive->filename; + info->line_number = cmd->directive->line_num; + info->module_index = APLOG_MODULE_INDEX; + errstring = ap_expr_parse(cmd->pool, cmd->temp_pool, info, arg, NULL); + if (errstring) { + return apr_pstrcat(cmd->pool, "SSLRequire: ", errstring, NULL); + } + + require = apr_array_push(dc->aRequirement); + require->cpExpr = arg; + require->mpExpr = info; + + return NULL; +} + +const char *ssl_cmd_SSLRenegBufferSize(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 SSLRenegBufferSize: ", + arg, NULL); + } + dc->nRenegBufferSize = val; + + return NULL; +} + +static const char *ssl_cmd_protocol_parse(cmd_parms *parms, + const char *arg, + ssl_proto_t *options) +{ + ssl_proto_t thisopt; + + *options = SSL_PROTOCOL_NONE; + + while (*arg) { + char *w = ap_getword_conf(parms->temp_pool, &arg); + char action = '\0'; + + if ((*w == '+') || (*w == '-')) { + action = *(w++); + } + + if (strcEQ(w, "SSLv2")) { + if (action == '-') { + continue; + } + else { + return "SSLProtocol: SSLv2 is no longer supported"; + } + } + else if (strcEQ(w, "SSLv3")) { +#ifdef OPENSSL_NO_SSL3 + if (action != '-') { + return "SSLv3 not supported by this version of OpenSSL"; + } + /* Nothing to do, the flag is not present to be toggled */ + continue; +#else + thisopt = SSL_PROTOCOL_SSLV3; +#endif + } + else if (strcEQ(w, "TLSv1")) { + thisopt = SSL_PROTOCOL_TLSV1; + } +#ifdef HAVE_TLSV1_X + else if (strcEQ(w, "TLSv1.1")) { + thisopt = SSL_PROTOCOL_TLSV1_1; + } + else if (strcEQ(w, "TLSv1.2")) { + thisopt = SSL_PROTOCOL_TLSV1_2; + } + else if (SSL_HAVE_PROTOCOL_TLSV1_3 && strcEQ(w, "TLSv1.3")) { + thisopt = SSL_PROTOCOL_TLSV1_3; + } +#endif + else if (strcEQ(w, "all")) { + thisopt = SSL_PROTOCOL_ALL; + } + else { + return apr_pstrcat(parms->temp_pool, + parms->cmd->name, + ": Illegal protocol '", w, "'", NULL); + } + + if (action == '-') { + *options &= ~thisopt; + } + else if (action == '+') { + *options |= thisopt; + } + else { + if (*options != SSL_PROTOCOL_NONE) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, parms->server, APLOGNO(02532) + "%s: Protocol '%s' overrides already set parameter(s). " + "Check if a +/- prefix is missing.", + parms->cmd->name, w); + } + *options = thisopt; + } + } + + return NULL; +} + +const char *ssl_cmd_SSLProtocol(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->protocol_set = 1; + return ssl_cmd_protocol_parse(cmd, arg, &sc->server->protocol); +} + +const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->proxy_enabled = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLProxyProtocol(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->proxy->protocol_set = 1; + return ssl_cmd_protocol_parse(cmd, arg, &dc->proxy->protocol); +} + +const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd, + void *dcfg, + const char *arg1, const char *arg2) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + if (arg2 == NULL) { + arg2 = arg1; + arg1 = "SSL"; + } + + if (!strcmp("SSL", arg1)) { + /* always disable null and export ciphers */ + arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL); + dc->proxy->auth.cipher_suite = arg2; + return NULL; + } +#if SSL_HAVE_PROTOCOL_TLSV1_3 + else if (!strcmp("TLSv1.3", arg1)) { + dc->proxy->auth.tls13_ciphers = arg2; + return NULL; + } +#endif + return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL); +} + +const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + ssl_verify_t mode = SSL_CVERIFY_NONE; + const char *err; + + if ((err = ssl_cmd_verify_parse(cmd, arg, &mode))) { + return err; + } + + dc->proxy->auth.verify_mode = mode; + + return NULL; +} + +const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + int depth; + const char *err; + + if ((err = ssl_cmd_verify_depth_parse(cmd, arg, &depth))) { + return err; + } + + dc->proxy->auth.verify_depth = depth; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCACertificateFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + dc->proxy->auth.ca_cert_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + dc->proxy->auth.ca_cert_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCARevocationPath(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + dc->proxy->crl_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCARevocationFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + dc->proxy->crl_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCARevocationCheck(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + return ssl_cmd_crlcheck_parse(cmd, arg, &dc->proxy->crl_check_mask); +} + +const char *ssl_cmd_SSLProxyMachineCertificateFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + dc->proxy->pkp->cert_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyMachineCertificatePath(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_dir(cmd, &arg))) { + return err; + } + + dc->proxy->pkp->cert_path = arg; + + return NULL; +} + +const char *ssl_cmd_SSLProxyMachineCertificateChainFile(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + dc->proxy->pkp->ca_cert_file = arg; + + return NULL; +} + +const char *ssl_cmd_SSLUserName(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + dc->szUserName = arg; + return NULL; +} + +static const char *ssl_cmd_ocspcheck_parse(cmd_parms *parms, + const char *arg, + int *mask) +{ + const char *w; + + w = ap_getword_conf(parms->temp_pool, &arg); + if (strcEQ(w, "off")) { + *mask = SSL_OCSPCHECK_NONE; + } + else if (strcEQ(w, "leaf")) { + *mask = SSL_OCSPCHECK_LEAF; + } + else if (strcEQ(w, "on")) { + *mask = SSL_OCSPCHECK_CHAIN; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", w, "'", + NULL); + } + + while (*arg) { + w = ap_getword_conf(parms->temp_pool, &arg); + if (strcEQ(w, "no_ocsp_for_cert_ok")) { + *mask |= SSL_OCSPCHECK_NO_OCSP_FOR_CERT_OK; + } + else { + return apr_pstrcat(parms->temp_pool, parms->cmd->name, + ": Invalid argument '", w, "'", + NULL); + } + } + + return NULL; +} + +const char *ssl_cmd_SSLOCSPEnable(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + +#ifdef OPENSSL_NO_OCSP + if (flag) { + return "OCSP support disabled in SSL library; cannot enable " + "OCSP validation"; + } +#endif + + return ssl_cmd_ocspcheck_parse(cmd, arg, &sc->server->ocsp_mask); +} + +const char *ssl_cmd_SSLOCSPOverrideResponder(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_force_default = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLOCSPDefaultResponder(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_responder = arg; + + return NULL; +} + +const char *ssl_cmd_SSLOCSPResponseTimeSkew(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->ocsp_resptime_skew = atoi(arg); + if (sc->server->ocsp_resptime_skew < 0) { + return "SSLOCSPResponseTimeSkew: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLOCSPResponseMaxAge(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->ocsp_resp_maxage = atoi(arg); + if (sc->server->ocsp_resp_maxage < 0) { + return "SSLOCSPResponseMaxAge: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLOCSPResponderTimeout(cmd_parms *cmd, void *dcfg, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->ocsp_responder_timeout = apr_time_from_sec(atoi(arg)); + if (sc->server->ocsp_responder_timeout < 0) { + return "SSLOCSPResponderTimeout: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLOCSPUseRequestNonce(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_use_request_nonce = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLOCSPProxyURL(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->proxy_uri = apr_palloc(cmd->pool, sizeof(apr_uri_t)); + if (apr_uri_parse(cmd->pool, arg, sc->server->proxy_uri) != APR_SUCCESS) { + return apr_psprintf(cmd->pool, + "SSLOCSPProxyURL: Cannot parse URL %s", arg); + } + return NULL; +} + +/* Set OCSP responder certificate verification directive */ +const char *ssl_cmd_SSLOCSPNoVerify(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->server->ocsp_noverify = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCheckPeerExpire(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->proxy->ssl_check_peer_expire = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCheckPeerCN(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->proxy->ssl_check_peer_cn = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLProxyCheckPeerName(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg; + + dc->proxy->ssl_check_peer_name = flag ? TRUE : FALSE; + + return NULL; +} + +const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag) +{ +#ifdef HAVE_TLSEXT + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + sc->strict_sni_vhost_check = flag ? SSL_ENABLED_TRUE : SSL_ENABLED_FALSE; + + return NULL; +#else + return "SSLStrictSNIVHostCheck failed; OpenSSL is not built with support " + "for TLS extensions and SNI indication. Refer to the " + "documentation, and build a compatible version of OpenSSL."; +#endif +} + +#ifdef HAVE_OCSP_STAPLING + +const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd, + void *dcfg, + const char *arg) +{ + SSLModConfigRec *mc = myModConfig(cmd->server); + const char *err, *sep, *name; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + /* Argument is of form 'name:args' or just 'name'. */ + sep = ap_strchr_c(arg, ':'); + if (sep) { + name = apr_pstrmemdup(cmd->pool, arg, sep - arg); + sep++; + } + else { + name = arg; + } + + /* Find the provider of given name. */ + mc->stapling_cache = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, + name, + AP_SOCACHE_PROVIDER_VERSION); + if (mc->stapling_cache) { + /* Cache found; create it, passing anything beyond the colon. */ + err = mc->stapling_cache->create(&mc->stapling_cache_context, + sep, cmd->temp_pool, + cmd->pool); + } + else { + apr_array_header_t *name_list; + const char *all_names; + + /* Build a comma-separated list of all registered provider + * names: */ + name_list = ap_list_provider_names(cmd->pool, + AP_SOCACHE_PROVIDER_GROUP, + AP_SOCACHE_PROVIDER_VERSION); + all_names = apr_array_pstrcat(cmd->pool, name_list, ','); + + err = apr_psprintf(cmd->pool, "'%s' stapling cache not supported " + "(known names: %s) Maybe you need to load the " + "appropriate socache module (mod_socache_%s?)", + name, all_names, name); + } + + if (err) { + return apr_psprintf(cmd->pool, "SSLStaplingCache: %s", err); + } + + return NULL; +} + +const char *ssl_cmd_SSLUseStapling(cmd_parms *cmd, void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_enabled = flag ? TRUE : FALSE; + return NULL; +} + +const char *ssl_cmd_SSLStaplingResponseTimeSkew(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_resptime_skew = atoi(arg); + if (sc->server->stapling_resptime_skew < 0) { + return "SSLStaplingResponseTimeSkew: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLStaplingResponseMaxAge(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_resp_maxage = atoi(arg); + if (sc->server->stapling_resp_maxage < 0) { + return "SSLStaplingResponseMaxAge: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLStaplingStandardCacheTimeout(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_cache_timeout = atoi(arg); + if (sc->server->stapling_cache_timeout < 0) { + return "SSLStaplingStandardCacheTimeout: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLStaplingErrorCacheTimeout(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_errcache_timeout = atoi(arg); + if (sc->server->stapling_errcache_timeout < 0) { + return "SSLStaplingErrorCacheTimeout: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLStaplingReturnResponderErrors(cmd_parms *cmd, + void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_return_errors = flag ? TRUE : FALSE; + return NULL; +} + +const char *ssl_cmd_SSLStaplingFakeTryLater(cmd_parms *cmd, + void *dcfg, int flag) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_fake_trylater = flag ? TRUE : FALSE; + return NULL; +} + +const char *ssl_cmd_SSLStaplingResponderTimeout(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_responder_timeout = atoi(arg); + sc->server->stapling_responder_timeout *= APR_USEC_PER_SEC; + if (sc->server->stapling_responder_timeout < 0) { + return "SSLStaplingResponderTimeout: invalid argument"; + } + return NULL; +} + +const char *ssl_cmd_SSLStaplingForceURL(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + sc->server->stapling_force_url = arg; + return NULL; +} + +#endif /* HAVE_OCSP_STAPLING */ + +#ifdef HAVE_SSL_CONF_CMD +const char *ssl_cmd_SSLOpenSSLConfCmd(cmd_parms *cmd, void *dcfg, + const char *arg1, const char *arg2) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config; + int value_type = SSL_CONF_cmd_value_type(cctx, arg1); + const char *err; + ssl_ctx_param_t *param; + + if (value_type == SSL_CONF_TYPE_UNKNOWN) { + return apr_psprintf(cmd->pool, + "'%s': invalid OpenSSL configuration command", + arg1); + } + + if (value_type == SSL_CONF_TYPE_FILE) { + if ((err = ssl_cmd_check_file(cmd, &arg2))) + return err; + } + else if (value_type == SSL_CONF_TYPE_DIR) { + if ((err = ssl_cmd_check_dir(cmd, &arg2))) + return err; + } + + if (strcEQ(arg1, "CipherString")) { + /* always disable null and export ciphers */ + arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL); + } + + param = apr_array_push(sc->server->ssl_ctx_param); + param->name = arg1; + param->value = arg2; + return NULL; +} +#endif + +#ifdef HAVE_SRP + +const char *ssl_cmd_SSLSRPVerifierFile(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) + return err; + /* SRP_VBASE_init takes char*, not const char* */ + sc->server->srp_vfile = apr_pstrdup(cmd->pool, arg); + return NULL; +} + +const char *ssl_cmd_SSLSRPUnknownUserSeed(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + /* SRP_VBASE_new takes char*, not const char* */ + sc->server->srp_unknown_user_seed = apr_pstrdup(cmd->pool, arg); + return NULL; +} + +#endif /* HAVE_SRP */ + +/* OCSP Responder File Function to read in value */ +const char *ssl_cmd_SSLOCSPResponderCertificateFile(cmd_parms *cmd, void *dcfg, + const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + const char *err; + + if ((err = ssl_cmd_check_file(cmd, &arg))) { + return err; + } + + sc->server->ocsp_certs_file = arg; + return NULL; +} + +void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s) +{ + apr_file_t *out = NULL; + if (!ap_exists_config_define("DUMP_CERTS")) { + return; + } + apr_file_open_stdout(&out, pconf); + apr_file_printf(out, "Server certificates:\n"); + + /* Dump the filenames of all configured server certificates to + * stdout. */ + while (s) { + SSLSrvConfigRec *sc = mySrvConfig(s); + + if (sc && sc->server && sc->server->pks) { + modssl_pk_server_t *const pks = sc->server->pks; + int i; + + for (i = 0; (i < pks->cert_files->nelts) && + APR_ARRAY_IDX(pks->cert_files, i, const char *); + i++) { + apr_file_printf(out, " %s\n", + APR_ARRAY_IDX(pks->cert_files, + i, const char *)); + } + } + + s = s->next; + } + +} + |