* Completed SSL support. Still doesn't work with all browsers... gotta
authorArt Cancro <ajc@citadel.org>
Fri, 16 Apr 2004 02:59:01 +0000 (02:59 +0000)
committerArt Cancro <ajc@citadel.org>
Fri, 16 Apr 2004 02:59:01 +0000 (02:59 +0000)
  figure out why

webcit/ChangeLog
webcit/crypto.c
webcit/floors.c
webcit/html2html.c
webcit/webcit.c
webcit/webcit.h
webcit/webserver.c
webcit/webserver.h

index efdffadef2471ea870e79197dad6cc6c73f2b62c..b73e91ee90ecfe2e4778e496ca76b183fb8f3692 100644 (file)
@@ -1,4 +1,8 @@
 $Log$
+Revision 506.4  2004/04/16 02:59:01  ajc
+* Completed SSL support.  Still doesn't work with all browsers... gotta
+  figure out why
+
 Revision 506.3  2004/04/15 03:57:00  ajc
 * Brought over the SSL/TLS stuff from Citadel.  I think it's complete but
   it has a crashy crashy bug in it.  Don't use it yet.
@@ -1766,4 +1770,3 @@ Sun Dec  6 19:50:55 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
 
 1998-12-03 Nathan Bryant <bryant@cs.usm.maine.edu>
        * webserver.c: warning fix
-
index 145beb79e9e7041b6dec101fed0725aaab5c64db..6c270ad5b75bad57bd667163778014946933b27b 100644 (file)
@@ -18,7 +18,7 @@
 #define CTDL_KEY_PATH          CTDL_CRYPTO_DIR "/citadel.key"
 #define CTDL_CSR_PATH          CTDL_CRYPTO_DIR "/citadel.csr"
 #define CTDL_CER_PATH          CTDL_CRYPTO_DIR "/citadel.cer"
-#define SIGN_DAYS              3650    /* Ten years */
+#define SIGN_DAYS              365
 
 
 /* Shared Diffie-Hellman parameters */
@@ -30,6 +30,8 @@
 SSL_CTX *ssl_ctx;              /* SSL context */
 pthread_mutex_t **SSLCritters; /* Things needing locking */
 
+pthread_key_t ThreadSSL;       /* Per-thread SSL context */
+
 static unsigned long id_callback(void)
 {
        return (unsigned long) pthread_self();
@@ -371,26 +373,27 @@ void init_ssl(void)
 /*
  * starttls() starts SSL/TLS encryption for the current session.
  */
-int starttls(void) {
-
+int starttls(int sock) {
        int retval, bits, alg_bits;
+       SSL *newssl;
+
+       pthread_setspecific(ThreadSSL, NULL);
 
        if (!ssl_ctx) {
                return(1);
        }
-       if (!(WC->ssl = SSL_new(ssl_ctx))) {
+       if (!(newssl = SSL_new(ssl_ctx))) {
                lprintf(3, "SSL_new failed: %s\n",
                                ERR_reason_error_string(ERR_get_error()));
                return(2);
        }
-       if (!(SSL_set_fd(WC->ssl, WC->http_sock))) {
+       if (!(SSL_set_fd(newssl, sock))) {
                lprintf(3, "SSL_set_fd failed: %s\n",
                        ERR_reason_error_string(ERR_get_error()));
-               SSL_free(WC->ssl);
-               WC->ssl = NULL;
+               SSL_free(newssl);
                return(3);
        }
-       retval = SSL_accept(WC->ssl);
+       retval = SSL_accept(newssl);
        if (retval < 1) {
                /*
                 * Can't notify the client of an error here; they will
@@ -399,21 +402,23 @@ int starttls(void) {
                 */
                long errval;
 
-               errval = SSL_get_error(WC->ssl, retval);
+               errval = SSL_get_error(newssl, retval);
                lprintf(3, "SSL_accept failed: %s\n",
                        ERR_reason_error_string(ERR_get_error()));
-               SSL_free(WC->ssl);
-               WC->ssl = NULL;
+               SSL_free(newssl);
+               newssl = NULL;
                return(4);
        }
-       BIO_set_close(WC->ssl->rbio, BIO_NOCLOSE);
+       BIO_set_close(newssl->rbio, BIO_NOCLOSE);
        bits =
-           SSL_CIPHER_get_bits(SSL_get_current_cipher(WC->ssl),
+           SSL_CIPHER_get_bits(SSL_get_current_cipher(newssl),
                                &alg_bits);
        lprintf(5, "SSL/TLS using %s on %s (%d of %d bits)\n",
-               SSL_CIPHER_get_name(SSL_get_current_cipher(WC->ssl)),
-               SSL_CIPHER_get_version(SSL_get_current_cipher(WC->ssl)),
+               SSL_CIPHER_get_name(SSL_get_current_cipher(newssl)),
+               SSL_CIPHER_get_version(SSL_get_current_cipher(newssl)),
                bits, alg_bits);
+
+       pthread_setspecific(ThreadSSL, newssl);
        return(0);
 }
 
@@ -428,9 +433,9 @@ int starttls(void) {
 void endtls(void)
 {
        lprintf(5, "Ending SSL/TLS\n");
-       SSL_shutdown(WC->ssl);
-       SSL_free(WC->ssl);
-       WC->ssl = NULL;
+       SSL_shutdown(THREADSSL);
+       SSL_free(THREADSSL);
+       pthread_setspecific(ThreadSSL, NULL);
 }
 
 
@@ -445,4 +450,104 @@ void ssl_lock(int mode, int n, const char *file, int line)
                pthread_mutex_unlock(SSLCritters[n]);
 }
 
+/*
+ * client_write_ssl() Send binary data to the client encrypted.
+ */
+void client_write_ssl(char *buf, int nbytes)
+{
+       int retval;
+       int nremain;
+       char junk[1];
+
+       nremain = nbytes;
+
+       while (nremain > 0) {
+               if (SSL_want_write(THREADSSL)) {
+                       if ((SSL_read(THREADSSL, junk, 0)) < 1) {
+                               lprintf(9, "SSL_read in client_write: %s\n", ERR_reason_error_string(ERR_get_error()));
+                       }
+               }
+               retval =
+                   SSL_write(THREADSSL, &buf[nbytes - nremain], nremain);
+               if (retval < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(THREADSSL, retval);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                           errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+                       lprintf(9, "SSL_write got error %ld, ret %d\n", errval, retval);
+                       if (retval == -1)
+                               lprintf(9, "errno is %d\n", errno);
+                       endtls();
+                       client_write(&buf[nbytes - nremain], nremain);
+                       return;
+               }
+               nremain -= retval;
+       }
+}
+
+
+/*
+ * client_read_ssl() - read data from the encrypted layer.
+ */
+int client_read_ssl(char *buf, int bytes, int timeout)
+{
+#if 0
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+       int s;
+#endif
+       int len, rlen;
+       char junk[1];
+
+       len = 0;
+       while (len < bytes) {
+#if 0
+               /*
+                * This code is disabled because we don't need it when
+                * using blocking reads (which we are). -IO
+                */
+               FD_ZERO(&rfds);
+               s = BIO_get_fd(THREADSSL->rbio, NULL);
+               FD_SET(s, &rfds);
+               tv.tv_sec = timeout;
+               tv.tv_usec = 0;
+
+               retval = select(s + 1, &rfds, NULL, NULL, &tv);
+
+               if (FD_ISSET(s, &rfds) == 0) {
+                       return (0);
+               }
+
+#endif
+               if (SSL_want_read(THREADSSL)) {
+                       if ((SSL_write(THREADSSL, junk, 0)) < 1) {
+                               lprintf(9, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error()));
+                       }
+               }
+               rlen = SSL_read(THREADSSL, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(THREADSSL, rlen);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                           errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+                       lprintf(9, "SSL_read got error %ld\n", errval);
+                       endtls();
+                       return (client_read_to
+                               (&buf[len], bytes - len, timeout));
+               }
+               len += rlen;
+       }
+       return (1);
+}
+
+
 #endif                         /* HAVE_OPENSSL */
index b2d9de93f288dcfdc89df5668c5c0a85868a38bf..d040aa3f38748f81f07577f5c6c3c0b0e20fd94a 100644 (file)
@@ -43,7 +43,7 @@ void display_floorconfig(char *prepend_html)
        output_headers(3);
 
        if (prepend_html != NULL) {
-               write(WC->http_sock, prepend_html, strlen(prepend_html));
+               client_write(prepend_html, strlen(prepend_html));
        }
 
        serv_printf("LFLR");    /* FIXME put a real test here */
index 3b6a9c3e83ad57fb7419d01a42572d20c3b22ace..a4931be33731a9e76ebdb1b887637f56fbe81ae9 100644 (file)
@@ -193,7 +193,7 @@ void output_html(void) {
        }
 
        /* Output our big pile of markup */
-       write(WC->http_sock, converted_msg, output_length);
+       client_write(converted_msg, output_length);
 
        /* A little trailing vertical whitespace... */
        wprintf("<BR><BR>\n");
index ea7a94e07fd78b2c247283a8a3f97ac370dbf43f..f5c6fba7c910e1d665336f69028d7ce034fc141b 100644 (file)
@@ -161,7 +161,7 @@ void wprintf(const char *format,...)
        vsprintf(wbuf, format, arg_ptr);
        va_end(arg_ptr);
 
-       write(WC->http_sock, wbuf, strlen(wbuf));
+       client_write(wbuf, strlen(wbuf));
 }
 
 
@@ -463,7 +463,7 @@ void http_transmit_thing(char *thing, size_t length, char *content_type,
                (long) length,
                SERVER
        );
-       write(WC->http_sock, thing, (size_t)length);
+       client_write(thing, (size_t)length);
 }
 
 
@@ -798,7 +798,7 @@ void session_loop(struct httprequest *req)
        char buf[SIZ];
        int a, b;
        int ContentLength = 0;
-       int BytesRead;
+       int BytesRead = 0;
        char ContentType[512];
        char *content;
        char *content_end;
@@ -866,6 +866,7 @@ void session_loop(struct httprequest *req)
                                ContentType, ContentLength);
                body_start = strlen(content);
 
+/***** old version
                BytesRead = 0;
                while (BytesRead < ContentLength) {
                        a=read(WC->http_sock, &content[BytesRead+body_start],
@@ -873,6 +874,10 @@ void session_loop(struct httprequest *req)
                        if (a <= 0) BytesRead = ContentLength;
                        else BytesRead += a;
                }
+*******/
+
+               /* Now we're daring and read it all at once. */
+               client_read(WC->http_sock, &content[BytesRead+body_start], ContentLength);
 
                if (!strncasecmp(ContentType,
                              "application/x-www-form-urlencoded", 33)) {
index afb3c9d7b406bfe2c203642de0b2556166e869ca..7eff9c1ca53aedaccb98912df3d7302ff616bc85 100644 (file)
@@ -227,17 +227,21 @@ struct wcsession {
        int outside_frameset_allowed;   /* nonzero if current req is allowed
                                         * outside of the main frameset */
        char last_chat_user[SIZ];
-#ifdef HAVE_OPENSSL
-       SSL *ssl;
-#endif
 };
 
 #define extract(dest,source,parmnum)   extract_token(dest,source,parmnum,'|')
 #define num_parms(source)              num_tokens(source, '|')
 
+/* Per-session data */
 #define WC ((struct wcsession *)pthread_getspecific(MyConKey))
 extern pthread_key_t MyConKey;
 
+/* Per-thread SSL context */
+#ifdef HAVE_OPENSSL
+#define THREADSSL ((SSL *)pthread_getspecific(ThreadSSL))
+extern pthread_key_t ThreadSSL;
+#endif
+
 struct serv_info serv_info;
 extern char floorlist[128][SIZ];
 extern char *axdefs[];
@@ -456,7 +460,9 @@ void spawn_another_worker_thread(void);
 void init_ssl(void);
 void endtls(void);
 void ssl_lock(int mode, int n, const char *file, int line);
-int starttls(void);
+int starttls(int sock);
 extern SSL_CTX *ssl_ctx;  
+int client_read_ssl(char *buf, int bytes, int timeout);
+void client_write_ssl(char *buf, int nbytes);
 #endif
 
index a8f0ac4d9b9105977455ea13a65b5bfc492d6ec4..1c308dc0c3db2ea5c030ce4c549be44e6caade29 100644 (file)
@@ -112,6 +112,13 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
        struct timeval tv;
        int retval;
 
+
+#ifdef HAVE_OPENSSL
+       if (is_https) {
+               return(client_read_ssl(buf, bytes, timeout));
+       }
+#endif
+
        len = 0;
        while (len < bytes) {
                FD_ZERO(&rfds);
@@ -124,7 +131,9 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
                if (FD_ISSET(sock, &rfds) == 0) {
                        return (0);
                }
+
                rlen = read(sock, &buf[len], bytes - len);
+
                if (rlen < 1) {
                        lprintf(2, "client_read() failed: %s\n",
                               strerror(errno));
@@ -135,6 +144,18 @@ int client_read_to(int sock, char *buf, int bytes, int timeout)
        return (1);
 }
 
+
+ssize_t client_write(const void *buf, size_t count) {
+#ifdef HAVE_OPENSSL
+       if (is_https) {
+               client_write_ssl((char *)buf, count);
+               return(count);
+       }
+#endif
+       return(write(WC->http_sock, buf, count));
+}
+
+
 /*
  * Read data from the client socket with default timeout.
  * (This is implemented in terms of client_read_to() and could be
@@ -295,6 +316,18 @@ int main(int argc, char **argv)
                 lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
         }
 
+       /*
+        * Set up a place to put thread-specific SSL data.
+        * We don't stick this in the wcsession struct because SSL starts
+        * up before the session is bound, and it gets torn down between
+        * transactions.
+        */
+#ifdef HAVE_OPENSSL
+        if (pthread_key_create(&ThreadSSL, NULL) != 0) {
+                lprintf(1, "Can't create TSD key: %s\n", strerror(errno));
+        }
+#endif
+
        /*
         * Bind the server to our favorite port.
         * There is no need to check for errors, because ig_tcp_server()
@@ -360,7 +393,7 @@ void worker_entry(void) {
                        /* If we are an HTTPS server, go crypto now. */
 #ifdef HAVE_OPENSSL
                        if (is_https) {
-                               if (starttls() != 0) {
+                               if (starttls(ssock) != 0) {
                                        fail_this_transaction = 1;
                                }
                        }
index 9bfede9eca6e0b078b21818856fddca980a70d19..c3588ffad1455f7f2beffe2dc07d8a1b524f80b8 100644 (file)
@@ -1,4 +1,6 @@
 /* $Id$ */
 int client_gets(int sock, char *buf);
 int client_read(int sock, char *buf, int bytes);
+int client_read_to(int sock, char *buf, int bytes, int timeout);
+ssize_t client_write(const void *buf, size_t count);
 int lprintf(int loglevel, const char *format, ...);