From: William A. Rowe Jr Date: Sat, 14 Dec 2002 07:46:45 +0000 (+0000) Subject: After some productive feedback and no negative feedback, introduce X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8f6a2a19a718cfdadc1c9661b044cfabc168cc4e;p=apache After some productive feedback and no negative feedback, introduce SSLEngine upgrade so that we can begin and continue to support these facilities. This makes it simpler to keep this effort (while we have no known clients that support Connection: upgrade at this time), and begin refactoring more of SSL into smaller and tighter (and then optional) components. Submitted by: Ryan Bloom Reviewed by: William Rowe, Joe Orton git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@97912 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h index 15d71919cb..8cfbc4f8bd 100644 --- a/modules/ssl/mod_ssl.h +++ b/modules/ssl/mod_ssl.h @@ -541,7 +541,7 @@ const char *ssl_cmd_SSLMutex(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 *); -const char *ssl_cmd_SSLEngine(cmd_parms *, void *, int); +const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *); const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *); @@ -588,6 +588,7 @@ int ssl_hook_UserCheck(request_rec *); int ssl_hook_Access(request_rec *); int ssl_hook_Fixup(request_rec *); int ssl_hook_ReadReq(request_rec *); +int ssl_hook_Upgrade(request_rec *); /* OpenSSL callbacks */ RSA *ssl_callback_TmpRSA(SSL *, int, int); @@ -709,6 +710,8 @@ ssl_algo_t ssl_util_algotypeof(X509 *, EVP_PKEY *); char *ssl_util_algotypestr(ssl_algo_t); char *ssl_util_ptxtsub(apr_pool_t *, const char *, const char *, char *); void ssl_util_thread_setup(apr_pool_t *); +int ssl_init_ssl_connection(conn_rec *c); + #define APR_SHM_MAXSIZE (64 * 1024 * 1024) #endif /* __MOD_SSL_H__ */ diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c index 57fdc3e88b..08c30fc83c 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -205,7 +205,7 @@ static SSLSrvConfigRec *ssl_config_server_new(apr_pool_t *p) SSLSrvConfigRec *sc = apr_palloc(p, sizeof(*sc)); sc->mc = NULL; - sc->enabled = UNSET; + sc->enabled = FALSE; sc->proxy_enabled = UNSET; sc->vhost_id = NULL; /* set during module init */ sc->vhost_id_len = 0; /* set during module init */ @@ -581,13 +581,24 @@ const char *ssl_cmd_SSLRandomSeed(cmd_parms *cmd, return NULL; } -const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, int flag) +const char *ssl_cmd_SSLEngine(cmd_parms *cmd, void *dcfg, const char *arg) { SSLSrvConfigRec *sc = mySrvConfig(cmd->server); - sc->enabled = flag ? TRUE : FALSE; - + if (!strcasecmp(arg, "On")) { + sc->enabled = TRUE; return NULL; + } + else if (!strcasecmp(arg, "Off")) { + sc->enabled = FALSE; + return NULL; + } + else if (!strcasecmp(arg, "Optional")) { + sc->enabled = UNSET; + return NULL; + } + + return "Argument must be On, Off, or Optional"; } const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd, diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c index 7a2e74273e..99428bfa8b 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c @@ -247,11 +247,13 @@ int ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, sc->vhost_id = ssl_util_vhostid(p, s); sc->vhost_id_len = strlen(sc->vhost_id); +#if 0 + /* If sc->enabled is UNSET, then SSL is optional on this vhost */ /* Fix up stuff that may not have been set */ if (sc->enabled == UNSET) { sc->enabled = FALSE; } - +#endif if (sc->proxy_enabled == UNSET) { sc->proxy_enabled = FALSE; } @@ -982,6 +984,9 @@ void ssl_init_ConfigureServer(server_rec *s, apr_pool_t *ptemp, SSLSrvConfigRec *sc) { + /* A bit of a hack, but initialize the server if SSL is optional or + * not. + */ if (sc->enabled) { ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "Configuring server for SSL protocol"); @@ -1010,7 +1015,7 @@ void ssl_init_CheckServers(server_rec *base_server, apr_pool_t *p) for (s = base_server; s; s = s->next) { sc = mySrvConfig(s); - if (sc->enabled && (s->port == DEFAULT_HTTP_PORT)) { + if ((sc->enabled == TRUE) && (s->port == DEFAULT_HTTP_PORT)) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, base_server, "Init: (%s) You configured HTTPS(%d) " diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index 041a59b653..6aa585e2fc 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -1181,6 +1181,89 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx) return APR_SUCCESS; } +static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f, + apr_bucket_brigade *bb) + +{ +#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols" +#define UPGRADE_HEADER "Upgrade: TLS/1.0 HTTP/1.1" +#define CONNECTION_HEADER "Connection: Upgrade" + const char *upgrade; + const char *connection; + apr_bucket_brigade *upgradebb; + request_rec *r = f->r; + SSLConnRec *sslconn; + SSL *ssl; + + /* Just remove the filter, if it doesn't work the first time, it won't + * work at all for this request. + */ + ap_remove_output_filter(f); + + /* No need to ensure that this is a server with optional SSL, the filter + * is only inserted if that is true. + */ + + upgrade = apr_table_get(r->headers_in, "Upgrade"); + if (upgrade == NULL) { + return ap_pass_brigade(f->next, bb); + } + connection = apr_table_get(r->headers_in, "Connection"); + + apr_table_unset(r->headers_out, "Upgrade"); + + /* XXX: I don't think the requirement that the client sends exactly + * "Connection: Upgrade" is correct; the only requirement here is + * on the client to send a Connection header including the "upgrade" + * token. + */ + if (strcmp(connection, "Upgrade") || strcmp(upgrade, "TLS/1.0")) { + return ap_pass_brigade(f->next, bb); + } + + if (r->method_number == M_OPTIONS) { + apr_bucket *b = NULL; + /* This is a mandatory SSL upgrade. */ + + upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc); + + ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF, + UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL); + + b = apr_bucket_flush_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(upgradebb, b); + + ap_pass_brigade(f->next, upgradebb); + } + else { + /* This is optional, and should be configurable, for now don't bother + * doing anything. + */ + return ap_pass_brigade(f->next, bb); + } + + ssl_init_ssl_connection(f->c); + + ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, + "Awaiting re-negotiation handshake"); + + sslconn = myConnConfig(f->c); + ssl = sslconn->ssl; + + SSL_set_state(ssl, SSL_ST_ACCEPT); + SSL_do_handshake(ssl); + + if (SSL_get_state(ssl) != SSL_ST_OK) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Re-negotiation handshake failed: " + "Not accepted by client!?"); + + return AP_FILTER_ERROR; + } + + return OK; +} + static apr_status_t ssl_io_filter_input(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, @@ -1393,6 +1476,11 @@ void ssl_io_filter_init(conn_rec *c, SSL *ssl) void ssl_io_filter_register(apr_pool_t *p) { + /* This filter MUST be after the HTTP_HEADER filter, but it also must be + * a resource-level filter so it has the request_rec. + */ + ap_register_output_filter ("UPGRADE_FILTER", ssl_io_filter_Upgrade, NULL, AP_FTYPE_PROTOCOL + 5); + ap_register_input_filter (ssl_io_filter, ssl_io_filter_input, NULL, AP_FTYPE_CONNECTION + 5); ap_register_output_filter (ssl_io_filter, ssl_io_filter_output, NULL, AP_FTYPE_CONNECTION + 5); return; diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c index d4d4c5ce6e..bbc5707a19 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -223,6 +223,16 @@ int ssl_hook_Access(request_rec *r) * Support for SSLRequireSSL directive */ if (dc->bSSLRequired && !ssl) { + if (sc->enabled == UNSET) { + /* This vhost was configured for optional SSL, just tell the + * client that we need to upgrade. + */ + apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1"); + apr_table_setn(r->err_headers_out, "Connection", "Upgrade"); + + return HTTP_UPGRADE_REQUIRED; + } + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "access to %s failed, reason: %s", r->filename, "SSL connection required"); @@ -1014,6 +1024,10 @@ int ssl_hook_Fixup(request_rec *r) SSL *ssl; int i; + if (sc->enabled == UNSET) { + apr_table_setn(r->headers_out, "Upgrade", "TLS/1.0, HTTP/1.1"); + } + /* * Check to see if SSL is on */ diff --git a/modules/ssl/ssl_util.c b/modules/ssl/ssl_util.c index 9b208b404f..6b3ecabeaa 100644 --- a/modules/ssl/ssl_util.c +++ b/modules/ssl/ssl_util.c @@ -84,7 +84,7 @@ char *ssl_util_vhostid(apr_pool_t *p, server_rec *s) port = s->port; else { sc = mySrvConfig(s); - if (sc->enabled) + if (sc->enabled == TRUE) port = DEFAULT_HTTPS_PORT; else port = DEFAULT_HTTP_PORT;