@@ -1181,6 +1181,89 @@ static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx)
11811181 return APR_SUCCESS ;
11821182}
11831183
1184+ static apr_status_t ssl_io_filter_Upgrade (ap_filter_t * f ,
1185+ apr_bucket_brigade * bb )
1186+
1187+ {
1188+ #define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"
1189+ #define UPGRADE_HEADER "Upgrade: TLS/1.0 HTTP/1.1"
1190+ #define CONNECTION_HEADER "Connection: Upgrade"
1191+ const char * upgrade ;
1192+ const char * connection ;
1193+ apr_bucket_brigade * upgradebb ;
1194+ request_rec * r = f -> r ;
1195+ SSLConnRec * sslconn ;
1196+ SSL * ssl ;
1197+
1198+ /* Just remove the filter, if it doesn't work the first time, it won't
1199+ * work at all for this request.
1200+ */
1201+ ap_remove_output_filter (f );
1202+
1203+ /* No need to ensure that this is a server with optional SSL, the filter
1204+ * is only inserted if that is true.
1205+ */
1206+
1207+ upgrade = apr_table_get (r -> headers_in , "Upgrade" );
1208+ if (upgrade == NULL ) {
1209+ return ap_pass_brigade (f -> next , bb );
1210+ }
1211+ connection = apr_table_get (r -> headers_in , "Connection" );
1212+
1213+ apr_table_unset (r -> headers_out , "Upgrade" );
1214+
1215+ /* XXX: I don't think the requirement that the client sends exactly
1216+ * "Connection: Upgrade" is correct; the only requirement here is
1217+ * on the client to send a Connection header including the "upgrade"
1218+ * token.
1219+ */
1220+ if (strcmp (connection , "Upgrade" ) || strcmp (upgrade , "TLS/1.0" )) {
1221+ return ap_pass_brigade (f -> next , bb );
1222+ }
1223+
1224+ if (r -> method_number == M_OPTIONS ) {
1225+ apr_bucket * b = NULL ;
1226+ /* This is a mandatory SSL upgrade. */
1227+
1228+ upgradebb = apr_brigade_create (r -> pool , f -> c -> bucket_alloc );
1229+
1230+ ap_fputstrs (f -> next , upgradebb , SWITCH_STATUS_LINE , CRLF ,
1231+ UPGRADE_HEADER , CRLF , CONNECTION_HEADER , CRLF , CRLF , NULL );
1232+
1233+ b = apr_bucket_flush_create (f -> c -> bucket_alloc );
1234+ APR_BRIGADE_INSERT_TAIL (upgradebb , b );
1235+
1236+ ap_pass_brigade (f -> next , upgradebb );
1237+ }
1238+ else {
1239+ /* This is optional, and should be configurable, for now don't bother
1240+ * doing anything.
1241+ */
1242+ return ap_pass_brigade (f -> next , bb );
1243+ }
1244+
1245+ ssl_init_ssl_connection (f -> c );
1246+
1247+ ap_log_error (APLOG_MARK , APLOG_INFO , 0 , r -> server ,
1248+ "Awaiting re-negotiation handshake" );
1249+
1250+ sslconn = myConnConfig (f -> c );
1251+ ssl = sslconn -> ssl ;
1252+
1253+ SSL_set_state (ssl , SSL_ST_ACCEPT );
1254+ SSL_do_handshake (ssl );
1255+
1256+ if (SSL_get_state (ssl ) != SSL_ST_OK ) {
1257+ ap_log_error (APLOG_MARK , APLOG_ERR , 0 , r -> server ,
1258+ "Re-negotiation handshake failed: "
1259+ "Not accepted by client!?" );
1260+
1261+ return AP_FILTER_ERROR ;
1262+ }
1263+
1264+ return OK ;
1265+ }
1266+
11841267static apr_status_t ssl_io_filter_input (ap_filter_t * f ,
11851268 apr_bucket_brigade * bb ,
11861269 ap_input_mode_t mode ,
@@ -1393,6 +1476,11 @@ void ssl_io_filter_init(conn_rec *c, SSL *ssl)
13931476
13941477void ssl_io_filter_register (apr_pool_t * p )
13951478{
1479+ /* This filter MUST be after the HTTP_HEADER filter, but it also must be
1480+ * a resource-level filter so it has the request_rec.
1481+ */
1482+ ap_register_output_filter ("UPGRADE_FILTER" , ssl_io_filter_Upgrade , NULL , AP_FTYPE_PROTOCOL + 5 );
1483+
13961484 ap_register_input_filter (ssl_io_filter , ssl_io_filter_input , NULL , AP_FTYPE_CONNECTION + 5 );
13971485 ap_register_output_filter (ssl_io_filter , ssl_io_filter_output , NULL , AP_FTYPE_CONNECTION + 5 );
13981486 return ;
0 commit comments