From b9af9a3fafd99a3fb81cb09f01390531ffe6f29d Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Fri, 16 Apr 2004 02:59:01 +0000 Subject: [PATCH] * Completed SSL support. Still doesn't work with all browsers... gotta figure out why --- webcit/ChangeLog | 5 +- webcit/crypto.c | 141 +++++++++++++++++++++++++++++++++++++++------ webcit/floors.c | 2 +- webcit/html2html.c | 2 +- webcit/webcit.c | 11 +++- webcit/webcit.h | 14 +++-- webcit/webserver.c | 35 ++++++++++- webcit/webserver.h | 2 + 8 files changed, 183 insertions(+), 29 deletions(-) diff --git a/webcit/ChangeLog b/webcit/ChangeLog index efdffadef..b73e91ee9 100644 --- a/webcit/ChangeLog +++ b/webcit/ChangeLog @@ -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 1998-12-03 Nathan Bryant * webserver.c: warning fix - diff --git a/webcit/crypto.c b/webcit/crypto.c index 145beb79e..6c270ad5b 100644 --- a/webcit/crypto.c +++ b/webcit/crypto.c @@ -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 */ diff --git a/webcit/floors.c b/webcit/floors.c index b2d9de93f..d040aa3f3 100644 --- a/webcit/floors.c +++ b/webcit/floors.c @@ -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 */ diff --git a/webcit/html2html.c b/webcit/html2html.c index 3b6a9c3e8..a4931be33 100644 --- a/webcit/html2html.c +++ b/webcit/html2html.c @@ -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("

\n"); diff --git a/webcit/webcit.c b/webcit/webcit.c index ea7a94e07..f5c6fba7c 100644 --- a/webcit/webcit.c +++ b/webcit/webcit.c @@ -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)) { diff --git a/webcit/webcit.h b/webcit/webcit.h index afb3c9d7b..7eff9c1ca 100644 --- a/webcit/webcit.h +++ b/webcit/webcit.h @@ -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 diff --git a/webcit/webserver.c b/webcit/webserver.c index a8f0ac4d9..1c308dc0c 100644 --- a/webcit/webserver.c +++ b/webcit/webserver.c @@ -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; } } diff --git a/webcit/webserver.h b/webcit/webserver.h index 9bfede9ec..c3588ffad 100644 --- a/webcit/webserver.h +++ b/webcit/webserver.h @@ -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, ...); -- 2.30.2