From 65aba238a8acdc9b14140bf678062d0b9595a61d Mon Sep 17 00:00:00 2001 From: ~hatteb-mitlyd Date: Thu, 13 Mar 2014 18:52:03 -0700 Subject: [PATCH] first-line work on SSLifying the HTTP client doesn't work at all, because the encrypted buffer never gets written to the net --- Makefile | 6 +- include/v/vere.h | 15 +++- v/cttp.c | 208 ++++++++++++++++++++++++++++++++++++++++------- v/loop.c | 2 + 4 files changed, 198 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index d647f9336..bf146d972 100644 --- a/Makefile +++ b/Makefile @@ -43,14 +43,14 @@ ifeq ($(OS),osx) OSLIBS=-framework CoreServices -framework CoreFoundation endif ifeq ($(OS),linux) - OSLIBS=-lcrypto -lpthread -lrt -lcurses + OSLIBS=-lpthread -lrt -lcurses DEFINES=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE endif ifeq ($(OS),bsd) - OSLIBS=-lcrypto -lpthread -lncurses -lkvm + OSLIBS=-lpthread -lncurses -lkvm endif -LIBS=-lgmp -lncurses -lsigsegv $(OSLIBS) +LIBS=-lssl -lcrypto -lgmp -lncurses -lsigsegv $(OSLIBS) INCLUDE=include GENERATED=generated diff --git a/include/v/vere.h b/include/v/vere.h index 16b9c20ea..91455c760 100644 --- a/include/v/vere.h +++ b/include/v/vere.h @@ -44,7 +44,9 @@ typedef enum { u2_csat_dead = 0, // connection dead u2_csat_addr = 1, // connection addressed - u2_csat_live = 2, // connection open + u2_csat_clyr = 2, // connection open in cleartext + u2_csat_shak = 3, // connection handshaking ssl + u2_csat_cryp = 4, // connection open and crypted } u2_csat; /* u2_hmet: http method. Matches jhttp encoding. @@ -139,12 +141,21 @@ struct _u2_creq* nex_u; // next in queue } u2_creq; + /* u2_sslx: per-connection ssl context. + */ + typedef struct _u2_sslx { + void* ssl_u; // struct SSL* + void* rio_u; // struct BIO* for read + void* wio_u; // struct BIO* for write + } u2_sslx; + /* u2_ccon: outgoing http connection. */ typedef struct _u2_ccon { // client connection uv_tcp_t wax_u; // i/o handler state uv_connect_t cot_u; // connection handler state uv_getaddrinfo_t adr_u; // resolver state + u2_sslx ssl; // ssl state u2_csat sat_e; // connection state c3_c* hot_c; // hostname c3_s por_s; // port @@ -532,12 +543,14 @@ u2_unix unx_u; // sync and clay u2_batz beh_u; // batz timer u2_bean liv; // if u2_no, shut down + void* ssl_u; // struct SSL_CTX* u2_reck* arv_u; // runtime } u2_host; // host == computer == process # define u2L u2_Host.lup_u // global event loop # define u2R (&(u2_Raft)) +# define u2S u2_Host.ssl_u /* u2_funk: standard system function. */ diff --git a/v/cttp.c b/v/cttp.c index db559ab49..9ca9b231f 100644 --- a/v/cttp.c +++ b/v/cttp.c @@ -19,10 +19,19 @@ #include #include +#include +#include +#include + #include "../outside/jhttp/http_parser.h" // Joyent HTTP #include "all.h" #include "v/vere.h" +#ifdef U2_OS_osx +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + /* Forward declarations. */ static void _cttp_ccon_kick(u2_ccon* coc_u); @@ -720,6 +729,9 @@ _cttp_ccon_waste(u2_ccon* coc_u, c3_c* msg_c) if ( coc_u->nex_u ) { coc_u->nex_u->pre_u = coc_u->pre_u; } + if ( coc_u->ssl.ssl_u ) { + SSL_free(coc_u->ssl.ssl_u); + } free(coc_u); } @@ -762,7 +774,13 @@ _cttp_ccon_reboot(u2_ccon* coc_u) _cttp_ccon_waste(coc_u, "connection failed"); break; } - case u2_csat_live: { + case u2_csat_shak: { + /* Got a connection, but SSL failed. Waste it. + */ + _cttp_ccon_waste(coc_u, "ssl handshake failed"); + break; + } + case u2_csat_clyr: { /* We had a connection but it broke. Either there are no ** living requests, in which case waste; otherwise reset. */ @@ -803,7 +821,7 @@ _cttp_ccon_fail(u2_ccon* coc_u, u2_bean say) uL(fprintf(uH, "cttp: %s\n", uv_strerror(uv_last_error(u2L)))); } - if ( coc_u->sat_e != u2_csat_live ) { + if ( coc_u->sat_e < u2_csat_shak ) { _cttp_ccon_reboot(coc_u); } else { @@ -872,7 +890,9 @@ _cttp_ccon_kick_connect_cb(uv_connect_t* cot_u, _cttp_ccon_fail(coc_u, u2_yes); } else { - coc_u->sat_e = u2_csat_live; + coc_u->sat_e = (u2_yes == coc_u->sec) ? + u2_csat_shak : + u2_csat_clyr; _cttp_ccon_kick(coc_u); } } @@ -911,10 +931,10 @@ _cttp_ccon_kick_connect(u2_ccon* coc_u) c3_y* buf_y; } _u2_write_t; -/* _cttp_ccon_kick_write_cb(): general write callback. +/* _cttp_ccon_kick_write_clyr_cb(): general write callback for cleartext conn */ static void -_cttp_ccon_kick_write_cb(uv_write_t* wri_u, c3_i sas_i) +_cttp_ccon_kick_write_clyr_cb(uv_write_t* wri_u, c3_i sas_i) { u2_lo_open(); { @@ -942,7 +962,7 @@ _cttp_ccon_kick_write_buf(u2_ccon* coc_u, uv_buf_t buf_u) if ( 0 != uv_write(&ruq_u->wri_u, (uv_stream_t*)&(coc_u->wax_u), &buf_u, 1, - _cttp_ccon_kick_write_cb) ) + _cttp_ccon_kick_write_clyr_cb) ) { _cttp_ccon_fail(coc_u, u2_yes); } @@ -986,13 +1006,68 @@ _cttp_ccon_kick_write(u2_ccon* coc_u) } } -/* _cttp_ccon_read_cb() -*/ static void -_cttp_ccon_kick_read_cb(uv_stream_t* tcp_u, - ssize_t siz_i, - uv_buf_t buf_u) +_cttp_ccon_kick_cryp_hurr(SSL* ssl, int rev) { + c3_i err = SSL_get_error(ssl, rev); + if ( SSL_ERROR_WANT_READ == err ) { + uL(fprintf(uH, ("ssl-hurr want read\n"))); + } + else { + uL(fprintf(uH, ("ssl-hurr fucked\n"))); + } +} +static void +_cttp_ccon_pars_shov(u2_ccon* coc_u, void* buf_u, ssize_t siz_i) +{ + + u2_creq* ceq_u = coc_u->ceq_u; + + if ( !ceq_u ) { // spurious input + uL(fprintf(uH, "http: response to no request\n")); + } + else { + if ( !ceq_u->res_u ) { + _cttp_cres_start(ceq_u); + } + + if ( siz_i != http_parser_execute(ceq_u->res_u->par_u, + &_cttp_settings, + (c3_c*)buf_u, + siz_i) ) + { + uL(fprintf(uH, "http: parse error\n")); + _cttp_ccon_fail(coc_u, u2_no); + } + } +} + +static void +_cttp_ccon_kick_cryp_pull(u2_ccon* coc_u) +{ + uL(fprintf(uH, "cttp-cryp-pull\n")); + if ( SSL_is_init_finished(coc_u->ssl.ssl_u) ) { + static c3_c buf[1<<14]; + c3_i ruf; + while ( 0 < (ruf = SSL_read(coc_u->ssl.ssl_u, &buf, sizeof(buf))) ) { + _cttp_ccon_pars_shov(coc_u, &buf, ruf); + } + } + else { + // not connected + c3_i r = SSL_connect(coc_u->ssl.ssl_u); + if ( 0 > r ) { + _cttp_ccon_kick_cryp_hurr(coc_u->ssl.ssl_u, r); + } + } +} + +static void +_cttp_ccon_kick_read_cryp_cb(uv_stream_t* tcp_u, + ssize_t siz_i, + uv_buf_t buf_u) +{ + uL(fprintf(uH, "cttp-cryp-read\n")); u2_ccon *coc_u = _cttp_ccon_wax((uv_tcp_t*)tcp_u); u2_lo_open(); @@ -1009,18 +1084,8 @@ _cttp_ccon_kick_read_cb(uv_stream_t* tcp_u, uL(fprintf(uH, "http: response to no request\n")); } else { - if ( !ceq_u->res_u ) { - _cttp_cres_start(ceq_u); - } - - if ( siz_i != http_parser_execute(ceq_u->res_u->par_u, - &_cttp_settings, - (c3_c*)buf_u.base, - siz_i) ) - { - uL(fprintf(uH, "http: parse error\n")); - _cttp_ccon_fail(coc_u, u2_no); - } + BIO_write(coc_u->ssl.rio_u, (c3_c*)buf_u.base, siz_i); + _cttp_ccon_kick_cryp_pull(coc_u); } } if ( buf_u.base ) { @@ -1030,14 +1095,73 @@ _cttp_ccon_kick_read_cb(uv_stream_t* tcp_u, u2_lo_shut(u2_yes); } -/* _cttp_ccon_kick_read(): start reading. +/* _cttp_ccon_read_clyr_cb() */ static void -_cttp_ccon_kick_read(u2_ccon* coc_u) +_cttp_ccon_kick_read_clyr_cb(uv_stream_t* tcp_u, + ssize_t siz_i, + uv_buf_t buf_u) +{ + u2_ccon *coc_u = _cttp_ccon_wax((uv_tcp_t*)tcp_u); + + u2_lo_open(); + { + if ( siz_i < 0 ) { + uv_err_t las_u = uv_last_error(u2L); + + _cttp_ccon_fail(coc_u, (UV_EOF == las_u.code) ? u2_no : u2_yes); + } + else { + _cttp_ccon_pars_shov(coc_u, buf_u.base, siz_i); + } + if ( buf_u.base ) { + free(buf_u.base); + } + } + u2_lo_shut(u2_yes); +} + +/* _cttp_ccon_kick_read_clyr(): start reading on insecure socket. +*/ +static void +_cttp_ccon_kick_read_clyr(u2_ccon* coc_u) { uv_read_start((uv_stream_t*)&coc_u->wax_u, _cttp_alloc, - _cttp_ccon_kick_read_cb); + _cttp_ccon_kick_read_clyr_cb); +} + +/* _cttp_ccon_kick_handshake(): start ssl handshake. +*/ +static void +_cttp_ccon_kick_handshake(u2_ccon* coc_u) +{ + uL(fprintf(uH, "cttp: shak\n")); + + coc_u->ssl.ssl_u = SSL_new(u2S); + c3_assert(coc_u->ssl.ssl_u); + + coc_u->ssl.rio_u = BIO_new(BIO_s_mem()); + c3_assert(coc_u->ssl.rio_u); + + coc_u->ssl.wio_u = BIO_new(BIO_s_mem()); + c3_assert(coc_u->ssl.wio_u); + + BIO_set_nbio(coc_u->ssl.rio_u, 1); + BIO_set_nbio(coc_u->ssl.wio_u, 1); + + SSL_set_bio(coc_u->ssl.ssl_u, + coc_u->ssl.rio_u, + coc_u->ssl.wio_u); + + SSL_set_connect_state(coc_u->ssl.ssl_u); + SSL_do_handshake(coc_u->ssl.ssl_u); + + coc_u->sat_e = u2_csat_cryp; + uv_read_start((uv_stream_t*)&coc_u->wax_u, + _cttp_alloc, + _cttp_ccon_kick_read_cryp_cb); + _cttp_ccon_kick_cryp_pull(coc_u); } /* _cttp_ccon_kick(): start appropriate I/O on client connection. @@ -1059,13 +1183,17 @@ _cttp_ccon_kick(u2_ccon* coc_u) _cttp_ccon_kick_connect(coc_u); break; } - case u2_csat_live: { + case u2_csat_shak: { + _cttp_ccon_kick_handshake(coc_u); + break; + } + case u2_csat_clyr: { _cttp_ccon_fill(coc_u); if ( coc_u->rub_u ) { _cttp_ccon_kick_write(coc_u); } - _cttp_ccon_kick_read(coc_u); + _cttp_ccon_kick_read_clyr(coc_u); break; } } @@ -1320,12 +1448,33 @@ u2_cttp_ef_thus(c3_l num_l, u2z(cuq); } -/* u2_cttp_io_init(): initialize http I/O. +/* u2_cttp_io_init(): initialize http client I/O. */ void u2_cttp_io_init() { + c3_i rad; + c3_y buf[4096]; + u2_Host.ctp_u.coc_u = 0; + + SSL_library_init(); + SSL_load_error_strings(); + + u2_Host.ssl_u = SSL_CTX_new(TLSv1_client_method()); + SSL_CTX_set_options(u2S, SSL_OP_NO_SSLv2); + SSL_CTX_set_verify(u2S, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_session_cache_mode(u2S, SSL_SESS_CACHE_OFF); + + // RAND_status, at least on OS X, never returns true. + // 4096 bytes should be enough entropy for anyone, right? + rad = open("/dev/urandom", O_RDONLY); + if ( 4096 != read(rad, &buf, 4096) ) { + perror("rand-seed"); + exit(1); + } + RAND_seed(buf, 4096); + close(rad); } /* u2_cttp_io_poll(): poll kernel for cttp I/O. @@ -1340,4 +1489,5 @@ u2_cttp_io_poll(void) void u2_cttp_io_exit(void) { + SSL_CTX_free(u2S); } diff --git a/v/loop.c b/v/loop.c index a501144f2..71ef42668 100644 --- a/v/loop.c +++ b/v/loop.c @@ -163,6 +163,7 @@ _lo_init() u2_ames_io_init(); u2_term_io_init(); u2_http_io_init(); + u2_cttp_io_init(); u2_save_io_init(); u2_batz_io_init(); } @@ -186,6 +187,7 @@ u2_lo_exit(void) u2_ames_io_exit(); u2_term_io_exit(); u2_http_io_exit(); + u2_cttp_io_exit(); u2_save_io_exit(); u2_batz_io_exit(); }