Loading CHANGES +3 −0 Original line number Diff line number Diff line Changes with Apache 2.0.12-dev *) Get mod_tls to the point where it actually appears to work in all cases. [Ben Laurie] *) implement --enable-modules and --enable-mods-shared for "all" and "most". [Greg Stein] Loading modules/tls/mod_tls.c +137 −75 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ #include "openssl_state_machine.h" #include "apr_strings.h" #include "http_protocol.h" #include "http_log.h" // temp #include <assert.h> Loading @@ -81,7 +82,8 @@ typedef struct SSLStateMachine *pStateMachine; ap_filter_t *pInputFilter; ap_filter_t *pOutputFilter; apr_bucket_brigade *pbbInput; apr_bucket_brigade *pbbInput; /* encrypted input */ apr_bucket_brigade *pbbPendingInput; /* decrypted input */ } TLSFilterCtx; static void *create_tls_server_config(apr_pool_t *p, server_rec *s) Loading Loading @@ -132,11 +134,12 @@ static int tls_filter_inserter(conn_rec *c) pCtx->pInputFilter=ap_add_input_filter(s_szTLSFilterName,pCtx,NULL,c); pCtx->pOutputFilter=ap_add_output_filter(s_szTLSFilterName,pCtx,NULL,c); pCtx->pbbInput=apr_brigade_create(c->pool); pCtx->pbbPendingInput=apr_brigade_create(c->pool); return OK; } static apr_status_t churn(TLSFilterCtx *pCtx) static apr_status_t churn_output(TLSFilterCtx *pCtx) { apr_bucket_brigade *pbbOutput=NULL; int done; Loading @@ -148,17 +151,26 @@ static apr_status_t churn(TLSFilterCtx *pCtx) done=0; n=SSLStateMachine_write_extract(pCtx->pStateMachine,buf,sizeof buf); if(SSLStateMachine_write_can_extract(pCtx->pStateMachine)) { n=SSLStateMachine_write_extract(pCtx->pStateMachine,buf, sizeof buf); if(n > 0) { char *pbuf; if(!pbbOutput) pbbOutput=apr_brigade_create(pCtx->pOutputFilter->c->pool); pbkt=apr_bucket_pool_create(buf,n,pCtx->pOutputFilter->c->pool); pbuf=apr_pmemdup(pCtx->pOutputFilter->c->pool,buf,n); pbkt=apr_bucket_pool_create(pbuf,n, pCtx->pOutputFilter->c->pool); APR_BRIGADE_INSERT_TAIL(pbbOutput,pbkt); done=1; /* } else if(n == 0) { apr_bucket *pbktEOS=apr_bucket_create_eos(); APR_BRIGADE_INSERT_TAIL(pbbOutput,pbktEOS);*/ } assert(n > 0); } } while(done); // XXX: check for errors Loading @@ -174,87 +186,59 @@ static apr_status_t churn(TLSFilterCtx *pCtx) return APR_SUCCESS; } static apr_status_t tls_out_filter(ap_filter_t *f,apr_bucket_brigade *pbbIn) static apr_status_t churn(TLSFilterCtx *pCtx,apr_read_type_e eReadType) { TLSFilterCtx *pCtx=f->ctx; ap_input_mode_t eMode=eReadType == APR_BLOCK_READ ? AP_MODE_BLOCKING : AP_MODE_NONBLOCKING; apr_bucket *pbktIn; int bFlush=0; apr_status_t ret; APR_BRIGADE_FOREACH(pbktIn,pbbIn) { const char *data; apr_size_t len; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: why can't I reuse pbktIn??? // XXX: isn't this wrong? // Write eof! break; } if(APR_BUCKET_IS_FLUSH(pbktIn)) { bFlush=1; continue; } // read filter apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ); // write SSL SSLStateMachine_write_inject(pCtx->pStateMachine,data,len); } // churn the state machine ret=churn(pCtx); if(bFlush) { apr_bucket_brigade *pbbOut; apr_bucket *pbktOut; pbbOut=apr_brigade_create(f->c->pool); pbktOut=apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); // XXX: and what if this returns an error??? ap_pass_brigade(f->next,pbbOut); } return ret; } static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, ap_input_mode_t eMode) { TLSFilterCtx *pCtx=f->ctx; apr_bucket *pbktIn; apr_read_type_e eReadType=eMode == AP_MODE_BLOCKING ? APR_BLOCK_READ : APR_NONBLOCK_READ; // XXX: we don't currently support peek assert(eMode != AP_MODE_PEEK); if(APR_BRIGADE_EMPTY(pCtx->pbbInput)) { ap_get_brigade(pCtx->pInputFilter->next,pCtx->pbbInput,eMode); if(APR_BRIGADE_EMPTY(pCtx->pbbInput)) ap_get_brigade(f->next,pCtx->pbbInput,eMode); return APR_EOF; } APR_BRIGADE_FOREACH(pbktIn,pCtx->pbbInput) { const char *data; apr_size_t len; int n; char buf[1024]; apr_status_t ret; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: why can't I reuse pbktIn??? // XX: isn't this wrong? // Write eof! break; } // read filter apr_bucket_read(pbktIn,&data,&len,eReadType); ret=apr_bucket_read(pbktIn,&data,&len,eReadType); APR_BUCKET_REMOVE(pbktIn); if(ret == APR_SUCCESS && len == 0 && eReadType == APR_BLOCK_READ) ret=APR_EOF; // presumably this can only happen when we are non-blocking if(len == 0) { // Lazy frickin browsers just reset instead of shutting down. if(ret == APR_EOF || ret == APR_ECONNRESET) if(APR_BRIGADE_EMPTY(pCtx->pbbPendingInput)) return APR_EOF; else /* Next time around, the incoming brigade will be empty, * so we'll return EOF then */ return APR_SUCCESS; if(eReadType != APR_NONBLOCK_READ) ap_log_error(APLOG_MARK,APLOG_ERR,ret,NULL, "Read failed in tls_in_filter"); assert(eReadType == APR_NONBLOCK_READ); break; assert(ret == APR_SUCCESS || ret == APR_EAGAIN); /* In this case, we have data in the output bucket, or we were * non-blocking, so returning nothing is fine. */ return APR_SUCCESS; } assert(len > 0); Loading @@ -271,7 +255,7 @@ static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, // 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,pCtx->pInputFilter->c->pool); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); APR_BRIGADE_INSERT_TAIL(pCtx->pbbPendingInput,pbktOut); // Once we've read something, we can move to non-blocking mode (if // we weren't already). Loading @@ -284,9 +268,87 @@ static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, } assert(n >= 0); ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; } return churn_output(pCtx); } static apr_status_t tls_out_filter(ap_filter_t *f,apr_bucket_brigade *pbbIn) { TLSFilterCtx *pCtx=f->ctx; apr_bucket *pbktIn; APR_BRIGADE_FOREACH(pbktIn,pbbIn) { const char *data; apr_size_t len; apr_status_t ret; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: demote to debug ap_log_error(APLOG_MARK,APLOG_ERR,0,NULL,"Got EOS on output"); SSLStateMachine_write_close(pCtx->pStateMachine); // XXX: dubious - does this always terminate? Does it return the right thing? for( ; ; ) { ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; ret=churn(pCtx,APR_NONBLOCK_READ); if(ret != APR_SUCCESS) if(ret == APR_EOF) return APR_SUCCESS; else return ret; } break; } if(APR_BUCKET_IS_FLUSH(pbktIn)) { // assume that churn will flush (or already has) if there's output ret=churn(pCtx,APR_NONBLOCK_READ); if(ret != APR_SUCCESS) return ret; continue; } // read filter apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ); // write SSL SSLStateMachine_write_inject(pCtx->pStateMachine,data,len); // churn the state machine // XXX: check for errors churn(pCtx); ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; } return APR_SUCCESS; } static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, ap_input_mode_t eMode) { TLSFilterCtx *pCtx=f->ctx; apr_read_type_e eReadType=eMode == AP_MODE_BLOCKING ? APR_BLOCK_READ : APR_NONBLOCK_READ; apr_status_t ret; // XXX: we don't currently support peek assert(eMode != AP_MODE_PEEK); // churn the state machine ret=churn(pCtx,eReadType); if(ret != APR_SUCCESS) return ret; // XXX: shame that APR_BRIGADE_FOREACH doesn't work here while(!APR_BRIGADE_EMPTY(pCtx->pbbPendingInput)) { apr_bucket *pbktIn=APR_BRIGADE_FIRST(pCtx->pbbPendingInput); APR_BUCKET_REMOVE(pbktIn); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktIn); } return APR_SUCCESS; Loading modules/tls/openssl_state_machine.c +15 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,16 @@ void SSLStateMachine_write_inject(SSLStateMachine *pMachine, const unsigned char *aucBuf,int nBuf) { int n=SSL_write(pMachine->pSSL,aucBuf,nBuf); if(n < 0) { if(ERR_peek_error() == ERR_PACK(ERR_LIB_SSL,SSL_F_SSL_WRITE, SSL_R_PROTOCOL_IS_SHUTDOWN)) { SSLStateMachine_print_error(pMachine,"SSL_write error (someone wrote after shutdown)"); return; } SSLStateMachine_print_error(pMachine,"SSL_write error"); } /* If it turns out this assert fails, then buffer the data here * and just feed it in in churn instead. Seems to me that it * should be guaranteed to succeed, though. Loading @@ -247,3 +257,8 @@ void SSLStateMachine_write_inject(SSLStateMachine *pMachine, assert(n == nBuf); fprintf(stderr,"%d bytes of unencrypted data fed to state machine\n",n); } void SSLStateMachine_write_close(SSLStateMachine *pMachine) { SSL_shutdown(pMachine->pSSL); } modules/tls/openssl_state_machine.h +1 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,4 @@ int SSLStateMachine_write_extract(SSLStateMachine *pMachine, unsigned char *aucBuf,int nBuf); void SSLStateMachine_write_inject(SSLStateMachine *pMachine, const unsigned char *aucBuf,int nBuf); void SSLStateMachine_write_close(SSLStateMachine *pMachine); Loading
CHANGES +3 −0 Original line number Diff line number Diff line Changes with Apache 2.0.12-dev *) Get mod_tls to the point where it actually appears to work in all cases. [Ben Laurie] *) implement --enable-modules and --enable-mods-shared for "all" and "most". [Greg Stein] Loading
modules/tls/mod_tls.c +137 −75 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ #include "openssl_state_machine.h" #include "apr_strings.h" #include "http_protocol.h" #include "http_log.h" // temp #include <assert.h> Loading @@ -81,7 +82,8 @@ typedef struct SSLStateMachine *pStateMachine; ap_filter_t *pInputFilter; ap_filter_t *pOutputFilter; apr_bucket_brigade *pbbInput; apr_bucket_brigade *pbbInput; /* encrypted input */ apr_bucket_brigade *pbbPendingInput; /* decrypted input */ } TLSFilterCtx; static void *create_tls_server_config(apr_pool_t *p, server_rec *s) Loading Loading @@ -132,11 +134,12 @@ static int tls_filter_inserter(conn_rec *c) pCtx->pInputFilter=ap_add_input_filter(s_szTLSFilterName,pCtx,NULL,c); pCtx->pOutputFilter=ap_add_output_filter(s_szTLSFilterName,pCtx,NULL,c); pCtx->pbbInput=apr_brigade_create(c->pool); pCtx->pbbPendingInput=apr_brigade_create(c->pool); return OK; } static apr_status_t churn(TLSFilterCtx *pCtx) static apr_status_t churn_output(TLSFilterCtx *pCtx) { apr_bucket_brigade *pbbOutput=NULL; int done; Loading @@ -148,17 +151,26 @@ static apr_status_t churn(TLSFilterCtx *pCtx) done=0; n=SSLStateMachine_write_extract(pCtx->pStateMachine,buf,sizeof buf); if(SSLStateMachine_write_can_extract(pCtx->pStateMachine)) { n=SSLStateMachine_write_extract(pCtx->pStateMachine,buf, sizeof buf); if(n > 0) { char *pbuf; if(!pbbOutput) pbbOutput=apr_brigade_create(pCtx->pOutputFilter->c->pool); pbkt=apr_bucket_pool_create(buf,n,pCtx->pOutputFilter->c->pool); pbuf=apr_pmemdup(pCtx->pOutputFilter->c->pool,buf,n); pbkt=apr_bucket_pool_create(pbuf,n, pCtx->pOutputFilter->c->pool); APR_BRIGADE_INSERT_TAIL(pbbOutput,pbkt); done=1; /* } else if(n == 0) { apr_bucket *pbktEOS=apr_bucket_create_eos(); APR_BRIGADE_INSERT_TAIL(pbbOutput,pbktEOS);*/ } assert(n > 0); } } while(done); // XXX: check for errors Loading @@ -174,87 +186,59 @@ static apr_status_t churn(TLSFilterCtx *pCtx) return APR_SUCCESS; } static apr_status_t tls_out_filter(ap_filter_t *f,apr_bucket_brigade *pbbIn) static apr_status_t churn(TLSFilterCtx *pCtx,apr_read_type_e eReadType) { TLSFilterCtx *pCtx=f->ctx; ap_input_mode_t eMode=eReadType == APR_BLOCK_READ ? AP_MODE_BLOCKING : AP_MODE_NONBLOCKING; apr_bucket *pbktIn; int bFlush=0; apr_status_t ret; APR_BRIGADE_FOREACH(pbktIn,pbbIn) { const char *data; apr_size_t len; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: why can't I reuse pbktIn??? // XXX: isn't this wrong? // Write eof! break; } if(APR_BUCKET_IS_FLUSH(pbktIn)) { bFlush=1; continue; } // read filter apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ); // write SSL SSLStateMachine_write_inject(pCtx->pStateMachine,data,len); } // churn the state machine ret=churn(pCtx); if(bFlush) { apr_bucket_brigade *pbbOut; apr_bucket *pbktOut; pbbOut=apr_brigade_create(f->c->pool); pbktOut=apr_bucket_flush_create(); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); // XXX: and what if this returns an error??? ap_pass_brigade(f->next,pbbOut); } return ret; } static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, ap_input_mode_t eMode) { TLSFilterCtx *pCtx=f->ctx; apr_bucket *pbktIn; apr_read_type_e eReadType=eMode == AP_MODE_BLOCKING ? APR_BLOCK_READ : APR_NONBLOCK_READ; // XXX: we don't currently support peek assert(eMode != AP_MODE_PEEK); if(APR_BRIGADE_EMPTY(pCtx->pbbInput)) { ap_get_brigade(pCtx->pInputFilter->next,pCtx->pbbInput,eMode); if(APR_BRIGADE_EMPTY(pCtx->pbbInput)) ap_get_brigade(f->next,pCtx->pbbInput,eMode); return APR_EOF; } APR_BRIGADE_FOREACH(pbktIn,pCtx->pbbInput) { const char *data; apr_size_t len; int n; char buf[1024]; apr_status_t ret; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: why can't I reuse pbktIn??? // XX: isn't this wrong? // Write eof! break; } // read filter apr_bucket_read(pbktIn,&data,&len,eReadType); ret=apr_bucket_read(pbktIn,&data,&len,eReadType); APR_BUCKET_REMOVE(pbktIn); if(ret == APR_SUCCESS && len == 0 && eReadType == APR_BLOCK_READ) ret=APR_EOF; // presumably this can only happen when we are non-blocking if(len == 0) { // Lazy frickin browsers just reset instead of shutting down. if(ret == APR_EOF || ret == APR_ECONNRESET) if(APR_BRIGADE_EMPTY(pCtx->pbbPendingInput)) return APR_EOF; else /* Next time around, the incoming brigade will be empty, * so we'll return EOF then */ return APR_SUCCESS; if(eReadType != APR_NONBLOCK_READ) ap_log_error(APLOG_MARK,APLOG_ERR,ret,NULL, "Read failed in tls_in_filter"); assert(eReadType == APR_NONBLOCK_READ); break; assert(ret == APR_SUCCESS || ret == APR_EAGAIN); /* In this case, we have data in the output bucket, or we were * non-blocking, so returning nothing is fine. */ return APR_SUCCESS; } assert(len > 0); Loading @@ -271,7 +255,7 @@ static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, // 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,pCtx->pInputFilter->c->pool); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); APR_BRIGADE_INSERT_TAIL(pCtx->pbbPendingInput,pbktOut); // Once we've read something, we can move to non-blocking mode (if // we weren't already). Loading @@ -284,9 +268,87 @@ static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, } assert(n >= 0); ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; } return churn_output(pCtx); } static apr_status_t tls_out_filter(ap_filter_t *f,apr_bucket_brigade *pbbIn) { TLSFilterCtx *pCtx=f->ctx; apr_bucket *pbktIn; APR_BRIGADE_FOREACH(pbktIn,pbbIn) { const char *data; apr_size_t len; apr_status_t ret; if(APR_BUCKET_IS_EOS(pbktIn)) { // XXX: demote to debug ap_log_error(APLOG_MARK,APLOG_ERR,0,NULL,"Got EOS on output"); SSLStateMachine_write_close(pCtx->pStateMachine); // XXX: dubious - does this always terminate? Does it return the right thing? for( ; ; ) { ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; ret=churn(pCtx,APR_NONBLOCK_READ); if(ret != APR_SUCCESS) if(ret == APR_EOF) return APR_SUCCESS; else return ret; } break; } if(APR_BUCKET_IS_FLUSH(pbktIn)) { // assume that churn will flush (or already has) if there's output ret=churn(pCtx,APR_NONBLOCK_READ); if(ret != APR_SUCCESS) return ret; continue; } // read filter apr_bucket_read(pbktIn,&data,&len,APR_BLOCK_READ); // write SSL SSLStateMachine_write_inject(pCtx->pStateMachine,data,len); // churn the state machine // XXX: check for errors churn(pCtx); ret=churn_output(pCtx); if(ret != APR_SUCCESS) return ret; } return APR_SUCCESS; } static apr_status_t tls_in_filter(ap_filter_t *f,apr_bucket_brigade *pbbOut, ap_input_mode_t eMode) { TLSFilterCtx *pCtx=f->ctx; apr_read_type_e eReadType=eMode == AP_MODE_BLOCKING ? APR_BLOCK_READ : APR_NONBLOCK_READ; apr_status_t ret; // XXX: we don't currently support peek assert(eMode != AP_MODE_PEEK); // churn the state machine ret=churn(pCtx,eReadType); if(ret != APR_SUCCESS) return ret; // XXX: shame that APR_BRIGADE_FOREACH doesn't work here while(!APR_BRIGADE_EMPTY(pCtx->pbbPendingInput)) { apr_bucket *pbktIn=APR_BRIGADE_FIRST(pCtx->pbbPendingInput); APR_BUCKET_REMOVE(pbktIn); APR_BRIGADE_INSERT_TAIL(pbbOut,pbktIn); } return APR_SUCCESS; Loading
modules/tls/openssl_state_machine.c +15 −0 Original line number Diff line number Diff line Loading @@ -240,6 +240,16 @@ void SSLStateMachine_write_inject(SSLStateMachine *pMachine, const unsigned char *aucBuf,int nBuf) { int n=SSL_write(pMachine->pSSL,aucBuf,nBuf); if(n < 0) { if(ERR_peek_error() == ERR_PACK(ERR_LIB_SSL,SSL_F_SSL_WRITE, SSL_R_PROTOCOL_IS_SHUTDOWN)) { SSLStateMachine_print_error(pMachine,"SSL_write error (someone wrote after shutdown)"); return; } SSLStateMachine_print_error(pMachine,"SSL_write error"); } /* If it turns out this assert fails, then buffer the data here * and just feed it in in churn instead. Seems to me that it * should be guaranteed to succeed, though. Loading @@ -247,3 +257,8 @@ void SSLStateMachine_write_inject(SSLStateMachine *pMachine, assert(n == nBuf); fprintf(stderr,"%d bytes of unencrypted data fed to state machine\n",n); } void SSLStateMachine_write_close(SSLStateMachine *pMachine) { SSL_shutdown(pMachine->pSSL); }
modules/tls/openssl_state_machine.h +1 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,4 @@ int SSLStateMachine_write_extract(SSLStateMachine *pMachine, unsigned char *aucBuf,int nBuf); void SSLStateMachine_write_inject(SSLStateMachine *pMachine, const unsigned char *aucBuf,int nBuf); void SSLStateMachine_write_close(SSLStateMachine *pMachine);