From: Stefan Eissing Date: Wed, 16 Aug 2017 12:22:28 +0000 (+0000) Subject: On the trunk: X-Git-Tag: 2.5.0-alpha~221 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e4431e60a42a8e634982ba84c1479be2d6e2d999;p=thirdparty%2Fapache%2Fhttpd.git On the trunk: mod_ssl: adding SSLPolicy and SSLProxyPolicy directives plus documentation. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1805182 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 826575b7f0d..e0ff6c45419 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,10 @@ -*- coding: utf-8 -*- Changes with Apache 2.5.0 + *) mod_ssl: add SSLPolicy (define/use) and SSLProxyPolicy directives plus documentation. Add + core definitions for policies 'modern', 'intermediate' and 'old', as defined by Mozilla + in . [Stefan Eissing] + *) mod_proxy: Fix ProxyAddHeaders merging. [Joe Orton] *) mod_md: new module for managing domains across VirtualHosts with ACME protocol diff --git a/docs/conf/extra/httpd-ssl.conf.in b/docs/conf/extra/httpd-ssl.conf.in index ac1776d7c75..541735cfe06 100644 --- a/docs/conf/extra/httpd-ssl.conf.in +++ b/docs/conf/extra/httpd-ssl.conf.in @@ -42,6 +42,34 @@ Listen @@SSLPort@@ ## the main server and all SSL-enabled virtual hosts. ## +# SSL Policy: +# Choose from a pre-defined setting of SSL* configurations, based on +# the Mozilla recommendations from: +# https://wiki.mozilla.org/Security/Server_Side_TLS +# These policies will be updated over time in new releases to keep +# settings compatible and secure with "modern" browser, or if you +# need to support legacy installtions, "intermediate" might be your +# choice. +# If you run the following command on your installation, the exact +# contents of the defined SSL policies will be listed: +# +# > httpd -t -D DUMP_SSL_POLICIES +# +# Worth noting, a SSLPolicy defines the ground rules. What ever is +# configured beside it, is added "on top". If a configuration has +# a SSLPolicy *and* a SSLCipherSuite, any cipher suite in the policy +# will be overwritten in this context. +# +# If your OpenSSL library does not support TLSv1.2 (OpenSSL 1.0.2 +# and later), "modern" will not be available. +# If you enable one of the policies below, we recommend that you +# refrain from configuring any of the following, unless you are +# certain that you want to override: +# SSLCipherSuite, SSLProxyCipherSuite, SSLHonorCipherOrder, +# SSLProtocol, SSLProxyProtocol +#SSLPolicy intermediate +#SSLPolicy modern + # SSL Cipher Suite: # List the ciphers that the client is permitted to negotiate, # and that httpd will negotiate as the client of a proxied server. diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml index af29963c463..1000597132e 100644 --- a/docs/manual/mod/mod_ssl.xml +++ b/docs/manual/mod/mod_ssl.xml @@ -2813,4 +2813,193 @@ SSLOpenSSLConfCmd SignatureAlgorithms RSA+SHA384:ECDSA+SHA256 + +SSLPolicySection + +<SSLPolicy name> +server config +Available in httpd 2.4.28 and later + + +

This directive defines a set of SSL* configurations under +and gives it a name. This name can be used in the directives +SSLPolicy and SSLProxyPolicy +to apply this configuration set in the current context.

+ +Examples + +<SSLPolicy safe-stapling> + SSLUseStapling on + SSLStaplingResponderTimeout 2 + SSLStaplingReturnResponderErrors off + SSLStaplingFakeTryLater off + SSLStaplingStandardCacheTimeout 86400 +</SSLPolicy> + + ... + <VirtualHost...> + SSLPolicy safe-stapling + ... + + + +

On the one hand, this can make server configurations easier to +read and maintain. On the other hand, it is +intended to make SSL easier and safer to use. For the +latter, Apache httpd ships with a set of pre-defined policies +that reflect good open source practise. The policy "modern", +for example, carries the settings to make your server work +compatible and securely with current browsers.

+ +

The list of predefined policies in your Apache can be obtained +by running the following command. This list shows you the +detailed configurations each policy is made of:

+ +Examples + +> httpd -t -D DUMP_SSL_POLICIES + + + +

The directive can only be used in the server config (global context), so +there cannot be two policies with the same name. However, policies can +be redefined:

+ +Examples + +<SSLPolicy proxy-trust> + SSLProxyVerify require +</SSLPolicy> + ... +<SSLPolicy proxy-trust> + SSLProxyVerify none +</SSLPolicy> + + + +

Policy definitions are added in the order they appear, but are +applied when the whole configuration is read. This means that any +use of 'proxy-trust' will mean 'SSLProxyVerify none'. The first definition +has no effect at all. You can replace policy definitions that have been +pre-installed without the need to disable them.

+ +

Additional to replacing policies, redefinitions may just alter +an aspect of a policy:

+ +Examples + +<SSLPolicy proxy-trust> + SSLProxyVerify require +</SSLPolicy> + ... +<SSLPolicy proxy-trust> + SSLPolicy proxy-trust + SSLProxyVerifyDepth 10 +</SSLPolicy> + + + +

This re-uses all settings from the previous 'proxy-trust' and adds +one directive on top of it. All others still apply. This is very handy +when pre-defined policies (from Apache itself or a distributor) +that almost fit ones needs. Previously, such definitions were +(copied and) edited. This made updating them difficult. Now they can +be setup like this:

+ +Examples + +Include ssl-policies.conf + +<SSLPolicy modern> + SSLPolicy modern + SSLProxyVerify none +</SSLPolicy> + + + +
+
+ + +SSLPolicy + +SSLPolicy name +server config +virtual host +Available in httpd 2.4.28 and later + + +

This directive applies the set of SSL* directives defined +under 'name' (see SSLPolicySection) as the base +settings in the current context. That means that any other SSL* directives +you make in the same context remain effective. So, the effective +SSLProtocol value in the following settings are:

+ +Examples + + <VirtualHost...> # effective: 'all' + SSLPolicy modern + SSLProtocol all + </VirtualHost> + + <VirtualHost...> # effective: 'all' + SSLProtocol all + SSLPolicy modern + </VirtualHost> + + SSLPolicy modern + <VirtualHost...> # effective: 'all' + SSLProtocol all + </VirtualHost> + + SSLProtocol all + <VirtualHost...> # effective: '+TLSv1.2' + SSLPolicy modern + </VirtualHost> + + + +

There can be more than one policy applied in a context. The +later ones overshadowing the earlier ones:

+ +Examples + + <VirtualHost...> # effective: 'intermediate > modern' + SSLPolicy modern + SSLPolicy intermediate + </VirtualHost> + + <VirtualHost...> # effective: 'modern > intermediate' + SSLPolicy intermediate + SSLPolicy modern + </VirtualHost> + + + +
+
+ + +SSLProxyPolicy + +SSLProxyPolicy name +server config +virtual host +Available in httpd 2.4.28 and later + + +

This directive is similar to SSLPolicy, but +applies only the SSLProxy* directives defined in the policy. This helps +when you need different policies for front and backends:

+ +Examples + +SSLPolicy modern +SSLProxyPolicy intermediate + + + +
+
+ diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c index 6df5d43055d..d6c87edd8da 100644 --- a/modules/ssl/mod_ssl.c +++ b/modules/ssl/mod_ssl.c @@ -312,6 +312,15 @@ static const command_rec ssl_config_cmds[] = { AP_INIT_RAW_ARGS("SSLLogLevel", ap_set_deprecated, NULL, OR_ALL, "SSLLogLevel directive is no longer supported - use LogLevel."), + AP_INIT_TAKE1(" +#include + #include "ssl_private.h" #include "util_mutex.h" #include "ap_provider.h" @@ -226,6 +229,8 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) sc->compression = UNSET; #endif sc->session_tickets = UNSET; + sc->policies = NULL; + sc->error_policy = NULL; modssl_ctx_init_server(sc, p); @@ -339,12 +344,19 @@ static void modssl_ctx_cfg_merge_server(apr_pool_t *p, #endif } +static void ssl_policy_apply(SSLSrvConfigRec *sc, apr_pool_t *p); +static void ssl_dir_policy_apply(SSLDirConfigRec *dc, apr_pool_t *p); + 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); + /* This is a NOP, unless a policy has not been applied yet */ + ssl_policy_apply(base, p); + ssl_policy_apply(add, p); + cfgMerge(mc, NULL); cfgMerge(enabled, SSL_ENABLED_UNSET); cfgMergeInt(session_cache_timeout); @@ -361,6 +373,9 @@ void *ssl_config_server_merge(apr_pool_t *p, void *basev, void *addv) #endif cfgMergeBool(session_tickets); + mrg->policies = NULL; + cfgMergeString(error_policy); + modssl_ctx_cfg_merge_server(p, base->server, add->server, mrg->server); return mrg; @@ -410,6 +425,9 @@ void *ssl_config_perdir_create(apr_pool_t *p, char *dir) modssl_ctx_init_proxy(dc, p); dc->proxy_post_config = FALSE; + dc->policies = NULL; + dc->error_policy = NULL; + return dc; } @@ -435,6 +453,9 @@ void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv) SSLDirConfigRec *add = (SSLDirConfigRec *)addv; SSLDirConfigRec *mrg = (SSLDirConfigRec *)apr_palloc(p, sizeof(*mrg)); + ssl_dir_policy_apply(base, p); + ssl_dir_policy_apply(add, p); + cfgMerge(bSSLRequired, FALSE); cfgMergeArray(aRequirement); @@ -474,6 +495,9 @@ void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv) mrg->proxy = add->proxy; } + mrg->policies = NULL; + cfgMergeString(error_policy); + return mrg; } @@ -488,6 +512,314 @@ void ssl_config_proxy_merge(apr_pool_t *p, modssl_ctx_cfg_merge_proxy(p, base->proxy, conf->proxy, conf->proxy); } +/* _________________________________________________________________ +** +** Policy handling +** _________________________________________________________________ +*/ + +#define SSL_MOD_POLICIES_KEY "ssl_module_policies" + +/** + * Define a core set of policies that are always there: + * - 'modern' from https://wiki.mozilla.org/Security/Server_Side_TLS + * - 'intermediate' from https://wiki.mozilla.org/Security/Server_Side_TLS + * - 'old' from https://wiki.mozilla.org/Security/Server_Side_TLS + */ +#ifdef HAVE_TLSV1_X + /* Only with OpenSSL > v1.0.2 do we have a chance to implement modern */ +#define SSL_POLICY_LEGACY_PROTOCOLS \ + (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1|SSL_PROTOCOL_TLSV1_1) + +#define SSL_POLICY_MODERN_PROTOCOLS \ + (SSL_PROTOCOL_ALL & ~SSL_POLICY_LEGACY_PROTOCOLS) +#define SSL_POLICY_MODERN_CIPHERS \ + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" \ + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" \ + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:" \ + "ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" +#endif + +#define SSL_POLICY_INTERMEDIATE_PROTOCOLS \ + (SSL_PROTOCOL_ALL & ~SSL_PROTOCOL_SSLV3) +#define SSL_POLICY_INTERMEDIATE_CIPHERS \ + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" \ + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:" \ + "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:" \ + "ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:" \ + "ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:" \ + "ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:" \ + "DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:" \ + "ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:" \ + "AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:" \ + "AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS" + +#define SSL_POLICY_OLD_PROTOCOLS (SSL_PROTOCOL_ALL) +#define SSL_POLICY_OLD_CIPHERS \ + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:" \ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:" \ + "DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" \ + "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:" \ + "ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:" \ + "ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:" \ + "DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:" \ + "DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:" \ + "ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:" \ + "AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:" \ + "DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:" \ + "!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP" + +#define SSL_POLICY_PROXY_VERIFY_MODE SSL_CVERIFY_REQUIRE +#define SSL_POLICY_PROXY_VERIFY_DEPTH -1 + +static void add_policy(apr_hash_t *policies, apr_pool_t *p, const char *name, + int protocols, const char *ciphers, + int honor_order, int compression, int session_tickets, + ssl_verify_t proxy_verify_mode, int proxy_verify_depth) +{ + SSLPolicyRec *policy; + + policy = apr_pcalloc(p, sizeof(*policy)); + policy->name = name; + policy->sc = ssl_config_server_new(p); + policy->dc = ssl_config_perdir_create(p, "/"); + + if (protocols || ciphers) { + policy->sc->server->protocol_set = 1; + policy->sc->server->protocol = protocols; + policy->dc->proxy->protocol_set = 1; + policy->dc->proxy->protocol = protocols; + } + + if (ciphers) { + policy->sc->server->auth.cipher_suite = ciphers; + policy->dc->proxy->auth.cipher_suite = ciphers; + } + + policy->sc->compression = compression ? TRUE : FALSE; + policy->sc->session_tickets = session_tickets ? TRUE : FALSE; + + policy->dc->proxy->auth.verify_mode = proxy_verify_mode; + if (proxy_verify_depth >= 0) { + policy->dc->proxy->auth.verify_depth = proxy_verify_depth; + } + + apr_hash_set(policies, policy->name, APR_HASH_KEY_STRING, policy); +} + +static apr_hash_t *get_policies(apr_pool_t *p, int create) +{ + apr_hash_t *policies; + void *vp; + + apr_pool_userdata_get(&vp, SSL_MOD_POLICIES_KEY, p); + if (vp) { + return vp; /* reused for lifetime of the pool */ + } + if (create) { + policies = apr_hash_make(p); + + /* We could also invoke the commands here and let them parse strings. */ +#ifdef HAVE_TLSV1_X + add_policy(policies, p, "modern", + SSL_POLICY_MODERN_PROTOCOLS, SSL_POLICY_MODERN_CIPHERS, + 1, 0, 0, + SSL_POLICY_PROXY_VERIFY_MODE, SSL_POLICY_PROXY_VERIFY_DEPTH); +#endif + add_policy(policies, p, "intermediate", + SSL_POLICY_INTERMEDIATE_PROTOCOLS, SSL_POLICY_INTERMEDIATE_CIPHERS, + 1, 0, 0, + SSL_POLICY_PROXY_VERIFY_MODE, SSL_POLICY_PROXY_VERIFY_DEPTH); + + add_policy(policies, p, "old", + SSL_POLICY_OLD_PROTOCOLS, SSL_POLICY_OLD_CIPHERS, + 1, 0, 0, + SSL_CVERIFY_NONE, -1); + + apr_pool_userdata_set(policies, SSL_MOD_POLICIES_KEY, + apr_pool_cleanup_null, p); + return policies; + } + return NULL; +} + +static int policy_collect_names(void *baton, const void *key, apr_ssize_t klen, const void *val) +{ + apr_array_header_t *names = baton; + APR_ARRAY_PUSH(names, const char *) = (const char*)key; + return 1; +} + +static int qstrcmp(const void *v1, const void *v2) +{ + return strcmp(*(const char**)v1, *(const char**)v2); +} + +static apr_array_header_t *get_policy_names(apr_pool_t *p, int create) +{ + apr_array_header_t *names = apr_array_make(p, 10, sizeof(const char*)); + apr_hash_t *policies = get_policies(p, create); + + if (policies) { + apr_hash_do(policy_collect_names, names, policies); + qsort(names->elts, names->nelts, sizeof(const char*), qstrcmp); + } + return names; +} + +SSLPolicyRec *ssl_policy_lookup(apr_pool_t *pool, const char *name) +{ + apr_hash_t *policies = get_policies(pool, 0); + if (policies) { + return apr_hash_get(policies, name, APR_HASH_KEY_STRING); + } + else if ((pool = apr_pool_parent_get(pool))) { + return ssl_policy_lookup(pool, name); + } + return NULL; +} + +static void ssl_policy_set(apr_pool_t *pool, SSLPolicyRec *policy) +{ + apr_hash_t *policies = get_policies(pool, 1); + return apr_hash_set(policies, policy->name, APR_HASH_KEY_STRING, policy); +} + +const char *ssl_cmd_SSLPolicyDefine(cmd_parms *cmd, void *mconfig, const char *arg) +{ + server_rec *s = cmd->server; + SSLSrvConfigRec *sc = mySrvConfig(s); + SSLDirConfigRec *dc = ap_get_module_config(s->lookup_defaults, &ssl_module); + SSLPolicyRec *policy; + const char *endp = ap_strrchr_c(arg, '>'); + const char *err, *name; + + if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { + return err; + } + + if (endp == NULL) { + return apr_pstrcat(cmd->pool, cmd->cmd->name, "> directive missing closing '>'", NULL); + } + + arg = apr_pstrndup(cmd->pool, arg, endp-arg); + if (!arg || !*arg) { + return " block must specify a name"; + } + + name = ap_getword_white(cmd->pool, &arg); + if (*arg != '\0') { + return apr_pstrcat(cmd->pool, cmd->cmd->name, "> takes only 1 argument", NULL); + } + + policy = apr_pcalloc(cmd->pool, sizeof(*policy)); + policy->name = name; + policy->sc = ssl_config_server_new(cmd->pool); + policy->dc = ssl_config_perdir_create(cmd->pool, "/");/* TODO */ + + ap_set_module_config(s->module_config, &ssl_module, policy->sc); + ap_set_module_config(s->lookup_defaults, &ssl_module, policy->dc); + + err = ap_walk_config(cmd->directive->first_child, cmd, cmd->context); + if (!err) { + /* If this new policy uses other policies, we need to merge it + * before adding, otherwise a policy cannot re-use an existing one */ + ssl_policy_apply(policy->sc, cmd->pool); + ssl_dir_policy_apply(policy->dc, cmd->pool); + /* time to persist */ + ssl_policy_set(cmd->pool, policy); + } + + ap_set_module_config(s->module_config, &ssl_module, sc); + ap_set_module_config(s->lookup_defaults, &ssl_module, dc); + + return err; +} + +const char *ssl_cmd_SSLPolicyApply(cmd_parms *cmd, void *mconfig, const char *arg) +{ + SSLSrvConfigRec *sc = mySrvConfig(cmd->server); + + if (!sc->policies) { + sc->policies = apr_array_make(cmd->pool, 5, sizeof(const char*)); + } + APR_ARRAY_PUSH(sc->policies, const char *) = arg; + + /* Also apply the proxy parts of the policy */ + return ssl_cmd_SSLProxyPolicyApply(cmd, mconfig, arg); +} + +const char *ssl_cmd_SSLProxyPolicyApply(cmd_parms *cmd, void *mconfig, const char *arg) +{ + SSLDirConfigRec *dc = ap_get_module_config(cmd->server->lookup_defaults, &ssl_module); + + if (!dc->policies) { + dc->policies = apr_array_make(cmd->pool, 5, sizeof(const char*)); + } + APR_ARRAY_PUSH(dc->policies, const char *) = arg; + + return NULL; +} + +void ssl_policy_apply(SSLSrvConfigRec *sc, apr_pool_t *p) +{ + SSLSrvConfigRec *mrg; + SSLPolicyRec *policy; + apr_array_header_t *policies; + const char *name; + int i; + + policies = sc->policies; + if (policies && policies->nelts > 0 && !sc->error_policy) { + sc->policies = NULL; + for (i = policies->nelts - 1; i >= 0; --i) { + name = APR_ARRAY_IDX(policies, i, const char *); + policy = ssl_policy_lookup(p, name); + if (policy) { + mrg = ssl_config_server_merge(p, policy->sc, sc); + /* apply in place */ + memcpy(sc, mrg, sizeof(*sc)); + } + else { + sc->error_policy = name; + break; + /* report error policies in post_config */ + } + } + } +} + +void ssl_dir_policy_apply(SSLDirConfigRec *dc, apr_pool_t *p) +{ + SSLDirConfigRec *mrg; + SSLPolicyRec *policy; + apr_array_header_t *policies; + const char *name; + int i; + + policies = dc->policies; + if (policies && policies->nelts > 0 &&!dc->error_policy) { + dc->policies = NULL; + for (i = policies->nelts - 1; i >= 0; --i) { + name = APR_ARRAY_IDX(policies, i, const char *); + policy = ssl_policy_lookup(p, name); + if (policy) { + mrg = ssl_config_perdir_merge(p, policy->dc, dc); + /* apply in place */ + memcpy(dc, mrg, sizeof(*dc)); + } + else { + dc->error_policy = name; + break; + /* report error policies in post_config */ + } + } + } +} + /* * Configuration functions for particular directives */ @@ -588,6 +920,7 @@ const char *ssl_cmd_SSLRandomSeed(cmd_parms *cmd, ssl_randseed_t *seed; int arg2len = strlen(arg2); + /* replace: check_no_policy_and(flags) */ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { return err; } @@ -1996,6 +2329,13 @@ const char *ssl_cmd_SSLOCSPResponderCertificateFile(cmd_parms *cmd, void *dcfg, return NULL; } +static void ssl_srv_dump(SSLSrvConfigRec *sc, apr_pool_t *p, + apr_file_t *out, const char *indent, const char **psep); +static void ssl_dir_dump(SSLDirConfigRec *dc, apr_pool_t *p, + apr_file_t *out, const char *indent, const char **psep); +static void ssl_policy_dump(SSLPolicyRec *policy, apr_pool_t *p, + apr_file_t *out, const char *indent); + void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s) { apr_file_t *out = NULL; @@ -2056,4 +2396,412 @@ void ssl_hook_ConfigTest(apr_pool_t *pconf, server_rec *s) return; } + if (ap_exists_config_define("DUMP_SSL_POLICIES")) { + apr_array_header_t *names = get_policy_names(pconf, 1); + SSLPolicyRec *policy; + const char *name, *sep = ""; + int i; + + apr_file_open_stdout(&out, pconf); + apr_file_printf(out, "SSLPolicies: {"); + for (i = 0; i < names->nelts; ++i) { + name = APR_ARRAY_IDX(names, i, const char*); + policy = ssl_policy_lookup(pconf, name); + if (policy) { + apr_file_printf(out, "%s\n \"%s\": {", sep, name); + sep = ", "; + ssl_policy_dump(policy, pconf, out, " "); + apr_file_printf(out, "\n }"); + } + } + apr_file_printf(out, "\n}\n"); + return; + } +} + +/* _________________________________________________________________ +** +** Dump Config Data +** _________________________________________________________________ +*/ + +static const char *json_quote(const char *s, apr_pool_t *p) +{ + const char *src, *dq = s; + int n = 0; + + while ((dq = ap_strchr_c(dq, '\"'))) { + ++n; + ++dq; + } + if (n > 0) { + char *dst, c; + src = s; + s = dst = apr_pcalloc(p, strlen(s) + n + 1); + while ((c = *src++)) { + if (c == '\"') { + *dst++ = '\\'; + } + *dst++ = c; + } + } + return s; +} + +static void val_str_dump(apr_file_t *out, const char *key, const char *val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val) { + /* TODO: JSON quite string val */ + apr_file_printf(out, "%s\n%s\"%s\": \"%s\"", *psep, indent, key, json_quote(val, p)); + *psep = ", "; + } +} + +static void val_str_array_dump(apr_file_t *out, const char *key, apr_array_header_t *val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val && val->nelts > 0) { + const char *s; + int i; + + for (i = 0; i < val->nelts; ++i) { + s = APR_ARRAY_IDX(val, i, const char*); + val_str_dump(out, key, s, p, indent, psep); + } + } +} + +static void val_long_dump(apr_file_t *out, const char *key, long val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val != UNSET) { + apr_file_printf(out, "%s\n%s\"%s\": %ld", *psep, indent, key, val); + *psep = ", "; + } +} + +static void val_itime_dump(apr_file_t *out, const char *key, apr_interval_time_t val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val != UNSET) { + apr_file_printf(out, "%s\n%s\"%s\": %f", *psep, indent, key, + ((double)val/APR_USEC_PER_SEC)); + *psep = ", "; + } +} + +static void val_onoff_dump(apr_file_t *out, const char *key, BOOL val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val != UNSET) { + val_str_dump(out, key, val? "on" : "off", p, indent, psep); + } +} + +static void val_uri_dump(apr_file_t *out, const char *key, apr_uri_t *val, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (val) { + val_str_dump(out, key, apr_uri_unparse(p, val, 0), p, indent, psep); + } +} + +static void val_verify_dump(apr_file_t *out, const char *key, ssl_verify_t mode, + apr_pool_t *p, const char *indent, const char **psep) +{ + switch (mode) { + case SSL_CVERIFY_NONE: + val_str_dump(out, key, "none", p, indent, psep); + return; + case SSL_CVERIFY_OPTIONAL: + val_str_dump(out, key, "optional", p, indent, psep); + return; + case SSL_CVERIFY_REQUIRE: + val_str_dump(out, key, "require", p, indent, psep); + return; + case SSL_CVERIFY_OPTIONAL_NO_CA: + val_str_dump(out, key, "optional_no_ca", p, indent, psep); + return; + default: + return; + } +} + +static void val_enabled_dump(apr_file_t *out, const char *key, ssl_enabled_t val, + apr_pool_t *p, const char *indent, const char **psep) +{ + switch (val) { + case SSL_ENABLED_FALSE: + val_str_dump(out, key, "off", p, indent, psep); + return; + case SSL_ENABLED_TRUE: + val_str_dump(out, key, "on", p, indent, psep); + return; + case SSL_ENABLED_OPTIONAL: + val_str_dump(out, key, "optional", p, indent, psep); + return; + default: + return; + } +} + +static void val_pphrase_dump(apr_file_t *out, const char *key, + ssl_pphrase_t pphrase_type, const char *path, + apr_pool_t *p, const char *indent, const char **psep) +{ + switch (pphrase_type) { + case SSL_PPTYPE_BUILTIN: + val_str_dump(out, key, "builtin", p, indent, psep); + return; + case SSL_PPTYPE_FILTER: + val_str_dump(out, key, apr_pstrcat(p, "|", path, NULL), p, indent, psep); + return; + case SSL_PPTYPE_PIPE: + val_str_dump(out, key, apr_pstrcat(p, "exec:", path, NULL), p, indent, psep); + return; + default: + return; + } +} + +static void val_crl_check_dump(apr_file_t *out, const char *key, int mask, + apr_pool_t *p, const char *indent, const char **psep) +{ + if (mask != UNSET) { + if (mask == SSL_CRLCHECK_NONE) { + val_str_dump(out, key, "none", p, indent, psep); + } + else if (mask == SSL_CRLCHECK_LEAF) { + val_str_dump(out, key, "leaf", p, indent, psep); + } + else if (mask == SSL_CRLCHECK_CHAIN) { + val_str_dump(out, key, "chain", p, indent, psep); + } + else if (mask == (SSL_CRLCHECK_CHAIN|SSL_CRLCHECK_NO_CRL_FOR_CERT_OK)) { + val_str_dump(out, key, "chain no_crl_for_cert_ok", p, indent, psep); + } + else { + val_str_dump(out, key, "???", p, indent, psep); + } + } +} + +static void val_option_dump(apr_file_t *out, const char *key, const char *optname, + int val, int set_mask, int add_mask, int del_mask, + apr_pool_t *p, const char *indent, const char **psep) +{ + const char *op = ((val & set_mask)? "" : + ((val & add_mask)? "+" : + (val & del_mask)? "-" : NULL)); + if (op) { + apr_file_printf(out, "%s\n%s\"%s\": \"%s%s\"", *psep, indent, key, op, + json_quote(optname, p)); + *psep = ", "; + } +} + +static const char *protocol_str(ssl_proto_t proto, apr_pool_t *p) +{ + if (SSL_PROTOCOL_NONE == proto) { + return "none"; + } + else if (SSL_PROTOCOL_ALL == proto) { + return "all"; + } + else { + /* icing: I think it is nuts that we define our own IETF protocol constants + * only whent the linked *SSL lib supports them. */ + apr_array_header_t *names = apr_array_make(p, 5, sizeof(const char*)); + if ((1<<4) & proto) { + APR_ARRAY_PUSH(names, const char*) = "+TLSv1.2"; + } + if ((1<<3) & proto) { + APR_ARRAY_PUSH(names, const char*) = "+TLSv1.1"; + } + if ((1<<2) & proto) { + APR_ARRAY_PUSH(names, const char*) = "+TLSv1.0"; + } + if ((1<<1) & proto) { + APR_ARRAY_PUSH(names, const char*) = "+SSLv3"; + } + return apr_array_pstrcat(p, names, ' '); + } +} + +#define DMP_STRING(k,v) \ + val_str_dump(out, k, v, p, indent, psep) +#define DMP_LONG(k,v) \ + val_long_dump(out, k, v, p, indent, psep) +#define DMP_ITIME(k,v) \ + val_itime_dump(out, k, v, p, indent, psep) +#define DMP_STRARR(k,v) \ + val_str_array_dump(out, k, v, p, indent, psep) +#define DMP_VERIFY(k,v) \ + val_verify_dump(out, k, v, p, indent, psep) +#define DMP_ON_OFF(k,v) \ + val_onoff_dump(out, k, v, p, indent, psep) +#define DMP_URI(k,v) \ + val_uri_dump(out, k, v, p, indent, psep) +#define DMP_CRLCHK(k,v) \ + val_crl_check_dump(out, k, v, p, indent, psep) +#define DMP_PHRASE(k,v, v2) \ + val_pphrase_dump(out, k, v, v2, p, indent, psep) +#define DMP_ENABLD(k,v) \ + val_enabled_dump(out, k, v, p, indent, psep) +#define DMP_OPTION(n,v) \ + val_option_dump(out, "SSLOption", n, v, \ + dc->nOptions, dc->nOptionsAdd, dc->nOptionsDel, p, indent, psep); + +static void modssl_auth_ctx_dump(modssl_auth_ctx_t *auth, apr_pool_t *p, int proxy, + apr_file_t *out, const char *indent, const char **psep) +{ + DMP_STRING(proxy? "SSLProxyCipherSuite" : "SSLCipherSuite", auth->cipher_suite); + DMP_VERIFY(proxy? "SSLProxyVerify" : "SSLVerifyClient", auth->verify_mode); + DMP_LONG( proxy? "SSLProxyVerify" : "SSLVerifyDepth", auth->verify_depth); + DMP_STRING(proxy? "SSLProxyCACertificateFile" : "SSLCACertificateFile", auth->ca_cert_file); + DMP_STRING(proxy? "SSLProxyCACertificatePath" : "SSLCACertificatePath", auth->ca_cert_path); +} + +static void modssl_ctx_dump(modssl_ctx_t *ctx, apr_pool_t *p, int proxy, + apr_file_t *out, const char *indent, const char **psep) +{ + int i; + + if (ctx->protocol_set) { + DMP_STRING(proxy? "SSLProxyProtocol" : "SSLProtocol", protocol_str(ctx->protocol, p)); + } + + modssl_auth_ctx_dump(&ctx->auth, p, proxy, out, indent, psep); + + DMP_STRING(proxy? "SSLProxyCARevocationFile" : "SSLCARevocationFile", ctx->crl_file); + DMP_STRING(proxy? "SSLProxyCARevocationPath" : "SSLCARevocationPath", ctx->crl_path); + DMP_CRLCHK(proxy? "SSLProxyCARevocationCheck" : "SSLCARevocationCheck", ctx->crl_check_mask); + if (!proxy) { + DMP_PHRASE("SSLPassPhraseDialog", ctx->pphrase_dialog_type, ctx->pphrase_dialog_path); + if (ctx->pks) { + DMP_STRING("SSLCADNRequestFile", ctx->pks->ca_name_file); + DMP_STRING("SSLCADNRequestPath", ctx->pks->ca_name_path); + DMP_STRARR("SSLCertificateFile", ctx->pks->cert_files); + DMP_STRARR("SSLCertificateKeyFile", ctx->pks->key_files); + } +#ifdef HAVE_OCSP_STAPLING + DMP_ON_OFF("SSLUseStapling", ctx->stapling_enabled); + DMP_LONG( "SSLStaplingResponseTimeSkew", ctx->stapling_resptime_skew); + DMP_LONG( "SSLStaplingResponseMaxAge", ctx->stapling_resp_maxage); + DMP_LONG( "SSLStaplingStandardCacheTimeout", ctx->stapling_cache_timeout); + DMP_ON_OFF("SSLStaplingReturnResponderErrors", ctx->stapling_return_errors); + DMP_ON_OFF("SSLStaplingFakeTryLater", ctx->stapling_fake_trylater); + DMP_LONG( "SSLStaplingErrorCacheTimeout", ctx->stapling_errcache_timeout); + DMP_ITIME( "SSLStaplingResponderTimeout", ctx->stapling_responder_timeout); + DMP_STRING("SSLStaplingForceURL", ctx->stapling_force_url); +#endif /* if HAVE_OCSP_STAPLING */ + +#ifdef HAVE_SRP + DMP_STRING("SSLSRPUnknownUserSeed", ctx->srp_unknown_user_seed); + DMP_STRING("SSLSRPVerifierFile", ctx->srp_vfile); +#endif + DMP_ON_OFF("SSLOCSPEnable", ctx->ocsp_enabled); + DMP_ON_OFF("SSLOCSPOverrideResponder", ctx->ocsp_force_default); + DMP_STRING("SSLOCSPDefaultResponder", ctx->ocsp_responder); + DMP_LONG( "SSLOCSPResponseTimeSkew", ctx->ocsp_resptime_skew); + DMP_LONG( "SSLOCSPResponseMaxAge", ctx->ocsp_resp_maxage); + DMP_ITIME( "SSLOCSPResponderTimeout", ctx->ocsp_responder_timeout); + DMP_ON_OFF("SSLOCSPUseRequestNonce", ctx->ocsp_use_request_nonce); + DMP_URI( "SSLOCSPProxyURL", ctx->proxy_uri); + DMP_ON_OFF("SSLOCSPNoVerify", ctx->ocsp_noverify); + DMP_STRING("SSLOCSPResponderCertificateFile", ctx->ocsp_certs_file); + +#ifdef HAVE_SSL_CONF_CMD + if (ctx->ssl_ctx_param && ctx->ssl_ctx_param->nelts > 0) { + ssl_ctx_param_t *param = (ssl_ctx_param_t *)ctx->ssl_ctx_param->elts; + for (i = 0; i < ctx->ssl_ctx_param->nelts; ++i, ++param) { + apr_file_printf(out, "%s\n%s\"%s\": \"%s %s\"", *psep, indent, + "SSLOpenSSLConfCmd", json_quote(param->name, p), + json_quote(param->value, p)); + *psep = ", "; + } + } +#endif + +#ifdef HAVE_TLS_SESSION_TICKETS + if (ctx->ticket_key) { + DMP_STRING("SSLSessionTicketKeyFile", ctx->ticket_key->file_path); + } +#endif + } + else { /* proxy */ + if (ctx->pkp) { + DMP_STRING("SSLProxyMachineCertificateFile", ctx->pkp->cert_file); + DMP_STRING("SSLProxyMachineCertificatePath", ctx->pkp->cert_path); + DMP_STRING("SSLProxyMachineCertificateChainFile", ctx->pkp->ca_cert_file); + } + DMP_ON_OFF("SSLProxyCheckPeerCN", ctx->ssl_check_peer_cn); + DMP_ON_OFF("SSLProxyCheckPeerName", ctx->ssl_check_peer_cn); + DMP_ON_OFF("SSLProxyCheckPeerExpire", ctx->ssl_check_peer_expire); + } +} + +static void ssl_srv_dump(SSLSrvConfigRec *sc, apr_pool_t *p, + apr_file_t *out, const char *indent, const char **psep) +{ + DMP_ENABLD("SSLEngine", sc->enabled); + DMP_ON_OFF("SSLHonorCipherOrder", sc->cipher_server_pref); + +#ifndef OPENSSL_NO_COMP + DMP_ON_OFF("SSLCompression", sc->compression); +#endif + + modssl_ctx_dump(sc->server, p, 0, out, indent, psep); + + DMP_LONG( "SSLSessionCacheTimeout", sc->session_cache_timeout); + DMP_ON_OFF("SSLInsecureRenegotiation", sc->insecure_reneg); + DMP_ON_OFF("SSLStrictSNIVHostCheck", sc->strict_sni_vhost_check); +#ifdef HAVE_FIPS + DMP_ON_OFF("SSLFIPS", sc->fips); +#endif + DMP_ON_OFF("SSLSessionTickets", sc->session_tickets); + DMP_STRARR("SSLPolicy", sc->policies); +} + +static void ssl_dir_dump(SSLDirConfigRec *dc, apr_pool_t *p, + apr_file_t *out, const char *indent, const char **psep) +{ + int i; + + DMP_ON_OFF("SSLProxyEngine", dc->proxy_enabled); + + modssl_ctx_dump(dc->proxy, p, 1, out, indent, psep); + + if (dc->bSSLRequired == TRUE) { + DMP_ON_OFF("SSLRequireSSL", dc->bSSLRequired); + } + if (dc->aRequirement && dc->aRequirement->nelts > 0) { + ssl_require_t *r = (ssl_require_t *)dc->aRequirement->elts; + for (i = 0; i < dc->aRequirement->nelts; ++i, ++r) { + DMP_STRING("SSLRequire", r->cpExpr); + } + } + DMP_OPTION("StdEnvVars", SSL_OPT_STDENVVARS); + DMP_OPTION("ExportCertData", SSL_OPT_EXPORTCERTDATA); + DMP_OPTION("FakeBasicAuth", SSL_OPT_FAKEBASICAUTH); + DMP_OPTION("StrictRequire", SSL_OPT_STRICTREQUIRE); + DMP_OPTION("OptRenegotiate", SSL_OPT_OPTRENEGOTIATE); + DMP_OPTION("LegacyDNStringFormat", SSL_OPT_LEGACYDNFORMAT); + DMP_STRARR("SSLProxyPolicy", dc->policies); +} + +static void ssl_policy_dump(SSLPolicyRec *policy, apr_pool_t *p, + apr_file_t *out, const char *indent) +{ + const char *sep = ""; + if (policy->sc) { + ssl_srv_dump(policy->sc, p, out, indent, &sep); + } + if (policy->dc) { + ssl_dir_dump(policy->dc, p, out, indent, &sep); + } } + + + diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index ce84415abf1..79e9a58a301 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -280,6 +280,13 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, sc->fips = FALSE; } #endif + + if (sc->error_policy) { + rv = ssl_policy_lookup(p, sc->error_policy)? APR_EGENERAL : APR_ENOENT; + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, APLOGNO() + "Applying SSLPolicy '%s'", sc->error_policy); + return rv; + } } #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h index a46e6c82dfd..a6bb59f04cb 100644 --- a/modules/ssl/ssl_private.h +++ b/modules/ssl/ssl_private.h @@ -586,6 +586,7 @@ typedef struct { apr_global_mutex_t *stapling_cache_mutex; apr_global_mutex_t *stapling_refresh_mutex; #endif + } SSLModConfigRec; /** Structure representing configured filenames for certs and keys for @@ -736,6 +737,9 @@ struct SSLSrvConfigRec { BOOL compression; #endif BOOL session_tickets; + + apr_array_header_t *policies; /* policy that shall be applied to this config */ + const char *error_policy; /* error in policy merge, bubble up */ }; /** @@ -758,8 +762,20 @@ struct SSLDirConfigRec { modssl_ctx_t *proxy; BOOL proxy_enabled; BOOL proxy_post_config; + + apr_array_header_t *policies; /* policy that shall be applied to this config */ + const char *error_policy; /* error in policy merge, bubble up */ +}; + +typedef struct SSLPolicyRec SSLPolicyRec; +struct SSLPolicyRec { + const char *name; + SSLSrvConfigRec *sc; + SSLDirConfigRec *dc; }; +SSLPolicyRec *ssl_policy_lookup(apr_pool_t *pool, const char *name); + /** * function prototypes */ @@ -777,6 +793,8 @@ void *ssl_config_perdir_create(apr_pool_t *, char *); void *ssl_config_perdir_merge(apr_pool_t *, void *, void *); void ssl_config_proxy_merge(apr_pool_t *, SSLDirConfigRec *, SSLDirConfigRec *); +const char *ssl_cmd_SSLPolicyDefine(cmd_parms *, void *, const char *); +const char *ssl_cmd_SSLPolicyApply(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *); @@ -808,6 +826,7 @@ const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char * const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag); +const char *ssl_cmd_SSLProxyPolicyApply(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag); const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *);