/* _ _ ** _ __ ___ ___ __| | ___ ___| | mod_ssl ** | '_ ` _ \ / _ \ / _` | / __/ __| | Apache Interface to OpenSSL ** | | | | | | (_) | (_| | \__ \__ \ | www.modssl.org ** |_| |_| |_|\___/ \__,_|___|___/___/_| ftp.modssl.org ** |_____| ** mod_ssl.c ** Apache API interface structures */ /* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000-2002 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== */ #include "mod_ssl.h" #include "util_md5.h" #include /* * 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 } static const command_rec ssl_config_cmds[] = { /* * Global (main-server) context configuration directives */ SSL_CMD_SRV(Mutex, TAKE1, "SSL lock for handling internal mutual exclusions " "(`none', `file:/path/to/file')") 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')") #ifdef SSL_EXPERIMENTAL_ENGINE 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, FLAG, "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)") #ifdef SSL_EXPERIMENTAL_PERDIRCA 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)") #else SSL_CMD_SRV(CACertificatePath, TAKE1, "SSL CA Certificate path " "(`/path/to/dir' - contains PEM encoded files)") SSL_CMD_SRV(CACertificateFile, TAKE1, "SSL CA Certificate file " "(`/path/to/file' - PEM encoded)") #endif 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(Log, TAKE1, "SSL logfile for SSL-related messages " "(`/path/to/file', `|/path/to/program')") SSL_CMD_SRV(LogLevel, TAKE1, "SSL logfile verbosity level " "(`none', `error', `warn', `info', `debug')") SSL_CMD_SRV(Protocol, RAW_ARGS, "Enable or disable various SSL protocols" "(`[+-][SSLv2|SSLv3|TLSv1] ...' - see manual)") #ifdef SSL_EXPERIMENTAL_PROXY /* * Proxy configuration for remote SSL connections */ 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, FLAG, "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(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)") #endif /* * 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)") AP_END_CMD }; /* * the various processing hooks */ static int ssl_hook_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { /* Register us to handle mod_log_config %c/%x variables */ ssl_var_log_config_register(pconf); #if 0 /* XXX */ /* XXX: Register us to handle mod_proxy extensions that don't exist yet */ ssl_ext_proxy_register(pconf); /* XXX: Register us to handle mod_status extensions that don't exist yet */ ssl_scache_status_register(pconf); #endif /* -0- */ return OK; } static int ssl_hook_pre_connection(conn_rec *c, void *csd) { SSLSrvConfigRec *sc = mySrvConfig(c->base_server); SSL *ssl; SSLConnRec *sslconn; /* * Immediately stop processing if SSL is disabled for this connection */ if (!(sc && sc->bEnabled)) { return DECLINED; } /* * Create SSL context */ sslconn = apr_pcalloc(c->pool, sizeof(*sslconn)); myConnConfigSet(c, sslconn); sslconn->log_level = sc->nLogLevel; /* * Remember the connection information for * later access inside callback functions */ ssl_log(c->base_server, SSL_LOG_INFO, "Connection to child %d established " "(server %s, client %s)", c->id, sc->szVHostID, c->remote_ip ? c->remote_ip : "unknown"); /* * Seed the Pseudo Random Number Generator (PRNG) */ ssl_rand_seed(c->base_server, c->pool, SSL_RSCTX_CONNECT, ""); /* * 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(sc->pSSLCtx))) { ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Unable to create a new SSL connection from the SSL context"); c->aborted = 1; return DECLINED; /* XXX */ } if (!SSL_set_session_id_context(ssl, (unsigned char *)sc->szVHostID, sc->nVHostID_length)) { ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Unable to set session id context to `%s'", sc->szVHostID); 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 apr_status_t ssl_abort(SSLFilterRec *filter, conn_rec *c) { SSLConnRec *sslconn = myConnConfig(c); /* * try to gracefully shutdown the connection: * - send an own shutdown message (be gracefully) * - don't wait for peer's shutdown message (deadloop) * - kick away the SSL stuff immediately * - block the socket, so Apache cannot operate any more */ SSL_set_shutdown(filter->pssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(filter->pssl); SSL_free(filter->pssl); filter->pssl = NULL; /* so filters know we've been shutdown */ sslconn->ssl = NULL; c->aborted = 1; return APR_EGENERAL; } /* * The hook is NOT registered with ap_hook_process_connection. Instead, it is * called manually from the churn () before it tries to read any data. * There is some problem if I accept conn_rec *. Still investigating.. * Adv. if conn_rec * can be accepted is we can hook this function using the * ap_hook_process_connection hook. */ int ssl_hook_process_connection(SSLFilterRec *filter) { conn_rec *c = (conn_rec *)SSL_get_app_data(filter->pssl); SSLConnRec *sslconn = myConnConfig(c); SSLSrvConfigRec *sc = mySrvConfig(c->base_server); X509 *cert; int n, err; long verify_result; if (!SSL_is_init_finished(filter->pssl)) { if ((n = SSL_accept(filter->pssl)) <= 0) { err = SSL_get_error(filter->pssl, n); if (err == SSL_ERROR_ZERO_RETURN) { /* * The case where the connection was closed before any data * was transferred. That's not a real error and can occur * sporadically with some clients. */ ssl_log(c->base_server, SSL_LOG_INFO, "SSL handshake stopped: connection was closed"); } else if (err == SSL_ERROR_WANT_READ) { /* * This is in addition to what was present earlier. It is * borrowed from openssl_state_machine.c [mod_tls]. * TBD. */ return SSL_ERROR_WANT_READ; } else if (ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) { /* * The case where OpenSSL has recognized a HTTP request: * This means the client speaks plain HTTP on our HTTPS port. * Hmmmm... Punt this out of here after removing our output * filter. */ ap_remove_output_filter(filter->pOutputFilter); return HTTP_BAD_REQUEST; } else if ((SSL_get_error(filter->pssl, n) == SSL_ERROR_SYSCALL) && (errno != EINTR)) { if (errno > 0) { ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO, "SSL handshake interrupted by system " "[Hint: Stop button pressed in browser?!]"); } else { ssl_log(c->base_server, SSL_LOG_INFO|SSL_ADD_SSLERR|SSL_ADD_ERRNO, "Spurious SSL handshake interrupt [Hint: " "Usually just one of those OpenSSL confusions!?]"); } } else { /* * Ok, anything else is a fatal error */ ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR|SSL_ADD_ERRNO, "SSL handshake failed (server %s, client %s)", ssl_util_vhostid(c->pool, c->base_server), c->remote_ip ? c->remote_ip : "unknown"); } return ssl_abort(filter, c); } /* * Check for failed client authentication */ verify_result = SSL_get_verify_result(filter->pssl); if ((verify_result != X509_V_OK) || sslconn->verify_error) { if (ssl_verify_error_is_optional(verify_result) && (sc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA)) { /* leaving this log message as an error for the moment, * according to the mod_ssl docs: * "level optional_no_ca is actually against the idea * of authentication (but can be used to establish * SSL test pages, etc.)" * optional_no_ca doesn't appear to work as advertised * in 1.x */ ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "SSL client authentication failed, " "accepting certificate based on " "\"SSLVerifyClient optional_no_ca\" configuration"); } else { const char *error = sslconn->verify_error ? sslconn->verify_error : X509_verify_cert_error_string(verify_result); ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "SSL client authentication failed: %s", error ? error : "unknown"); return ssl_abort(filter, c); } } /* * Remember the peer certificate's DN */ if ((cert = SSL_get_peer_certificate(filter->pssl))) { sslconn->client_cert = cert; sslconn->client_dn = NULL; } /* * Make really sure that when a peer certificate * is required we really got one... (be paranoid) */ if ((sc->nVerifyClient == SSL_CVERIFY_REQUIRE) && !sslconn->client_cert) { ssl_log(c->base_server, SSL_LOG_ERROR, "No acceptable peer certificate available"); return ssl_abort(filter, c); } } return APR_SUCCESS; } static const char *ssl_hook_http_method(const request_rec *r) { SSLSrvConfigRec *sc = mySrvConfig(r->server); if (sc->bEnabled == FALSE) { return NULL; } return "https"; } static apr_port_t ssl_hook_default_port(const request_rec *r) { SSLSrvConfigRec *sc = mySrvConfig(r->server); if (sc->bEnabled == FALSE) { return 0; } return 443; } /* * 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_handler (ssl_hook_Handler, 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); ssl_var_register(); } 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 */ };