Skip to content

Instantly share code, notes, and snippets.

@markrwilliams
Last active March 19, 2017 07:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save markrwilliams/ff25334d544fcb10cb6231697b518b3b to your computer and use it in GitHub Desktop.
Save markrwilliams/ff25334d544fcb10cb6231697b518b3b to your computer and use it in GitHub Desktop.
OpenSSL incomplete handshake SSL_ERROR_SSL -> SSL_ERROR_SYSCALL
import os
import sys
from subprocess import check_call, CalledProcessError
HERE = os.path.dirname(__file__)
def build_openssl():
check_call(['git', 'clean', '-fxdq'])
check_call(['./config'])
check_call(['make', '-j', '4', 'build_libs'])
def build_canary():
check_call(['make', 'clean'], cwd=HERE)
check_call(['make', 'OPENSSL={}'.format(os.getcwd())], cwd=HERE)
def run_canary():
check_call(['./client'], cwd=HERE)
try:
build_openssl()
build_canary()
except CalledProcessError:
sys.exit(125)
else:
run_canary()
finally:
check_call(['git', 'reset', '--hard', '-q'])
check_call(['git', 'clean', '-fxdq'])
/*
Reproduce the disconnect-without-handshake behavior of
twisted.protocols.test.test_tls.TLSProducerTests.registerProducerAfterConnectionLost.
Written in C so that it compiles against versions of OpenSSL 1.1.0
(such as 1.1.0-pre[123]) that do not implement the API cryptogaphy.io
requires of 1.1.0.
*/
#include <assert.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <string.h>
#include <stdio.h>
void
report_library_error()
{
unsigned long error;
error = ERR_get_error();
printf("%ld %s %s %s\n",
error,
ERR_lib_error_string(error),
ERR_func_error_string(error),
ERR_reason_error_string(error));
}
void
report_library_error_and_die()
{
report_library_error();
exit(1);
}
void
consider_ssl_err(int ssl_err)
{
switch(ssl_err) {
case SSL_ERROR_NONE:
puts("SSL_ERROR_NONE");
break;
case SSL_ERROR_ZERO_RETURN:
puts("SSL_ERROR_ZERO_RETURN");
break;
case SSL_ERROR_WANT_READ:
puts("SSL_ERROR_WANT_READ");
break;
case SSL_ERROR_WANT_WRITE:
puts("SSL_ERROR_WANT_WRITE");
break;
case SSL_ERROR_WANT_CONNECT:
puts("SSL_ERROR_WANT_CONNECT");
break;
case SSL_ERROR_WANT_ACCEPT:
puts("SSL_ERROR_WANT_ACCEPT");
break;
case SSL_ERROR_WANT_X509_LOOKUP:
puts("SSL_ERROR_WANT_X509_LOOKUP");
break;
case SSL_ERROR_SYSCALL:
puts("SSL_ERROR_SYSCALL");
report_library_error();
break;
case SSL_ERROR_SSL:
puts("SSL_ERROR_SSL");
report_library_error();
break;
default:
printf("unknown error code: %d\n", ssl_err);
break;
}
}
int
main(int argc, char *argv[])
{
SSL_CTX *ctx;
BIO *rbio, *wbio;
SSL *ssl;
int ret, ssl_err;
char buf[1024];
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
if (!(ctx = SSL_CTX_new(SSLv23_client_method()))) {
report_library_error_and_die();
}
if (!(ssl = SSL_new(ctx))) {
report_library_error_and_die();
}
rbio = BIO_new(BIO_s_mem());
wbio = BIO_new(BIO_s_mem());
SSL_set_bio(ssl, rbio, wbio);
SSL_set_connect_state(ssl);
assert((ret = SSL_do_handshake(ssl)) < 1);
assert(SSL_get_error(ssl, ret) == SSL_ERROR_WANT_READ);
assert(BIO_read(rbio, buf, sizeof(buf)) < 1);
assert(BIO_should_retry(rbio));
assert(BIO_set_mem_eof_return(rbio, 0));
ret = SSL_read(ssl, buf, sizeof(buf));
ssl_err = SSL_get_error(ssl, ret);
if (argc > 1 && !(strncmp(argv[1], "-v", 2))) {
consider_ssl_err(ssl_err);
}
return ssl_err != SSL_ERROR_SSL;
}
CFLAGS = -I${OPENSSL}/include -ldl -lpthread
LOADLIBES = ${OPENSSL}/libssl.a ${OPENSSL}/libcrypto.a
all: client
.PHONY : clean
clean:
-rm -f client
@markrwilliams
Copy link
Author

Run bisect.py from an OpenSSL checkout:

$ cd ~/src/c/openssl
$ git bisect start
$ git bisect good OpenSSL_1_0_2k
$ git bisect bad 
Bisecting: a merge base must be tested
[70b2186e240dc39fd75e3a89ca931f8ba4117e80] Stop warnings.
$ git bisect run /path/to/this/gist/bisect.py
...
commit 9ab930b27d51a13362e6647074f13589a8ac004d
Author: Matt Caswell <matt@openssl.org>
Date:   Wed Jul 29 14:20:05 2015 +0100

    Split ssl3_get_message
    
    The function ssl3_get_message gets a whole message from the underlying bio
    and returns it to the state machine code. The new state machine code will
    split this into two discrete steps: get the message header and get the
    message body. This commit splits the existing function into these two
    sub steps to facilitate the state machine implementation.
    
    Reviewed-by: Tim Hudson <tjh@openssl.org>
    Reviewed-by: Richard Levitte <levitte@openssl.org>

:040000 040000 36f59627729d58ab8f2ca5f27dd9d3b149809a7e f03d6711405d733c9b1a4078063b3a9e851d3978 M      include
:040000 040000 bab246d652218e7987a9f28c031a5a7e33fb05ad 3d1bc72c02073c426257c2ed4318bb77e237d7cf M      ssl
bisect run success

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment