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;