You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by je...@apache.org on 2001/10/11 03:49:21 UTC
cvs commit: httpd-2.0/modules/ssl mod_ssl.c mod_ssl.h ssl_engine_io.c
jerenkrantz 01/10/10 18:49:21
Modified: . STATUS CHANGES
modules/ssl mod_ssl.c mod_ssl.h ssl_engine_io.c
Log:
This is the mod_ssl input filtering rewrite. Lots of stuff here. I also
changed some of the style issues within the filtering code to conform to
the rest of the server.
Various incarnations of this patch have been posted to dev@httpd without
feedback. Now that it passes all of the httpd-test cases (with the
exception of module/negotiation test which fails without mod_ssl anyway),
it is time to check it in.
Please review and test. We are under C-T-R rules, so I'm going to take
advantage of that and commit it now. I have tested this about as much
as I can and it seems to work from everything I can give to it.
Considering that mod_ssl was broken before this commit, this is an
improvement.
Revision Changes Path
1.304 +4 -7 httpd-2.0/STATUS
Index: STATUS
===================================================================
RCS file: /home/cvs/httpd-2.0/STATUS,v
retrieving revision 1.303
retrieving revision 1.304
diff -u -r1.303 -r1.304
--- STATUS 2001/10/10 19:43:37 1.303
+++ STATUS 2001/10/11 01:49:21 1.304
@@ -1,5 +1,5 @@
APACHE 2.0 STATUS: -*-text-*-
-Last modified at [$Date: 2001/10/10 19:43:37 $]
+Last modified at [$Date: 2001/10/11 01:49:21 $]
Release:
@@ -97,12 +97,9 @@
fix the bug where request body content will end up closing the
connection (buggering up persistent conns).
Status: Justin is working on this as fast as he can.
- The core input filters and HTTP-related filters are
- switched to the new logic. At this point, proxy and
- ssl remain broken. Preliminary patches for ssl have been
- posted to dev@httpd. There are lots of places that made
- implementation assumptions about the fact that the core
- filter would return the socket. That code is now broken.
+ The core input filters, HTTP-related filters, and mod_ssl
+ are switched to the new logic. At this point, proxy may be
+ broken (Ian says it works, but dechunking is a bit shaky).
- socket bucket and core input filter changes. see end of
message ID (Feb 27): <20...@lyra.org>
1.387 +3 -0 httpd-2.0/CHANGES
Index: CHANGES
===================================================================
RCS file: /home/cvs/httpd-2.0/CHANGES,v
retrieving revision 1.386
retrieving revision 1.387
diff -u -r1.386 -r1.387
--- CHANGES 2001/10/10 15:12:12 1.386
+++ CHANGES 2001/10/11 01:49:21 1.387
@@ -1,5 +1,8 @@
Changes with Apache 2.0.26-dev
+ *) rewrite mod_ssl input filtering to work with the new input filtering
+ system. [Justin Erenkrantz]
+
*) prefork: Don't segfault when we are able to listen on some but
not all of the configured ports. [Jeff Trawick]
1.31 +3 -37 httpd-2.0/modules/ssl/mod_ssl.c
Index: mod_ssl.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.c,v
retrieving revision 1.30
retrieving revision 1.31
diff -u -r1.30 -r1.31
--- mod_ssl.c 2001/08/30 05:33:57 1.30
+++ mod_ssl.c 2001/10/11 01:49:21 1.31
@@ -73,8 +73,6 @@
AP_INIT_##args("SSL"#name, ssl_cmd_SSL##name, NULL, OR_##type, desc),
#define AP_END_CMD { NULL }
-#define HTTP_ON_HTTPS_PORT "GET /mod_ssl:error:HTTP-request HTTP/1.0\r\n"
-
static const command_rec ssl_config_cmds[] = {
/*
@@ -363,47 +361,15 @@
* borrowed from openssl_state_machine.c [mod_tls].
* TBD.
*/
- return 0;
+ 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... At least for this error we can be more friendly
- * and try to provide him with a HTML error page. We have only
- * one problem:OpenSSL has already read some bytes from the HTTP
- * request. So we have to skip the request line manually and
- * instead provide a faked one in order to continue the internal
- * Apache processing.
- *
+ * Hmmmm... Punt this out of here after removing our output
+ * filter.
*/
- apr_bucket *e;
- const char *str;
- apr_size_t len;
- /* log the situation */
- ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
- "SSL handshake failed: HTTP spoken on HTTPS port; "
- "trying to send HTML error page");
-
- /* fake the request line */
- e = apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT,
- strlen(HTTP_ON_HTTPS_PORT));
- APR_BRIGADE_INSERT_HEAD(pRec->pbbPendingInput, e);
-
- APR_BRIGADE_FOREACH(e, pRec->pbbInput) {
- apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
- if (len) {
- APR_BUCKET_REMOVE(e);
- APR_BRIGADE_INSERT_TAIL(pRec->pbbPendingInput, e);
- if ((strcmp(str, "\r\n") == 0) ||
- (ap_strstr_c(str, "\r\n\r\n"))) {
- break;
- }
- }
- }
- e = APR_BRIGADE_LAST(pRec->pbbInput);
- APR_BUCKET_REMOVE(e);
-
ap_remove_output_filter(pRec->pOutputFilter);
return HTTP_BAD_REQUEST;
}
1.34 +2 -2 httpd-2.0/modules/ssl/mod_ssl.h
Index: mod_ssl.h
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/mod_ssl.h,v
retrieving revision 1.33
retrieving revision 1.34
diff -u -r1.33 -r1.34
--- mod_ssl.h 2001/09/10 04:21:40 1.33
+++ mod_ssl.h 2001/10/11 01:49:21 1.34
@@ -442,8 +442,8 @@
BIO *pbioWrite;
ap_filter_t *pInputFilter;
ap_filter_t *pOutputFilter;
- apr_bucket_brigade *pbbInput; /* encrypted input */
- apr_bucket_brigade *pbbPendingInput; /* decrypted input */
+ apr_bucket_brigade *rawb; /* encrypted input */
+ apr_bucket_brigade *b; /* decrypted input */
} SSLFilterRec;
typedef struct {
1.38 +246 -127 httpd-2.0/modules/ssl/ssl_engine_io.c
Index: ssl_engine_io.c
===================================================================
RCS file: /home/cvs/httpd-2.0/modules/ssl/ssl_engine_io.c,v
retrieving revision 1.37
retrieving revision 1.38
diff -u -r1.37 -r1.38
--- ssl_engine_io.c 2001/10/04 17:50:39 1.37
+++ ssl_engine_io.c 2001/10/11 01:49:21 1.38
@@ -72,7 +72,7 @@
static const char ssl_io_filter[] = "SSL/TLS Filter";
-static int ssl_io_hook_read(SSL *ssl, unsigned char *buf, int len)
+static int ssl_io_hook_read(SSL *ssl, char *buf, int len)
{
int rc;
@@ -189,75 +189,67 @@
#define bio_is_renegotiating(bio) \
(((int)BIO_get_callback_arg(bio)) == SSL_ST_RENEGOTIATE)
+#define HTTP_ON_HTTPS_PORT "GET /mod_ssl:error:HTTP-request HTTP/1.0\r\n"
-static apr_status_t churn_input(SSLFilterRec *pRec,
- ap_input_mode_t eMode, apr_off_t *readbytes)
+static apr_status_t churn_input(SSLFilterRec *pRec, ap_input_mode_t eMode,
+ apr_off_t *readbytes)
{
- conn_rec *c = pRec->pInputFilter->c;
+ ap_filter_t *f = pRec->pInputFilter;
+ SSLFilterRec *ctx = pRec;
+ conn_rec *c = f->c;
apr_pool_t *p = c->pool;
- apr_bucket *pbktIn;
+ apr_bucket *e;
+ int found_eos = 0, n;
+ char buf[1024];
+ apr_status_t rv;
- if(APR_BRIGADE_EMPTY(pRec->pbbInput)) {
- ap_get_brigade(pRec->pInputFilter->next,pRec->pbbInput,eMode,readbytes);
- if(APR_BRIGADE_EMPTY(pRec->pbbInput))
- return APR_EOF;
- }
-
- APR_BRIGADE_FOREACH(pbktIn,pRec->pbbInput) {
- const char *data;
- apr_size_t len;
- int n;
- char buf[1024];
- apr_status_t ret;
-
- if (APR_BUCKET_IS_EOS(pbktIn)) {
- break;
- }
-
- /* read filter */
- ret = apr_bucket_read(pbktIn, &data, &len, (apr_read_type_e)eMode);
-
- if (!(eMode == AP_MODE_NONBLOCKING && APR_STATUS_IS_EAGAIN(ret))) {
- /* allow retry */
- APR_BUCKET_REMOVE(pbktIn);
- }
-
- if (ret == APR_SUCCESS && len == 0 && eMode == AP_MODE_BLOCKING)
- ret = APR_EOF;
-
- if (len == 0) {
- /* Lazy frickin browsers just reset instead of shutting down. */
- /* also gotta handle timeout of keepalive connections */
- if (ret == APR_EOF || APR_STATUS_IS_ECONNRESET(ret) ||
- ret == APR_TIMEUP)
- {
- if (APR_BRIGADE_EMPTY(pRec->pbbPendingInput))
- return APR_EOF;
- else
- /* Next time around, the incoming brigade will be empty,
- * so we'll return EOF then
- */
- return APR_SUCCESS;
- }
-
- if (eMode != AP_MODE_NONBLOCKING)
- ap_log_error(APLOG_MARK,APLOG_ERR,ret,NULL,
- "Read failed in ssl input filter");
-
- if ((eMode == AP_MODE_NONBLOCKING) &&
- (APR_STATUS_IS_SUCCESS(ret) || APR_STATUS_IS_EAGAIN(ret)))
- {
- /* In this case, we have data in the output bucket, or we were
- * non-blocking, so returning nothing is fine.
- */
- return APR_SUCCESS;
- }
- else {
- return ret;
+ /* Flush the output buffers. */
+ churn_output(pRec);
+
+ /* We have something in the processed brigade. Use that first. */
+ if (!APR_BRIGADE_EMPTY(ctx->b)) {
+ return APR_SUCCESS;
+ }
+
+ /* If we have nothing in the raw brigade, get some more. */
+ if (APR_BRIGADE_EMPTY(ctx->rawb)) {
+ rv = ap_get_brigade(f->next, ctx->rawb, eMode, readbytes);
+
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ /* Can't make any progress here. */
+ if (*readbytes == 0)
+ {
+ /* This means that we have nothing else to read ever. */
+ if (eMode == AP_MODE_BLOCKING) {
+ APR_BRIGADE_INSERT_TAIL(ctx->b, apr_bucket_eos_create());
}
- }
+ return APR_SUCCESS;
+ }
+ }
+
+ /* Process anything we have that we haven't done so already. */
+ while (!APR_BRIGADE_EMPTY(ctx->rawb)) {
+ const char *data;
+ apr_size_t len;
+
+ e = APR_BRIGADE_FIRST(ctx->rawb);
+
+ if (APR_BUCKET_IS_EOS(e)) {
+ apr_bucket_delete(e);
+ found_eos = 1;
+ break;
+ }
+
+ /* read from the bucket */
+ rv = apr_bucket_read(e, &data, &len, eMode);
+
+ if (rv != APR_SUCCESS)
+ return rv;
- n = BIO_write (pRec->pbioRead, data, len);
+ /* Write it to our BIO */
+ n = BIO_write(pRec->pbioRead, data, len);
if ((apr_size_t)n != len) {
/* this should never really happen, since we're just writing
@@ -270,48 +262,81 @@
return APR_ENOMEM;
}
- if (bio_is_renegotiating(pRec->pbioRead)) {
- /* we're doing renegotiation in the access phase */
- if (len >= *readbytes) {
- /* trick ap_http_filter into leaving us alone */
- *readbytes = 0;
- break; /* SSL_renegotiate will take it from here */
- }
- }
+ /* If we reached here, we read the bucket successfully, so toss
+ * it from the raw brigade. */
+ apr_bucket_delete(e);
- if ((ret = ssl_hook_process_connection(pRec)) != APR_SUCCESS) {
- /* if this is the case, ssl connection has been shutdown
- * and pRec->pssl has been freed
- */
- if (ret == HTTP_BAD_REQUEST) {
- return APR_SUCCESS;
- }
- return ret;
+ }
+
+ /* Flush the output buffers. */
+ churn_output(pRec);
+
+ /* Before we actually read any unencrypted data, go ahead and
+ * let ssl_hook_process_connection have a shot at it.
+ */
+ rv = ssl_hook_process_connection(pRec);
+
+ /* Flush again. */
+ churn_output(pRec);
+
+ if (rv != APR_SUCCESS) {
+ /* if process connection says HTTP_BAD_REQUEST, we've seen a
+ * HTTP on HTTPS error.
+ *
+ * The case where OpenSSL has recognized a HTTP request:
+ * This means the client speaks plain HTTP on our HTTPS port.
+ * Hmmmm... At least for this error we can be more friendly
+ * and try to provide him with a HTML error page. We have only
+ * one problem:OpenSSL has already read some bytes from the HTTP
+ * request. So we have to skip the request line manually and
+ * instead provide a faked one in order to continue the internal
+ * Apache processing.
+ *
+ */
+ if (rv == HTTP_BAD_REQUEST) {
+ /* log the situation */
+ ssl_log(c->base_server, SSL_LOG_ERROR|SSL_ADD_SSLERR,
+ "SSL handshake failed: HTTP spoken on HTTPS port; "
+ "trying to send HTML error page");
+
+ /* fake the request line */
+ e = apr_bucket_immortal_create(HTTP_ON_HTTPS_PORT,
+ sizeof(HTTP_ON_HTTPS_PORT) - 1);
+ APR_BRIGADE_INSERT_TAIL(ctx->b, e);
+ e = apr_bucket_immortal_create(CRLF, sizeof(CRLF) - 1);
+ APR_BRIGADE_INSERT_TAIL(ctx->b, e);
+
+ return APR_SUCCESS;
+ }
+ if (rv == SSL_ERROR_WANT_READ) {
+ apr_off_t tempread = 1024;
+ return churn_input(pRec, eMode, &tempread);
}
+ return rv;
+ }
- /* pass along all of the current BIO */
- while ((n = ssl_io_hook_read(pRec->pssl,
- (unsigned char *)buf, sizeof(buf))) > 0)
- {
- apr_bucket *pbktOut;
- char *pbuf;
+ /* try to pass along all of the current BIO to ctx->b */
+ /* FIXME: If there's an error and there was EOS, we may not really
+ * reach EOS.
+ */
+ while ((n = ssl_io_hook_read(pRec->pssl, buf, sizeof(buf))) > 0) {
+ char *pbuf;
+
+ pbuf = apr_pmemdup(p, buf, n);
+ e = apr_bucket_pool_create(pbuf, n, p);
+ APR_BRIGADE_INSERT_TAIL(ctx->b, e);
+
+ /* Flush the output buffers. */
+ churn_output(pRec);
+ }
+
+ if (n < 0 && errno == EINTR && APR_BRIGADE_EMPTY(ctx->b)) {
+ apr_off_t tempread = 1024;
+ return churn_input(pRec, eMode, &tempread);
+ }
- pbuf = apr_pmemdup(p, buf, n);
- /* XXX: should we use a heap bucket instead? Or a transient (in
- * which case we need a separate brigade for each bucket)?
- */
- pbktOut = apr_bucket_pool_create(pbuf, n, p);
- APR_BRIGADE_INSERT_TAIL(pRec->pbbPendingInput,pbktOut);
-
- /* Once we've read something, we can move to non-blocking mode (if
- * we weren't already).
- */
- eMode = AP_MODE_NONBLOCKING;
- }
-
- ret=churn_output(pRec);
- if(ret != APR_SUCCESS)
- return ret;
+ if (found_eos) {
+ APR_BRIGADE_INSERT_TAIL(ctx->b, apr_bucket_eos_create());
}
return churn_output(pRec);
@@ -324,19 +349,36 @@
apr_bucket *bucket;
apr_status_t ret = APR_SUCCESS;
- APR_BRIGADE_FOREACH(bucket, bb) {
+ while (!APR_BRIGADE_EMPTY(bb)) {
const char *data;
apr_size_t len, n;
- if (APR_BUCKET_IS_EOS(bucket)) {
+ bucket = APR_BRIGADE_FIRST(bb);
+
+ /* If it is a flush or EOS, we need to pass this down.
+ * These types do not require translation by OpenSSL.
+ */
+ if (APR_BUCKET_IS_EOS(bucket) || APR_BUCKET_IS_FLUSH(bucket)) {
+ apr_bucket_brigade *outbb;
+
if ((ret = churn_output(ctx)) != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL,
- "Error in churn_output");
+ return ret;
}
- break;
- }
+
+ outbb = apr_brigade_create(f->c->pool);
+ APR_BUCKET_REMOVE(bucket);
+ APR_BRIGADE_INSERT_TAIL(outbb, bucket);
+ ret = ap_pass_brigade(f->next, outbb);
+ if (ret != APR_SUCCESS) {
+ return ret;
+ }
- if (!APR_BUCKET_IS_FLUSH(bucket)) {
+ /* By definition, nothing can come after EOS. */
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ break;
+ }
+ }
+ else {
/* read filter */
apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
@@ -359,15 +401,14 @@
ret = APR_EINVAL;
break;
}
- }
- /* else fallthrough to flush the current wbio
- * likely triggered by renegotiation in ssl_hook_Access
- */
- /* churn the state machine */
- if ((ret = churn_output(ctx)) != APR_SUCCESS) {
- break;
+ /* churn the state machine */
+ if ((ret = churn_output(ctx)) != APR_SUCCESS) {
+ break;
+ }
}
+
+ apr_bucket_delete(bucket);
}
apr_brigade_destroy(bb);
@@ -376,23 +417,101 @@
static apr_status_t ssl_io_filter_Input(ap_filter_t *f,
apr_bucket_brigade *pbbOut,
- ap_input_mode_t eMode,
+ ap_input_mode_t mode,
apr_off_t *readbytes)
{
apr_status_t ret;
- SSLFilterRec *pRec = f->ctx;
+ SSLFilterRec *ctx = f->ctx;
+ apr_status_t rv;
+ apr_bucket *e;
+ apr_off_t tempread;
- /* XXX: we don't currently support peek */
- if (eMode == AP_MODE_PEEK) {
+ /* XXX: we don't currently support peek or readbytes == -1 */
+ if (mode == AP_MODE_PEEK || *readbytes == -1) {
return APR_ENOTIMPL;
}
+
+ /* Return the requested amount or less. */
+ if (*readbytes)
+ {
+ apr_bucket_brigade *newbb;
+
+ /* churn the state machine */
+ ret = churn_input(ctx, mode, readbytes);
+
+ if (ret != APR_SUCCESS)
+ return ret;
+
+ /* ### This is bad. */
+ APR_BRIGADE_NORMALIZE(ctx->b);
+
+ apr_brigade_length(ctx->b, 0, &tempread);
- /* churn the state machine */
- ret = churn_input(pRec, eMode, readbytes);
- if (ret != APR_SUCCESS)
- return ret;
+ if (*readbytes < tempread) {
+ tempread = *readbytes;
+ }
+ else {
+ *readbytes = tempread;
+ }
+
+ apr_brigade_partition(ctx->b, tempread, &e);
+ newbb = apr_brigade_split(ctx->b, e);
+ APR_BRIGADE_CONCAT(pbbOut, ctx->b);
+ APR_BRIGADE_CONCAT(ctx->b, newbb);
- APR_BRIGADE_CONCAT(pbbOut, pRec->pbbPendingInput);
+ return APR_SUCCESS;
+ }
+
+ /* Readbytes == 0 implies we only want a LF line.
+ * 1024 seems like a good number for now. */
+ if (APR_BRIGADE_EMPTY(ctx->b)) {
+ tempread = 1024;
+ rv = churn_input(ctx, mode, &tempread);
+ if (rv != APR_SUCCESS)
+ return rv;
+ /* We have already blocked. */
+ mode = AP_MODE_NONBLOCKING;
+ }
+ while (!APR_BRIGADE_EMPTY(ctx->b)) {
+ const char *pos, *str;
+ apr_size_t len;
+
+ e = APR_BRIGADE_FIRST(ctx->b);
+
+ /* Sure, we'll call this is a line. Whatever. */
+ if (APR_BUCKET_IS_EOS(e)) {
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(pbbOut, e);
+ break;
+ }
+
+ if ((rv = apr_bucket_read(e, &str, &len,
+ AP_MODE_NONBLOCKING)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ pos = memchr(str, APR_ASCII_LF, len);
+ /* We found a match. */
+ if (pos != NULL) {
+ apr_bucket_split(e, pos - str + 1);
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(pbbOut, e);
+ *readbytes += pos - str;
+ return APR_SUCCESS;
+ }
+ APR_BUCKET_REMOVE(e);
+ APR_BRIGADE_INSERT_TAIL(pbbOut, e);
+ *readbytes += len;
+
+ /* Hey, we're about to be starved - go fetch more data. */
+ if (APR_BRIGADE_EMPTY(ctx->b)) {
+ tempread = 1024;
+ ret = churn_input(ctx, mode, &tempread);
+ if (ret != APR_SUCCESS)
+ return ret;
+ mode = AP_MODE_NONBLOCKING;
+ }
+ }
return APR_SUCCESS;
}
@@ -423,8 +542,8 @@
filter = apr_pcalloc(c->pool, sizeof(SSLFilterRec));
filter->pInputFilter = ap_add_input_filter(ssl_io_filter, filter, NULL, c);
filter->pOutputFilter = ap_add_output_filter(ssl_io_filter, filter, NULL, c);
- filter->pbbInput = apr_brigade_create(c->pool);
- filter->pbbPendingInput = apr_brigade_create(c->pool);
+ filter->b = apr_brigade_create(c->pool);
+ filter->rawb = apr_brigade_create(c->pool);
filter->pbioRead = BIO_new(BIO_s_mem());
filter->pbioWrite = BIO_new(BIO_s_mem());
SSL_set_bio(ssl, filter->pbioRead, filter->pbioWrite);