ctx->b = apr_brigade_split(b, e);
}
else {
- ctx->b = NULL;
+ if (!APR_BRIGADE_EMPTY(ctx->b)) {
+ ctx->b = NULL; /*XXX*/
+ }
}
apr_brigade_length(b, 1, &total);
*readbytes -= total;
ap_hook_default_port (ssl_hook_default_port, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_handler (ssl_hook_Handler, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_pre_config (ssl_hook_pre_config, NULL,NULL, APR_HOOK_MIDDLE);
- ap_hook_fixups (ssl_hook_Fixup, NULL,NULL, APR_HOOK_MIDDLE);
-
#if 0 /* XXX - Work in progress */
ap_hook_child_init (ssl_init_Child, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_process_connection (ssl_hook_process_connection,
NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_post_read_request (ssl_hook_post_read_request,
NULL,NULL, APR_HOOK_MIDDLE);
+#endif
ap_hook_translate_name(ssl_hook_Translate, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_check_user_id (ssl_hook_UserCheck, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_fixups (ssl_hook_Fixup, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_access_checker(ssl_hook_Access, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_auth_checker (ssl_hook_Auth, NULL,NULL, APR_HOOK_MIDDLE);
-
+#if 0 /* XXX - Work in progress */
ap_hook_open_logs (ssl_hook_open_logs, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_quick_handler (ssl_hook_quick_handler, NULL,NULL, APR_HOOK_MIDDLE);
ap_hook_log_transaction(ssl_hook_fixer_upper, NULL,NULL, APR_HOOK_MIDDLE);
return APR_SUCCESS;
}
+#define bio_is_renegotiating(bio) \
+(((int)BIO_get_callback_arg(bio)) == SSL_ST_RENEGOTIATE)
+
static apr_status_t churn (SSLFilterRec *pRec,
apr_read_type_e eReadType, apr_off_t *readbytes)
{
assert(n >= 0 && (apr_size_t)n == len);
+ 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 ((ret = ssl_hook_process_connection(pRec)) != APR_SUCCESS) {
/* if this is the case, ssl connection has been shutdown
* and pRec->pssl has been freed
*/
return ret;
}
-
+
/* pass along all of the current BIO */
while ((n = ssl_io_hook_read(pRec->pssl,
(unsigned char *)buf, sizeof(buf))) > 0)
APLOG_MARK,APLOG_ERR,ret,NULL, "Error in churn_output");
return ret;
}
-
- if ((ret = ssl_hook_CloseConnection (pRec)) != APR_SUCCESS)
- ap_log_error(APLOG_MARK,APLOG_ERR,ret,NULL,
- "Error in ssl_hook_CloseConnection");
break;
}
- if(APR_BUCKET_IS_FLUSH(pbktIn)) {
- continue;
- }
+ if (!APR_BUCKET_IS_FLUSH(pbktIn)) {
+ /* read filter */
+ apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ);
- /* read filter */
- apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ);
+ /* write SSL */
+ n = ssl_io_hook_write(pRec->pssl, (unsigned char *)data, len);
+
+ if (n != len) {
+ conn_rec *c = (conn_rec *)SSL_get_app_data(pRec->pssl);
+ char *reason = "reason unknown";
- /* write SSL */
- n = ssl_io_hook_write(pRec->pssl, (unsigned char *)data, len);
- assert (n == len);
+ /* XXX: probably a better way to determine this */
+ if (SSL_total_renegotiations(pRec->pssl)) {
+ reason = "likely due to failed renegotiation";
+ }
+
+ ssl_log(c->base_server, SSL_LOG_ERROR,
+ "failed to write %d of %d bytes (%s)",
+ n > 0 ? len - n : len, len, reason);
+
+ return APR_EINVAL;
+ }
+ }
+ /* else fallthrough to flush the current wbio
+ * likely triggered by renegotiation in ssl_hook_Access
+ */
/* churn the state machine */
ret=churn_output(pRec);
apr_status_t ssl_io_filter_cleanup (void *data)
{
- SSL *ssl = (SSL *)data;
- return APR_SUCCESS;
+ apr_status_t ret;
+ SSLFilterRec *pRec = (SSLFilterRec *)data;
+
+ if ((ret = ssl_hook_CloseConnection(pRec)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, ret, NULL,
+ "Error in ssl_hook_CloseConnection");
+ }
+
+ return ret;
}
void ssl_io_filter_init(conn_rec *c, SSL *ssl)
SSL_set_bio(ssl, filter->pbioRead, filter->pbioWrite);
filter->pssl = ssl;
- apr_pool_cleanup_register(c->pool, (void*)ssl,
+ apr_pool_cleanup_register(c->pool, (void*)filter,
ssl_io_filter_cleanup, apr_pool_cleanup_null);
return;
return HTTP_BAD_REQUEST;
}
+typedef long bio_hook_t (BIO *, int, const char *, int, long, long);
+
+static void bio_hook_set(BIO *b, bio_hook_t *hook, void *data)
+{
+ while (b) {
+ BIO_set_callback(b, hook);
+ BIO_set_callback_arg(b, data);
+ b = BIO_next(b);
+ }
+}
+
+/* XXX: save/restore current callbacks if any? */
+#define ssl_bio_hooks_set(ssl, hook, data) \
+ bio_hook_set(SSL_get_wbio(ssl), hook, data); \
+ bio_hook_set(SSL_get_rbio(ssl), hook, data)
+
+#define ssl_bio_renegotiate_hook_set(ssl, data) \
+ssl_bio_hooks_set(ssl, ssl_renegotiate_hook, data)
+
+#define ssl_bio_hooks_unset(ssl) \
+ssl_bio_hooks_set(ssl, NULL, NULL)
+
+#define bio_mem_length(b) ((BUF_MEM *)b->ptr)->length
+
+/* if we need to renegotiate in the access phase
+ * data needs to be pushed to / pulled from the filter chain
+ * otherwise, a BIO_write just sits in memory and theres nothing
+ * to BIO_read
+ */
+
+static long ssl_renegotiate_hook(BIO *bio, int cmd, const char *argp,
+ int argi, long argl, long rc)
+{
+ request_rec *r = (request_rec *)BIO_get_callback_arg(bio);
+ SSL *ssl;
+
+ int is_failed_read = (cmd == (BIO_CB_READ|BIO_CB_RETURN) && (rc == -1));
+ int is_flush = ((cmd == BIO_CB_CTRL) && (argi == BIO_CTRL_FLUSH));
+
+ if (is_flush || is_failed_read) {
+ ssl = (SSL *)apr_table_get(r->connection->notes, "ssl");
+ /* disable this callback to prevent recursion
+ * and leave a "note" so the input filter leaves the rbio
+ * as-as
+ */
+ ssl_bio_hooks_set(ssl, NULL, (void*)SSL_ST_RENEGOTIATE);
+ }
+ else {
+ return rc;
+ }
+
+ if (is_flush) {
+ /* flush what was written into wbio to the client */
+ ssl_log(r->server, SSL_LOG_DEBUG,
+ "flushing %d bytes to the client",
+ bio_mem_length(bio));
+ ap_rflush(r);
+ }
+ else {
+ /* force read from the client socket */
+ apr_bucket_brigade *bb = apr_brigade_create(r->connection->pool);
+ apr_off_t bytes = argi;
+ ap_get_brigade(r->input_filters, bb,
+ AP_MODE_BLOCKING, &bytes);
+
+ rc = BIO_read(bio, (void *)argp, argi);
+
+ ssl_log(r->server, SSL_LOG_DEBUG,
+ "retry read: wanted %d, got %d, %d remain\n",
+ argi, rc, bio_mem_length(bio));
+ }
+
+ /* reset this bio hook for further read/writes */
+ ssl_bio_renegotiate_hook_set(ssl, r);
+
+ return rc;
+}
+
/*
* Access Handler
*/
SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main));
else
SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));
+ /* will need to push to / pull from filters to renegotiate */
+ ssl_bio_renegotiate_hook_set(ssl, r);
SSL_renegotiate(ssl);
SSL_do_handshake(ssl);
+
if (SSL_get_state(ssl) != SSL_ST_OK) {
ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed");
+ ssl_bio_hooks_unset(ssl);
return HTTP_FORBIDDEN;
}
ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake");
SSL_set_state(ssl, SSL_ST_ACCEPT);
SSL_do_handshake(ssl);
+
+ ssl_bio_hooks_unset(ssl);
+
if (SSL_get_state(ssl) != SSL_ST_OK) {
ssl_log(r->server, SSL_LOG_ERROR,
"Re-negotiation handshake failed: Not accepted by client!?");