| /* 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. |
| */ |
| |
| /* _ _ |
| * _ __ ___ ___ __| | ___ ___| | mod_ssl |
| * | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL |
| * | | | | | | (_) | (_| | \__ \__ \ | |
| * |_| |_| |_|\___/ \__,_|___|___/___/_| |
| * |_____| |
| * mod_ssl.c |
| * Apache API interface structures |
| */ |
| |
| #include "ssl_private.h" |
| #include "mod_ssl.h" |
| #include "util_md5.h" |
| #include <assert.h> |
| |
| /* |
| * the table of configuration directives we provide |
| */ |
| |
| #define SSL_CMD_ALL(name, args, desc) \ |
| AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \ |
| NULL, RSRC_CONF|OR_AUTHCFG, desc), |
| |
| #define SSL_CMD_SRV(name, args, desc) \ |
| AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \ |
| NULL, RSRC_CONF, desc), |
| |
| #define SSL_CMD_DIR(name, type, args, desc) \ |
| AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, \ |
| NULL, OR_##type, desc), |
| |
| #define AP_END_CMD { NULL } |
| |
| const char ssl_valid_ssl_mutex_string[] = |
| "Valid SSLMutex mechanisms are: `none', `default'" |
| #if APR_HAS_FLOCK_SERIALIZE |
| ", `flock:/path/to/file'" |
| #endif |
| #if APR_HAS_FCNTL_SERIALIZE |
| ", `fcntl:/path/to/file'" |
| #endif |
| #if APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM) |
| ", `sysvsem'" |
| #endif |
| #if APR_HAS_POSIXSEM_SERIALIZE |
| ", `posixsem'" |
| #endif |
| #if APR_HAS_PROC_PTHREAD_SERIALIZE |
| ", `pthread'" |
| #endif |
| #if APR_HAS_FLOCK_SERIALIZE || APR_HAS_FCNTL_SERIALIZE |
| ", `file:/path/to/file'" |
| #endif |
| #if (APR_HAS_SYSVSEM_SERIALIZE && !defined(PERCHILD_MPM)) || APR_HAS_POSIXSEM_SERIALIZE |
| ", `sem'" |
| #endif |
| " "; |
| |
| static const command_rec ssl_config_cmds[] = { |
| /* |
| * Global (main-server) context configuration directives |
| */ |
| SSL_CMD_SRV(Mutex, TAKE1, ssl_valid_ssl_mutex_string) |
| SSL_CMD_SRV(PassPhraseDialog, TAKE1, |
| "SSL dialog mechanism for the pass phrase query " |
| "(`builtin', `|/path/to/pipe_program`, " |
| "or `exec:/path/to/cgi_program')") |
| SSL_CMD_SRV(SessionCache, TAKE1, |
| "SSL Session Cache storage " |
| "(`none', `dbm:/path/to/file')") |
| #if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) |
| SSL_CMD_SRV(CryptoDevice, TAKE1, |
| "SSL external Crypto Device usage " |
| "(`builtin', `...')") |
| #endif |
| SSL_CMD_SRV(RandomSeed, TAKE23, |
| "SSL Pseudo Random Number Generator (PRNG) seeding source " |
| "(`startup|connect builtin|file:/path|exec:/path [bytes]')") |
| |
| /* |
| * Per-server context configuration directives |
| */ |
| SSL_CMD_SRV(Engine, TAKE1, |
| "SSL switch for the protocol engine " |
| "(`on', `off')") |
| SSL_CMD_ALL(CipherSuite, TAKE1, |
| "Colon-delimited list of permitted SSL Ciphers " |
| "(`XXX:...:XXX' - see manual)") |
| SSL_CMD_SRV(CertificateFile, TAKE1, |
| "SSL Server Certificate file " |
| "(`/path/to/file' - PEM or DER encoded)") |
| SSL_CMD_SRV(CertificateKeyFile, TAKE1, |
| "SSL Server Private Key file " |
| "(`/path/to/file' - PEM or DER encoded)") |
| SSL_CMD_SRV(CertificateChainFile, TAKE1, |
| "SSL Server CA Certificate Chain file " |
| "(`/path/to/file' - PEM encoded)") |
| SSL_CMD_ALL(CACertificatePath, TAKE1, |
| "SSL CA Certificate path " |
| "(`/path/to/dir' - contains PEM encoded files)") |
| SSL_CMD_ALL(CACertificateFile, TAKE1, |
| "SSL CA Certificate file " |
| "(`/path/to/file' - PEM encoded)") |
| SSL_CMD_SRV(CARevocationPath, TAKE1, |
| "SSL CA Certificate Revocation List (CRL) path " |
| "(`/path/to/dir' - contains PEM encoded files)") |
| SSL_CMD_SRV(CARevocationFile, TAKE1, |
| "SSL CA Certificate Revocation List (CRL) file " |
| "(`/path/to/file' - PEM encoded)") |
| SSL_CMD_ALL(VerifyClient, TAKE1, |
| "SSL Client verify type " |
| "(`none', `optional', `require', `optional_no_ca')") |
| SSL_CMD_ALL(VerifyDepth, TAKE1, |
| "SSL Client verify depth " |
| "(`N' - number of intermediate certificates)") |
| SSL_CMD_SRV(SessionCacheTimeout, TAKE1, |
| "SSL Session Cache object lifetime " |
| "(`N' - number of seconds)") |
| SSL_CMD_SRV(Protocol, RAW_ARGS, |
| "Enable or disable various SSL protocols" |
| "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)") |
| SSL_CMD_SRV(HonorCipherOrder, FLAG, |
| "Use the server's cipher ordering preference") |
| SSL_CMD_ALL(UserName, TAKE1, |
| "Set user name to SSL variable value") |
| |
| /* |
| * Proxy configuration for remote SSL connections |
| */ |
| SSL_CMD_SRV(ProxyEngine, FLAG, |
| "SSL switch for the proxy protocol engine " |
| "(`on', `off')") |
| SSL_CMD_SRV(ProxyProtocol, RAW_ARGS, |
| "SSL Proxy: enable or disable SSL protocol flavors " |
| "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)") |
| SSL_CMD_SRV(ProxyCipherSuite, TAKE1, |
| "SSL Proxy: colon-delimited list of permitted SSL ciphers " |
| "(`XXX:...:XXX' - see manual)") |
| SSL_CMD_SRV(ProxyVerify, TAKE1, |
| "SSL Proxy: whether to verify the remote certificate " |
| "(`on' or `off')") |
| SSL_CMD_SRV(ProxyVerifyDepth, TAKE1, |
| "SSL Proxy: maximum certificate verification depth " |
| "(`N' - number of intermediate certificates)") |
| SSL_CMD_SRV(ProxyCACertificateFile, TAKE1, |
| "SSL Proxy: file containing server certificates " |
| "(`/path/to/file' - PEM encoded certificates)") |
| SSL_CMD_SRV(ProxyCACertificatePath, TAKE1, |
| "SSL Proxy: directory containing server certificates " |
| "(`/path/to/dir' - contains PEM encoded certificates)") |
| SSL_CMD_SRV(ProxyCARevocationPath, TAKE1, |
| "SSL Proxy: CA Certificate Revocation List (CRL) path " |
| "(`/path/to/dir' - contains PEM encoded files)") |
| SSL_CMD_SRV(ProxyCARevocationFile, TAKE1, |
| "SSL Proxy: CA Certificate Revocation List (CRL) file " |
| "(`/path/to/file' - PEM encoded)") |
| SSL_CMD_SRV(ProxyMachineCertificateFile, TAKE1, |
| "SSL Proxy: file containing client certificates " |
| "(`/path/to/file' - PEM encoded certificates)") |
| SSL_CMD_SRV(ProxyMachineCertificatePath, TAKE1, |
| "SSL Proxy: directory containing client certificates " |
| "(`/path/to/dir' - contains PEM encoded certificates)") |
| |
| /* |
| * Per-directory context configuration directives |
| */ |
| SSL_CMD_DIR(Options, OPTIONS, RAW_ARGS, |
| "Set one or more options to configure the SSL engine" |
| "(`[+-]option[=value] ...' - see manual)") |
| SSL_CMD_DIR(RequireSSL, AUTHCFG, NO_ARGS, |
| "Require the SSL protocol for the per-directory context " |
| "(no arguments)") |
| SSL_CMD_DIR(Require, AUTHCFG, RAW_ARGS, |
| "Require a boolean expression to evaluate to true for granting access" |
| "(arbitrary complex boolean expression - see manual)") |
| |
| /* Deprecated directives. */ |
| AP_INIT_RAW_ARGS("SSLLog", ap_set_deprecated, NULL, OR_ALL, |
| "SSLLog directive is no longer supported - use ErrorLog."), |
| AP_INIT_RAW_ARGS("SSLLogLevel", ap_set_deprecated, NULL, OR_ALL, |
| "SSLLogLevel directive is no longer supported - use LogLevel."), |
| |
| AP_END_CMD |
| }; |
| |
| /* |
| * the various processing hooks |
| */ |
| static apr_status_t ssl_cleanup_pre_config(void *data) |
| { |
| /* |
| * Try to kill the internals of the SSL library. |
| */ |
| #ifdef HAVE_OPENSSL |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 |
| /* Corresponds to OPENSSL_load_builtin_modules(): |
| * XXX: borrowed from apps.h, but why not CONF_modules_free() |
| * which also invokes CONF_modules_finish()? |
| */ |
| CONF_modules_unload(1); |
| #endif |
| #endif |
| /* Corresponds to SSL_library_init: */ |
| EVP_cleanup(); |
| #if HAVE_ENGINE_LOAD_BUILTIN_ENGINES |
| ENGINE_cleanup(); |
| #endif |
| #ifdef HAVE_OPENSSL |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 |
| CRYPTO_cleanup_all_ex_data(); |
| #endif |
| #endif |
| ERR_remove_state(0); |
| |
| /* Don't call ERR_free_strings here; ERR_load_*_strings only |
| * actually load the error strings once per process due to static |
| * variable abuse in OpenSSL. */ |
| |
| /* |
| * TODO: determine somewhere we can safely shove out diagnostics |
| * (when enabled) at this late stage in the game: |
| * CRYPTO_mem_leaks_fp(stderr); |
| */ |
| return APR_SUCCESS; |
| } |
| |
| static int ssl_hook_pre_config(apr_pool_t *pconf, |
| apr_pool_t *plog, |
| apr_pool_t *ptemp) |
| { |
| /* We must register the library in full, to ensure our configuration |
| * code can successfully test the SSL environment. |
| */ |
| CRYPTO_malloc_init(); |
| #ifdef HAVE_OPENSSL |
| ERR_load_crypto_strings(); |
| #endif |
| SSL_load_error_strings(); |
| SSL_library_init(); |
| #if HAVE_ENGINE_LOAD_BUILTIN_ENGINES |
| ENGINE_load_builtin_engines(); |
| #endif |
| #ifdef HAVE_OPENSSL |
| #if OPENSSL_VERSION_NUMBER >= 0x00907001 |
| OPENSSL_load_builtin_modules(); |
| #endif |
| #endif |
| |
| /* |
| * Let us cleanup the ssl library when the module is unloaded |
| */ |
| apr_pool_cleanup_register(pconf, NULL, ssl_cleanup_pre_config, |
| apr_pool_cleanup_null); |
| |
| /* Register us to handle mod_log_config %c/%x variables */ |
| ssl_var_log_config_register(pconf); |
| |
| /* Register to handle mod_status status page generation */ |
| ssl_scache_status_register(pconf); |
| |
| return OK; |
| } |
| |
| static SSLConnRec *ssl_init_connection_ctx(conn_rec *c) |
| { |
| SSLConnRec *sslconn = myConnConfig(c); |
| |
| if (sslconn) { |
| return sslconn; |
| } |
| |
| sslconn = apr_pcalloc(c->pool, sizeof(*sslconn)); |
| |
| myConnConfigSet(c, sslconn); |
| |
| return sslconn; |
| } |
| |
| int ssl_proxy_enable(conn_rec *c) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(c->base_server); |
| |
| SSLConnRec *sslconn = ssl_init_connection_ctx(c); |
| |
| if (!sc->proxy_enabled) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, |
| "SSL Proxy requested for %s but not enabled " |
| "[Hint: SSLProxyEngine]", sc->vhost_id); |
| |
| return 0; |
| } |
| |
| sslconn->is_proxy = 1; |
| sslconn->disabled = 0; |
| |
| return 1; |
| } |
| |
| int ssl_engine_disable(conn_rec *c) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(c->base_server); |
| |
| SSLConnRec *sslconn; |
| |
| if (sc->enabled == SSL_ENABLED_FALSE) { |
| return 0; |
| } |
| |
| sslconn = ssl_init_connection_ctx(c); |
| |
| sslconn->disabled = 1; |
| |
| return 1; |
| } |
| |
| int ssl_init_ssl_connection(conn_rec *c) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(c->base_server); |
| SSL *ssl; |
| SSLConnRec *sslconn = myConnConfig(c); |
| char *vhost_md5; |
| modssl_ctx_t *mctx; |
| |
| /* |
| * Seed the Pseudo Random Number Generator (PRNG) |
| */ |
| ssl_rand_seed(c->base_server, c->pool, SSL_RSCTX_CONNECT, ""); |
| |
| if (!sslconn) { |
| sslconn = ssl_init_connection_ctx(c); |
| } |
| |
| mctx = sslconn->is_proxy ? sc->proxy : sc->server; |
| |
| /* |
| * Create a new SSL connection with the configured server SSL context and |
| * attach this to the socket. Additionally we register this attachment |
| * so we can detach later. |
| */ |
| if (!(ssl = SSL_new(mctx->ssl_ctx))) { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, |
| "Unable to create a new SSL connection from the SSL " |
| "context"); |
| ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); |
| |
| c->aborted = 1; |
| |
| return DECLINED; /* XXX */ |
| } |
| |
| vhost_md5 = ap_md5_binary(c->pool, (unsigned char *)sc->vhost_id, |
| sc->vhost_id_len); |
| |
| if (!SSL_set_session_id_context(ssl, (unsigned char *)vhost_md5, |
| APR_MD5_DIGESTSIZE*2)) |
| { |
| ap_log_error(APLOG_MARK, APLOG_ERR, 0, c->base_server, |
| "Unable to set session id context to `%s'", vhost_md5); |
| ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, c->base_server); |
| |
| c->aborted = 1; |
| |
| return DECLINED; /* XXX */ |
| } |
| |
| SSL_set_app_data(ssl, c); |
| SSL_set_app_data2(ssl, NULL); /* will be request_rec */ |
| |
| sslconn->ssl = ssl; |
| |
| /* |
| * Configure callbacks for SSL connection |
| */ |
| SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA); |
| SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH); |
| |
| SSL_set_verify_result(ssl, X509_V_OK); |
| |
| ssl_io_filter_init(c, ssl); |
| |
| return APR_SUCCESS; |
| } |
| |
| static const char *ssl_hook_http_method(const request_rec *r) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(r->server); |
| |
| if (sc->enabled == SSL_ENABLED_FALSE) { |
| return NULL; |
| } |
| |
| return "https"; |
| } |
| |
| static apr_port_t ssl_hook_default_port(const request_rec *r) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(r->server); |
| |
| if (sc->enabled == SSL_ENABLED_FALSE) { |
| return 0; |
| } |
| |
| return 443; |
| } |
| |
| static int ssl_hook_pre_connection(conn_rec *c, void *csd) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(c->base_server); |
| SSLConnRec *sslconn = myConnConfig(c); |
| |
| /* |
| * Immediately stop processing if SSL is disabled for this connection |
| */ |
| if (!(sc && (sc->enabled == SSL_ENABLED_TRUE || |
| (sslconn && sslconn->is_proxy)))) |
| { |
| return DECLINED; |
| } |
| |
| /* |
| * Create SSL context |
| */ |
| if (!sslconn) { |
| sslconn = ssl_init_connection_ctx(c); |
| } |
| |
| if (sslconn->disabled) { |
| return DECLINED; |
| } |
| |
| /* |
| * Remember the connection information for |
| * later access inside callback functions |
| */ |
| |
| ap_log_error(APLOG_MARK, APLOG_INFO, 0, c->base_server, |
| "Connection to child %ld established " |
| "(server %s, client %s)", c->id, sc->vhost_id, |
| c->remote_ip ? c->remote_ip : "unknown"); |
| |
| return ssl_init_ssl_connection(c); |
| } |
| |
| |
| static void ssl_hook_Insert_Filter(request_rec *r) |
| { |
| SSLSrvConfigRec *sc = mySrvConfig(r->server); |
| |
| if (sc->enabled == SSL_ENABLED_OPTIONAL) { |
| ap_add_output_filter("UPGRADE_FILTER", NULL, r, r->connection); |
| } |
| } |
| |
| /* |
| * the module registration phase |
| */ |
| |
| static void ssl_register_hooks(apr_pool_t *p) |
| { |
| ssl_io_filter_register(p); |
| |
| ap_hook_pre_connection(ssl_hook_pre_connection,NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_post_config (ssl_init_Module, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_http_method (ssl_hook_http_method, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_default_port (ssl_hook_default_port, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_pre_config (ssl_hook_pre_config, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_child_init (ssl_init_Child, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_translate_name(ssl_hook_Translate, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_check_user_id (ssl_hook_UserCheck, NULL,NULL, APR_HOOK_FIRST); |
| ap_hook_fixups (ssl_hook_Fixup, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_access_checker(ssl_hook_Access, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_auth_checker (ssl_hook_Auth, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_post_read_request(ssl_hook_ReadReq, NULL,NULL, APR_HOOK_MIDDLE); |
| ap_hook_insert_filter (ssl_hook_Insert_Filter, NULL,NULL, APR_HOOK_MIDDLE); |
| /* ap_hook_handler (ssl_hook_Upgrade, NULL,NULL, APR_HOOK_MIDDLE); */ |
| |
| ssl_var_register(); |
| |
| APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable); |
| APR_REGISTER_OPTIONAL_FN(ssl_engine_disable); |
| } |
| |
| module AP_MODULE_DECLARE_DATA ssl_module = { |
| STANDARD20_MODULE_STUFF, |
| ssl_config_perdir_create, /* create per-dir config structures */ |
| ssl_config_perdir_merge, /* merge per-dir config structures */ |
| ssl_config_server_create, /* create per-server config structures */ |
| ssl_config_server_merge, /* merge per-server config structures */ |
| ssl_config_cmds, /* table of configuration directives */ |
| ssl_register_hooks /* register hooks */ |
| }; |