ASF Bugzilla – Attachment 11681 Details for
Bug 12355
POST incompatible w/ renegotiate https: connection
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
patch for 2.0 to fix #12355
ssl_20reneg1.diff (text/plain), 22.99 KB, created by
Joe Orton
on 2004-05-27 13:14:45 UTC
(
hide
)
Description:
patch for 2.0 to fix #12355
Filename:
MIME Type:
Creator:
Joe Orton
Created:
2004-05-27 13:14:45 UTC
Size:
22.99 KB
patch
obsolete
>Index: mod_ssl.c >=================================================================== >RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.c,v >retrieving revision 1.74.2.6 >diff -u -r1.74.2.6 mod_ssl.c >--- mod_ssl.c 9 Feb 2004 20:53:20 -0000 1.74.2.6 >+++ mod_ssl.c 27 May 2004 13:12:28 -0000 >@@ -389,6 +389,7 @@ > static void ssl_register_hooks(apr_pool_t *p) > { > ssl_io_filter_register(p); >+ ssl_reneg_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); >Index: mod_ssl.h >=================================================================== >RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.h,v >retrieving revision 1.122.2.9 >diff -u -r1.122.2.9 mod_ssl.h >--- mod_ssl.h 9 Feb 2004 20:53:20 -0000 1.122.2.9 >+++ mod_ssl.h 27 May 2004 13:12:28 -0000 >@@ -675,6 +675,7 @@ > void ssl_io_filter_init(conn_rec *, SSL *); > void ssl_io_filter_register(apr_pool_t *); > long ssl_io_data_cb(BIO *, int, MODSSL_BIO_CB_ARG_TYPE *, int, long, long); >+void ssl_reneg_filter_register(apr_pool_t *p); > > /* PRNG */ > int ssl_rand_seed(server_rec *, apr_pool_t *, ssl_rsctx_t, char *); >Index: ssl_engine_kernel.c >=================================================================== >RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_kernel.c,v >retrieving revision 1.82.2.12 >diff -u -r1.82.2.12 ssl_engine_kernel.c >--- ssl_engine_kernel.c 9 Feb 2004 20:53:20 -0000 1.82.2.12 >+++ ssl_engine_kernel.c 27 May 2004 13:12:29 -0000 >@@ -29,6 +29,8 @@ > -- Unknown */ > #include "mod_ssl.h" > >+static int reneg_and_check(request_rec *r, int quick); >+ > /* > * Post Read Request Handler > */ >@@ -159,6 +161,66 @@ > return DECLINED; > } > >+static ap_filter_rec_t *reneg_filter_rec; >+ >+/* Output filter which is inserted into the filter stack to perform a >+ * renegotiation after the HTTP filter returns EOS. */ >+static apr_status_t reneg_in_filter(ap_filter_t *f, >+ apr_bucket_brigade *bb, >+ ap_input_mode_t mode, >+ apr_read_type_e block, >+ apr_off_t bytes) >+{ >+ apr_bucket *bkt; >+ apr_status_t rv; >+ >+ rv = ap_get_brigade(f->next, bb, mode, block, bytes); >+ if (rv != APR_SUCCESS || mode != AP_MODE_READBYTES) { >+ return rv; >+ } >+ >+ for (bkt = APR_BRIGADE_FIRST(bb); >+ bkt != APR_BRIGADE_SENTINEL(bb); >+ bkt = APR_BUCKET_NEXT(bkt)) >+ { >+ if (APR_BUCKET_IS_EOS(bkt)) { >+ /* No more work for this filter. */ >+ ap_remove_input_filter(f); >+ >+ /* Now really do the negotiation and access control checks. */ >+ if (reneg_and_check(f->r, 0)) { >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, >+ "renegotiation failed; sending 403 error"); >+ bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc); >+ bkt = ap_bucket_error_create(HTTP_FORBIDDEN, NULL, >+ f->r->pool, f->c->bucket_alloc); >+ APR_BRIGADE_INSERT_TAIL(bb, bkt); >+ bkt = apr_bucket_eos_create(f->c->bucket_alloc); >+ APR_BRIGADE_INSERT_TAIL(bb, bkt); >+ >+ rv = ap_pass_brigade(f->r->output_filters, bb); >+ if (rv) >+ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, >+ "failed sending 403 after renegotiation"); >+ >+ return APR_EACCES; >+ } >+ >+ } >+ } >+ >+ return APR_SUCCESS; >+} >+ >+void ssl_reneg_filter_register(apr_pool_t *p) >+{ >+ /* only requirement is that this input filter comes before the >+ * ap_http_filter. */ >+ reneg_filter_rec = >+ ap_register_input_filter("SSL_RENEG", reneg_in_filter, NULL, >+ AP_FTYPE_PROTOCOL - 1); >+} >+ > /* > * Access Handler > */ >@@ -169,18 +231,12 @@ > SSLConnRec *sslconn = myConnConfig(r->connection); > SSL *ssl = sslconn ? sslconn->ssl : NULL; > SSL_CTX *ctx = NULL; >- apr_array_header_t *requires; >- ssl_require_t *ssl_requires; > char *cp; >- int ok, i; > BOOL renegotiate = FALSE, renegotiate_quick = FALSE; >- X509 *cert; > X509 *peercert; >- X509_STORE *cert_store = NULL; >- X509_STORE_CTX cert_store_ctx; > STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL; > SSL_CIPHER *cipher = NULL; >- int depth, verify_old, verify, n; >+ int verify_old, verify, n; > > if (ssl) { > ctx = SSL_get_SSL_CTX(ssl); >@@ -489,235 +545,215 @@ > } > #endif /* HAVE_SSL_SET_CERT_STORE */ > >- /* >- * SSL renegotiations in conjunction with HTTP >- * requests using the POST method are not supported. >- * >- * Background: >- * >- * 1. When the client sends a HTTP/HTTPS request, Apache's core code >- * reads only the request line ("METHOD /path HTTP/x.y") and the >- * attached MIME headers ("Foo: bar") up to the terminating line ("CR >- * LF"). An attached request body (for instance the data of a POST >- * method) is _NOT_ read. Instead it is read by mod_cgi's content >- * handler and directly passed to the CGI script. >- * >- * 2. mod_ssl supports per-directory re-configuration of SSL parameters. >- * This is implemented by performing an SSL renegotiation of the >- * re-configured parameters after the request is read, but before the >- * response is sent. In more detail: the renegotiation happens after the >- * request line and MIME headers were read, but _before_ the attached >- * request body is read. The reason simply is that in the HTTP protocol >- * usually there is no acknowledgment step between the headers and the >- * body (there is the 100-continue feature and the chunking facility >- * only), so Apache has no API hook for this step. >- * >- * 3. the problem now occurs when the client sends a POST request for >- * URL /foo via HTTPS the server and the server has SSL parameters >- * re-configured on a per-URL basis for /foo. Then mod_ssl has to >- * perform an SSL renegotiation after the request was read and before >- * the response is sent. But the problem is the pending POST body data >- * in the receive buffer of SSL (which Apache still has not read - it's >- * pending until mod_cgi sucks it in). When mod_ssl now tries to perform >- * the renegotiation the pending data leads to an I/O error. >- * >- * Solution Idea: >- * >- * There are only two solutions: Either to simply state that POST >- * requests to URLs with SSL re-configurations are not allowed, or to >- * renegotiate really after the _complete_ request (i.e. including >- * the POST body) was read. Obviously the latter would be preferred, >- * but it cannot be done easily inside Apache, because as already >- * mentioned, there is no API step between the body reading and the body >- * processing. And even when we mod_ssl would hook directly into the >- * loop of mod_cgi, we wouldn't solve the problem for other handlers, of >- * course. So the only general solution is to suck in the pending data >- * of the request body from the OpenSSL BIO into the Apache BUFF. Then >- * the renegotiation can be done and after this step Apache can proceed >- * processing the request as before. >- * >- * Solution Implementation: >- * >- * We cannot simply suck in the data via an SSL_read-based loop because of >- * HTTP chunking. Instead we _have_ to use the Apache API for this step which >- * is aware of HTTP chunking. So the trick is to suck in the pending request >- * data via the Apache API (which uses Apache's BUFF code and in the >- * background mod_ssl's I/O glue code) and re-inject it later into the Apache >- * BUFF code again. This way the data flows twice through the Apache BUFF, of >- * course. But this way the solution doesn't depend on any Apache specifics >- * and is fully transparent to Apache modules. >- * >- * !! BUT ALL THIS IS STILL NOT RE-IMPLEMENTED FOR APACHE 2.0 !! >- */ >- if (renegotiate && !renegotiate_quick && (r->method_number == M_POST)) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "SSL Re-negotiation in conjunction " >- "with POST method not supported!\n" >- "hint: try SSLOptions +OptRenegotiate"); >- >- return HTTP_METHOD_NOT_ALLOWED; >+ if (!renegotiate) { >+ /* Nothing more to do here. */ >+ return DECLINED; > } > >- /* >- * now do the renegotiation if anything was actually reconfigured >- */ >- if (renegotiate) { >- /* >- * Now we force the SSL renegotation by sending the Hello Request >- * message to the client. Here we have to do a workaround: Actually >- * OpenSSL returns immediately after sending the Hello Request (the >- * intent AFAIK is because the SSL/TLS protocol says it's not a must >- * that the client replies to a Hello Request). But because we insist >- * on a reply (anything else is an error for us) we have to go to the >- * ACCEPT state manually. Using SSL_set_accept_state() doesn't work >- * here because it resets too much of the connection. So we set the >- * state explicitly and continue the handshake manually. >- */ >+ /* This function is called after reading the request-header. If >+ * the HTTP request includes a message body, then the client may >+ * already have sent all the SSL records containing that body. >+ * It's not possible to do a renegotiation until all those SSL >+ * records have been read and processed, so the renegotiation has >+ * to be delayed until that point (when an EOS is returned by the >+ * HTTP input filter). */ >+ >+ if (!renegotiate_quick && >+ (apr_table_get(r->headers_in, "Transfer-Encoding") || >+ ((cp = (char *)apr_table_get(r->headers_in, "Content-Length")) != NULL >+ && strcmp(cp, "0")))) { > ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, >- "Requesting connection re-negotiation"); >- >- if (renegotiate_quick) { >- STACK_OF(X509) *cert_stack; >- >- /* perform just a manual re-verification of the peer */ >- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, >- "Performing quick renegotiation: " >- "just re-verifying the peer"); >+ "SSL re-negotiation needed for request with body: " >+ "delaying until after body has been read"); >+ ap_add_input_filter_handle(reneg_filter_rec, NULL, r, r->connection); > >- cert_stack = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl); >- >- cert = SSL_get_peer_certificate(ssl); >- >- if (!cert_stack && cert) { >- /* client cert is in the session cache, but there is >- * no chain, since ssl3_get_client_certificate() >- * sk_X509_shift-ed the peer cert out of the chain. >- * we put it back here for the purpose of quick_renegotiation. >- */ >- cert_stack = sk_new_null(); >- sk_X509_push(cert_stack, MODSSL_PCHAR_CAST cert); >- } >- >- if (!cert_stack || (sk_X509_num(cert_stack) == 0)) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "Cannot find peer certificate chain"); >- >- return HTTP_FORBIDDEN; >- } >- >- if (!(cert_store || >- (cert_store = SSL_CTX_get_cert_store(ctx)))) >- { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "Cannot find certificate storage"); >- >- return HTTP_FORBIDDEN; >- } >- >- if (!cert) { >- cert = sk_X509_value(cert_stack, 0); >- } >- >- X509_STORE_CTX_init(&cert_store_ctx, cert_store, cert, cert_stack); >- depth = SSL_get_verify_depth(ssl); >- >- if (depth >= 0) { >- X509_STORE_CTX_set_depth(&cert_store_ctx, depth); >- } >- >- X509_STORE_CTX_set_ex_data(&cert_store_ctx, >- SSL_get_ex_data_X509_STORE_CTX_idx(), >- (char *)ssl); >+ return DECLINED; >+ } > >- if (!modssl_X509_verify_cert(&cert_store_ctx)) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "Re-negotiation verification step failed"); >- ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, r->server); >- } >+ /* Now actually perform the SSL renegotiation; return DECLINED >+ * here instead of OK, to allow mod_auth and other to deny >+ * access. */ >+ return (reneg_and_check(r, renegotiate_quick) >+ ? HTTP_FORBIDDEN : DECLINED); >+} > >- SSL_set_verify_result(ssl, cert_store_ctx.error); >- X509_STORE_CTX_cleanup(&cert_store_ctx); >+/* Do the SSL renegotiation and perform access control checks. >+ * Returns non-zero if access control checks failed. */ >+static int reneg_and_check(request_rec *r, int quick) >+{ >+ SSLDirConfigRec *dc = myDirConfig(r); >+ SSLConnRec *sslconn = myConnConfig(r->connection); >+ SSL *ssl = sslconn ? sslconn->ssl : NULL; >+ apr_array_header_t *requires; >+ ssl_require_t *ssl_requires; >+ X509_STORE *cert_store = NULL; >+ X509_STORE_CTX cert_store_ctx; >+ X509 *cert; >+ SSL_CTX *ctx = SSL_get_SSL_CTX(ssl); >+ int depth, i, ok; >+ char *cp; > >- if (cert_stack != SSL_get_peer_cert_chain(ssl)) { >- /* we created this ourselves, so free it */ >- sk_X509_pop_free(cert_stack, X509_free); >- } >+ /* >+ * Now we force the SSL renegotation by sending the Hello Request >+ * message to the client. Here we have to do a workaround: Actually >+ * OpenSSL returns immediately after sending the Hello Request (the >+ * intent AFAIK is because the SSL/TLS protocol says it's not a must >+ * that the client replies to a Hello Request). But because we insist >+ * on a reply (anything else is an error for us) we have to go to the >+ * ACCEPT state manually. Using SSL_set_accept_state() doesn't work >+ * here because it resets too much of the connection. So we set the >+ * state explicitly and continue the handshake manually. >+ */ >+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, >+ "Requesting connection re-negotiation"); >+ >+ if (quick) { >+ STACK_OF(X509) *cert_stack; >+ >+ /* perform just a manual re-verification of the peer */ >+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, >+ "Performing quick renegotiation: " >+ "just re-verifying the peer"); >+ >+ cert_stack = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl); >+ >+ cert = SSL_get_peer_certificate(ssl); >+ >+ if (!cert_stack && cert) { >+ /* client cert is in the session cache, but there is >+ * no chain, since ssl3_get_client_certificate() >+ * sk_X509_shift-ed the peer cert out of the chain. >+ * we put it back here for the purpose of quick_renegotiation. >+ */ >+ cert_stack = sk_new_null(); >+ sk_X509_push(cert_stack, MODSSL_PCHAR_CAST cert); > } >- else { >- request_rec *id = r->main ? r->main : r; >- >- /* do a full renegotiation */ >- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, >- "Performing full renegotiation: " >- "complete handshake protocol"); >- >- SSL_set_session_id_context(ssl, >- (unsigned char *)&id, >- sizeof(id)); >- >- SSL_renegotiate(ssl); >- SSL_do_handshake(ssl); >- >- if (SSL_get_state(ssl) != SSL_ST_OK) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "Re-negotiation request failed"); >- >- r->connection->aborted = 1; >- return HTTP_FORBIDDEN; >- } >- >- ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, >- "Awaiting re-negotiation handshake"); >- >- 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!?"); >- >- r->connection->aborted = 1; >- return HTTP_FORBIDDEN; >- } >+ >+ if (!cert_stack || (sk_X509_num(cert_stack) == 0)) { >+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >+ "Cannot find peer certificate chain"); >+ >+ return 1; > } >- >- /* >- * Remember the peer certificate's DN >- */ >- if ((cert = SSL_get_peer_certificate(ssl))) { >- if (sslconn->client_cert) { >- X509_free(sslconn->client_cert); >- } >- sslconn->client_cert = cert; >- sslconn->client_dn = NULL; >+ >+ if (!(cert_store || >+ (cert_store = SSL_CTX_get_cert_store(ctx)))) >+ { >+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >+ "Cannot find certificate storage"); >+ >+ return 1; > } >- >- /* >- * Finally check for acceptable renegotiation results >+ >+ if (!cert) { >+ cert = sk_X509_value(cert_stack, 0); >+ } >+ >+ X509_STORE_CTX_init(&cert_store_ctx, cert_store, cert, cert_stack); >+ depth = SSL_get_verify_depth(ssl); >+ >+ if (depth >= 0) { >+ X509_STORE_CTX_set_depth(&cert_store_ctx, depth); >+ } >+ >+ X509_STORE_CTX_set_ex_data(&cert_store_ctx, >+ SSL_get_ex_data_X509_STORE_CTX_idx(), >+ (char *)ssl); >+ >+ if (!modssl_X509_verify_cert(&cert_store_ctx)) { >+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >+ "Re-negotiation verification step failed"); >+ ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, r->server); >+ } >+ >+ SSL_set_verify_result(ssl, cert_store_ctx.error); >+ X509_STORE_CTX_cleanup(&cert_store_ctx); >+ >+ if (cert_stack != SSL_get_peer_cert_chain(ssl)) { >+ /* we created this ourselves, so free it */ >+ sk_X509_pop_free(cert_stack, X509_free); >+ } >+ } >+ else { >+ request_rec *id = r->main ? r->main : r; >+ >+ /* do a full renegotiation */ >+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, >+ "Performing full renegotiation: " >+ "complete handshake protocol"); >+ >+ SSL_set_session_id_context(ssl, >+ (unsigned char *)&id, >+ sizeof(id)); >+ >+ SSL_renegotiate(ssl); >+ SSL_do_handshake(ssl); >+ >+ if (SSL_get_state(ssl) != SSL_ST_OK) { >+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >+ "Re-negotiation request failed"); >+ >+ r->connection->aborted = 1; >+ return 1; >+ } >+ >+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, >+ "Awaiting re-negotiation handshake"); >+ >+ /* XXX: Should replace SSL_set_state with SSL_renegotiate(ssl); >+ * However, this causes failures in perl-framework currently, >+ * perhaps pre-test if we have already negotiated? > */ >- if (dc->nVerifyClient != SSL_CVERIFY_NONE) { >- BOOL do_verify = (dc->nVerifyClient == SSL_CVERIFY_REQUIRE); >+ 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!?"); >+ >+ r->connection->aborted = 1; >+ return 1; >+ } >+ } >+ >+ /* >+ * Remember the peer certificate's DN >+ */ >+ if ((cert = SSL_get_peer_certificate(ssl))) { >+ if (sslconn->client_cert) { >+ X509_free(sslconn->client_cert); >+ } >+ sslconn->client_cert = cert; >+ sslconn->client_dn = NULL; >+ } >+ >+ /* >+ * Finally check for acceptable renegotiation results >+ */ >+ if (dc->nVerifyClient != SSL_CVERIFY_NONE) { >+ BOOL do_verify = (dc->nVerifyClient == SSL_CVERIFY_REQUIRE); >+ >+ if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) { >+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >+ "Re-negotiation handshake failed: " >+ "Client verification failed"); >+ >+ return 1; >+ } >+ >+ if (do_verify) { >+ X509 *peercert; > >- if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) { >+ if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) { > ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, > "Re-negotiation handshake failed: " >- "Client verification failed"); >- >- return HTTP_FORBIDDEN; >- } >- >- if (do_verify) { >- if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) { >- ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, >- "Re-negotiation handshake failed: " >- "Client certificate missing"); >- >- return HTTP_FORBIDDEN; >- } >- >- X509_free(peercert); >+ "Client certificate missing"); >+ >+ return 1; > } >+ >+ X509_free(peercert); > } > } > >@@ -744,7 +780,7 @@ > /* remember forbidden access for strict require option */ > apr_table_setn(r->notes, "ssl-access-forbidden", "1"); > >- return HTTP_FORBIDDEN; >+ return 1; > } > > if (ok != 1) { >@@ -765,18 +801,11 @@ > /* remember forbidden access for strict require option */ > apr_table_setn(r->notes, "ssl-access-forbidden", "1"); > >- return HTTP_FORBIDDEN; >+ return 1; > } > } > >- /* >- * Else access is granted from our point of view (except vendor >- * handlers override). But we have to return DECLINED here instead >- * of OK, because mod_auth and other modules still might want to >- * deny access. >- */ >- >- return DECLINED; >+ return 0; > } > > /*
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 12355
:
9626
|
11681
|
11736
|
11745
|
16491
|
16495