From 4aa69fe0b6e03d3aaba5e975eb21a4dc98f016ad Mon Sep 17 00:00:00 2001 From: Geoff Thorpe Date: Tue, 28 Nov 2000 23:27:23 +0000 Subject: [PATCH] Minor tweaks and improvements to the tunala demo. - Add "-cipher" and "-out_state" command line arguments to control SSL cipher-suites and handshake debug output respectively. - Implemented error handling for SSL handshakes that break down. This uses a cheat - storing a non-NULL pointer as "app_data" in the SSL structure when the SSL should be killed. --- demos/tunala/Makefile | 5 ++-- demos/tunala/buffer.c | 35 ++++++++++++++++++++++++ demos/tunala/cb.c | 39 +++++++++++++++++++++++++++ demos/tunala/sm.c | 19 ++++++------- demos/tunala/tunala.c | 63 +++++++++++++++++++++++++++++++++++-------- demos/tunala/tunala.h | 6 ++++- 6 files changed, 144 insertions(+), 23 deletions(-) create mode 100644 demos/tunala/cb.c diff --git a/demos/tunala/Makefile b/demos/tunala/Makefile index fd5b651bc7..a68db7a39d 100644 --- a/demos/tunala/Makefile +++ b/demos/tunala/Makefile @@ -17,8 +17,8 @@ COMPILE=$(CC) $(CFLAGS) -c # Edit, particularly the "-ldl" if not building with "dlfcn" support LINK_FLAGS=-L$(SSL_LIBDIR) -lssl -lcrypto -ldl -SRCS=buffer.c ip.c sm.c tunala.c -OBJS=buffer.o ip.o sm.o tunala.o +SRCS=buffer.c cb.c ip.c sm.c tunala.c +OBJS=buffer.o cb.o ip.o sm.o tunala.o TARGETS=tunala @@ -35,6 +35,7 @@ tunala: $(OBJS) # Extra dependencies, should really use makedepend buffer.o: buffer.c tunala.h +cb.o: cb.c tunala.h ip.o: ip.c tunala.h sm.o: sm.c tunala.h tunala.o: tunala.c tunala.h diff --git a/demos/tunala/buffer.c b/demos/tunala/buffer.c index e9a4e5b030..2915f2c67b 100644 --- a/demos/tunala/buffer.c +++ b/demos/tunala/buffer.c @@ -101,6 +101,37 @@ int buffer_to_fd(buffer_t *buf, int fd) #ifndef NO_OPENSSL +static void int_ssl_check(SSL *s, int ret) +{ + int e = SSL_get_error(s, ret); + switch(e) { + /* These seem to be harmless and already "dealt with" by our + * non-blocking environment. NB: "ZERO_RETURN" is the clean + * "error" indicating a successfully closed SSL tunnel. We let + * this happen because our IO loop should not appear to have + * broken on this condition - and outside the IO loop, the + * "shutdown" state is checked. */ + case SSL_ERROR_NONE: + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_X509_LOOKUP: + case SSL_ERROR_ZERO_RETURN: + return; + /* These seem to be indications of a genuine error that should + * result in the SSL tunnel being regarded as "dead". */ + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + SSL_set_app_data(s, (char *)1); + return; + default: + break; + } + /* For any other errors that (a) exist, and (b) crop up - we need to + * interpret what to do with them - so "politely inform" the caller that + * the code needs updating here. */ + abort(); +} + void buffer_from_SSL(buffer_t *buf, SSL *ssl) { int ret; @@ -109,6 +140,8 @@ void buffer_from_SSL(buffer_t *buf, SSL *ssl) ret = SSL_read(ssl, buf->data + buf->used, buffer_unused(buf)); if(ret > 0) buf->used += ret; + if(ret < 0) + int_ssl_check(ssl, ret); } void buffer_to_SSL(buffer_t *buf, SSL *ssl) @@ -119,6 +152,8 @@ void buffer_to_SSL(buffer_t *buf, SSL *ssl) ret = SSL_write(ssl, buf->data, buf->used); if(ret > 0) buffer_takedata(buf, NULL, ret); + if(ret < 0) + int_ssl_check(ssl, ret); } void buffer_from_BIO(buffer_t *buf, BIO *bio) diff --git a/demos/tunala/cb.c b/demos/tunala/cb.c new file mode 100644 index 0000000000..ebc69bc690 --- /dev/null +++ b/demos/tunala/cb.c @@ -0,0 +1,39 @@ +#include "tunala.h" + +#ifndef NO_OPENSSL + +/* For callbacks generating output, here are their file-descriptors. */ +static FILE *fp_cb_ssl_info = NULL; + +/* This function is largely borrowed from the one used in OpenSSL's "s_client" + * and "s_server" utilities. */ +void cb_ssl_info(SSL *s, int where, int ret) +{ + char *str1, *str2; + int w; + + if(!fp_cb_ssl_info) + return; + + w = where & ~SSL_ST_MASK; + str1 = (w & SSL_ST_CONNECT ? "SSL_connect" : (w & SSL_ST_ACCEPT ? + "SSL_accept" : "undefined")), + str2 = SSL_state_string_long(s); + + if (where & SSL_CB_LOOP) + fprintf(stderr, "%s:%s\n", str1, str2); + else if (where & SSL_CB_EXIT) { + if (ret == 0) + fprintf(stderr, "%s:failed in %s\n", str1, str2); + else if (ret < 0) + fprintf(stderr, "%s:error in %s\n", str1, str2); + } +} + +void cb_ssl_info_set_output(FILE *fp) +{ + fp_cb_ssl_info = fp; +} + +#endif /* !defined(NO_OPENSSL) */ + diff --git a/demos/tunala/sm.c b/demos/tunala/sm.c index ee661c5197..c213d110d5 100644 --- a/demos/tunala/sm.c +++ b/demos/tunala/sm.c @@ -55,7 +55,7 @@ SSL *state_machine_get_SSL(state_machine_t *machine) return machine->ssl; } -void state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server) +int state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server) { if(machine->ssl) /* Shouldn't ever be set twice */ @@ -75,7 +75,7 @@ void state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server) /* If we're the first one to generate traffic - do it now otherwise we * go into the next select empty-handed and our peer will not send data * but will similarly wait for us. */ - state_machine_churn(machine); + return state_machine_churn(machine); } /* Performs the data-IO loop and returns zero if the machine should close */ @@ -98,13 +98,14 @@ int state_machine_churn(state_machine_t *machine) /* Still buffered data on the clean side to go out */ return 1; } - if(SSL_get_shutdown(machine->ssl)) { - /* An SSL shutdown was underway */ - if(buffer_empty(&machine->dirty_out)) { - /* Great, we can seal off the dirty side completely */ - if(!state_machine_close_dirty(machine)) - return 0; - } + /* We close on the SSL side if the info callback noticed some problems + * or an SSL shutdown was underway and shutdown traffic had all been + * sent. */ + if(SSL_get_app_data(machine->ssl) || (SSL_get_shutdown(machine->ssl) && + buffer_empty(&machine->dirty_out))) { + /* Great, we can seal off the dirty side completely */ + if(!state_machine_close_dirty(machine)) + return 0; } /* Either the SSL is alive and well, or the closing process still has * outgoing data waiting to be sent */ diff --git a/demos/tunala/tunala.c b/demos/tunala/tunala.c index cc32adc050..bb90338582 100644 --- a/demos/tunala/tunala.c +++ b/demos/tunala/tunala.c @@ -67,7 +67,8 @@ typedef struct _tunala_world_t { /*****************************/ static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id, - const char *CAfile, const char *cert, const char *key); + const char *CAfile, const char *cert, const char *key, + const char *cipher_list, int out_state); static void selector_init(tunala_selector_t *selector); static void selector_add_listener(tunala_selector_t *selector, int fd); static void selector_add_tunala(tunala_selector_t *selector, tunala_item_t *t); @@ -92,6 +93,8 @@ static const char *def_cert = NULL; static const char *def_key = NULL; static const char *def_engine_id = NULL; static int def_server_mode = 0; +static const char *def_cipher_list = NULL; +static int def_out_state = 0; static const char *helpstring = "\n'Tunala' (A tunneler with a New Zealand accent)\n" @@ -104,6 +107,8 @@ static const char *helpstring = " -key (default = whatever '-cert' is)\n" " -engine (default = NULL)\n" " -server <0|1> (default = 0, ie. an SSL client)\n" + " -cipher (specifies cipher list to use)\n" + " -out_state (prints SSL handshake states)\n" " - (displays this help screen)\n" "NB: It is recommended to specify a cert+key when operating as an\n" "SSL server. If you only specify '-cert', the same file must\n" @@ -178,6 +183,8 @@ int main(int argc, char *argv[]) const char *key = def_key; const char *engine_id = def_engine_id; int server_mode = def_server_mode; + const char *cipher_list = def_cipher_list; + int out_state = def_out_state; /* Parse command-line arguments */ next_arg: @@ -242,6 +249,15 @@ next_arg: if(!parse_server_mode(*argv, &server_mode)) return 1; goto next_arg; + } else if(strcmp(*argv, "-cipher") == 0) { + if(argc < 2) + return usage("-cipher requires an argument", 0); + argc--; argv++; + cipher_list = *argv; + goto next_arg; + } else if(strcmp(*argv, "-out_state") == 0) { + out_state = 1; + goto next_arg; } else if((strcmp(*argv, "-h") == 0) || (strcmp(*argv, "-help") == 0) || (strcmp(*argv, "-?") == 0)) { @@ -257,7 +273,7 @@ next_arg: err_str0("ip_initialise succeeded"); /* Create the SSL_CTX */ if((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id, - cacert, cert, key)) == NULL) + cacert, cert, key, cipher_list, out_state)) == NULL) return err_str1("initialise_ssl_ctx(engine_id=%s) failed", (engine_id == NULL) ? "NULL" : engine_id); err_str1("initialise_ssl_ctx(engine_id=%s) succeeded", @@ -306,12 +322,11 @@ main_loop: &newfd) == 1)) { /* We have a new connection */ if(!tunala_world_new_item(&world, newfd, - proxy_ip, proxy_port)) { + proxy_ip, proxy_port)) fprintf(stderr, "tunala_world_new_item failed\n"); - abort(); - } - fprintf(stderr, "Info, new tunnel opened, now up to %d\n", - world.tunnels_used); + else + fprintf(stderr, "Info, new tunnel opened, now up to " + "%d\n", world.tunnels_used); } /* Give each tunnel its moment, note the while loop is because it makes * the logic easier than with "for" to deal with an array that may shift @@ -344,7 +359,8 @@ main_loop: /****************/ static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id, - const char *CAfile, const char *cert, const char *key) + const char *CAfile, const char *cert, const char *key, + const char *cipher_list, int out_state) { SSL_CTX *ctx, *ret = NULL; SSL_METHOD *meth; @@ -439,6 +455,23 @@ static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id, } else fprintf(stderr, "Info, operating without a cert or key\n"); + /* cipher_list */ + if(cipher_list) { + if(!SSL_CTX_set_cipher_list(ctx, cipher_list)) { + fprintf(stderr, "Error setting cipher list '%s'\n", + cipher_list); + goto err; + } + fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list); + } else + fprintf(stderr, "Info, operating with default cipher list\n"); + + /* out_state (output of SSL handshake states to screen). */ + if(out_state) { + SSL_CTX_set_info_callback(ctx, cb_ssl_info); + cb_ssl_info_set_output(stderr); + } + /* Success! */ ret = ctx; err: @@ -577,9 +610,15 @@ static int tunala_world_new_item(tunala_world_t *world, int fd, { tunala_item_t *item; int newfd; + SSL *new_ssl = NULL; if(!tunala_world_make_room(world)) return 0; + if((new_ssl = SSL_new(world->ssl_ctx)) == NULL) { + fprintf(stderr, "Error creating new SSL\n"); + ERR_print_errors_fp(stderr); + return 0; + } item = world->tunnels + (world->tunnels_used++); state_machine_init(&item->sm); item->clean_read = item->clean_send = @@ -596,11 +635,13 @@ static int tunala_world_new_item(tunala_world_t *world, int fd, item->clean_read = item->clean_send = fd; item->dirty_read = item->dirty_send = newfd; } - state_machine_set_SSL(&item->sm, SSL_new(world->ssl_ctx), - world->server_mode); + /* We use the SSL's "app_data" to indicate a call-back induced "kill" */ + SSL_set_app_data(new_ssl, NULL); + if(!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode)) + goto err; return 1; err: - state_machine_close(&item->sm); + tunala_world_del_item(world, world->tunnels_used - 1); return 0; } diff --git a/demos/tunala/tunala.h b/demos/tunala/tunala.h index 7ad012b92e..2f4040462b 100644 --- a/demos/tunala/tunala.h +++ b/demos/tunala/tunala.h @@ -89,6 +89,10 @@ void buffer_from_SSL(buffer_t *buf, SSL *ssl); void buffer_to_SSL(buffer_t *buf, SSL *ssl); void buffer_from_BIO(buffer_t *buf, BIO *bio); void buffer_to_BIO(buffer_t *buf, BIO *bio); + +/* Callbacks */ +void cb_ssl_info(SSL *s, int where, int ret); +void cb_ssl_info_set_output(FILE *fp); /* Called if output should be sent too */ #endif /* !defined(NO_OPENSSL) */ #endif /* !defined(NO_BUFFER) */ @@ -111,7 +115,7 @@ void state_machine_init(state_machine_t *machine); void state_machine_close(state_machine_t *machine); buffer_t *state_machine_get_buffer(state_machine_t *machine, sm_buffer_t type); SSL *state_machine_get_SSL(state_machine_t *machine); -void state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server); +int state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server); /* Performs the data-IO loop and returns zero if the machine should close */ int state_machine_churn(state_machine_t *machine); /* Is used to handle closing conditions - namely when one side of the tunnel has -- GitLab