]> granicus.if.org Git - apache/commitdiff
Working SSL/TLS! Yay!
authorBen Laurie <ben@apache.org>
Sun, 18 Feb 2001 02:10:27 +0000 (02:10 +0000)
committerBen Laurie <ben@apache.org>
Sun, 18 Feb 2001 02:10:27 +0000 (02:10 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88223 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/tls/mod_tls.c
modules/tls/openssl_state_machine.c
modules/tls/openssl_state_machine.h

diff --git a/CHANGES b/CHANGES
index 04a4de5b076528154b6101b9d9a4e1eaec4b74b2..85a5724e28da06899571dcacbaa61d49d427b781 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,8 @@
 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]
 
index 04c885856874454dd369322d9dbc3c2c46bee16d..fda42408543c08b304f312598bdfd9a923bdfc86 100644 (file)
@@ -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>
@@ -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)
@@ -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;
@@ -148,16 +151,25 @@ static apr_status_t churn(TLSFilterCtx *pCtx)
 
        done=0;
 
-       n=SSLStateMachine_write_extract(pCtx->pStateMachine,buf,sizeof buf);
-       if(n > 0) {
-           if(!pbbOutput)
-               pbbOutput=apr_brigade_create(pCtx->pOutputFilter->c->pool);
-           pbkt=apr_bucket_pool_create(buf,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);*/
+       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);
+
+               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);
     
@@ -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);
+    if(APR_BRIGADE_EMPTY(pCtx->pbbInput)) {
+       ap_get_brigade(pCtx->pInputFilter->next,pCtx->pbbInput,eMode);
+       if(APR_BRIGADE_EMPTY(pCtx->pbbInput))
+           return APR_EOF;
     }
-    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(f->next,pCtx->pbbInput,eMode);
 
     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);
@@ -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).
@@ -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;
index d6d01bd878598e0241f137dba1ffa390693e3387..82c86a404aa95c1e450b19018260cf6e8c3fa42e 100644 (file)
@@ -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.
@@ -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);
+    }
index 84df580f41ec3c730da38e1bc4f173a15d376ead..10be69a3b073878085eeba1605f3d5e850daf516 100644 (file)
@@ -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);