Merge branch 'feature/starttls' of https://github.com/RichardPoole42/sslsplit into feature/autossl

This commit is contained in:
Daniel Roethlisberger 2015-04-21 15:09:31 +02:00
commit 96b038ef9b
6 changed files with 176 additions and 4 deletions

14
opts.c
View File

@ -123,7 +123,7 @@ opts_has_ssl_spec(opts_t *opts)
proxyspec_t *p = opts->spec;
while (p) {
if (p->ssl)
if (p->ssl || p->tlspeek)
return 1;
p = p->next;
}
@ -284,18 +284,27 @@ proxyspec_parse(int *argc, char **argv[], const char *natengine)
if (!strcmp(**argv, "tcp")) {
spec->ssl = 0;
spec->http = 0;
spec->tlspeek = 0;
} else
if (!strcmp(**argv, "ssl")) {
spec->ssl = 1;
spec->http = 0;
spec->tlspeek = 0;
} else
if (!strcmp(**argv, "http")) {
spec->ssl = 0;
spec->http = 1;
spec->tlspeek = 0;
} else
if (!strcmp(**argv, "https")) {
spec->ssl = 1;
spec->http = 1;
spec->tlspeek = 0;
} else
if (!strcmp(**argv, "genericstarttls")) {
spec->ssl = 0;
spec->http = 0;
spec->tlspeek = 1;
} else {
fprintf(stderr, "Unknown connection "
"type '%s'\n", **argv);
@ -459,9 +468,10 @@ proxyspec_str(proxyspec_t *spec)
return NULL;
}
}
if (asprintf(&s, "[%s]:%s %s %s %s", lhbuf, lpbuf,
if (asprintf(&s, "[%s]:%s %s %s %s %s", lhbuf, lpbuf,
(spec->ssl ? "ssl" : "tcp"),
(spec->http ? "http" : "plain"),
(spec->tlspeek ? "peeking" : ""),
(spec->natengine ? spec->natengine : cbuf)) < 0) {
s = NULL;
}

1
opts.h
View File

@ -39,6 +39,7 @@
typedef struct proxyspec {
unsigned int ssl : 1;
unsigned int http : 1;
unsigned int tlspeek: 1;
struct sockaddr_storage listen_addr;
socklen_t listen_addrlen;
/* connect_addr and connect_addrlen are set: static mode;

View File

@ -128,6 +128,8 @@ typedef struct pxy_conn_ctx {
unsigned int ocsp_denied : 1; /* 1 if OCSP was denied */
unsigned int enomem : 1; /* 1 if out of memory */
unsigned int sni_peek_retries : 6; /* max 64 SNI parse retries */
unsigned int looking_for_client_hello : 1; /* 1 if waiting for hello */
unsigned int pending_ssl_upgrade : 1; /* 1 if ssl upgrade in progress */
/* server name indicated by client in SNI TLS extension */
char *sni;
@ -198,6 +200,10 @@ pxy_conn_ctx_new(proxyspec_t *spec, opts_t *opts,
memset(ctx, 0, sizeof(pxy_conn_ctx_t));
ctx->spec = spec;
ctx->opts = opts;
ctx->looking_for_client_hello = spec->tlspeek;
if (OPTS_DEBUG(opts)) {
log_dbg_printf("looking status is %d\n", ctx->looking_for_client_hello);
}
ctx->fd = fd;
ctx->thridx = pxy_thrmgr_attach(thrmgr, &ctx->evbase, &ctx->dnsbase);
ctx->thrmgr = thrmgr;
@ -1505,6 +1511,50 @@ deny:
}
}
int
pxy_conn_check_and_upgrade(pxy_conn_ctx_t *ctx)
{
struct evbuffer *inbuf;
struct evbuffer_iovec vec_out[1];
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Checking for a client hello\n");
}
/* peek the buffer */
inbuf = bufferevent_get_input(ctx->src.bev);
if(evbuffer_peek(inbuf, 1024, 0, vec_out, 1)) {
if(ssl_tls_clienthello_identify(vec_out->iov_base, &(vec_out->iov_len))) {
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Found a clienthello in midstream\n");
}
ctx->dst.ssl = pxy_dstssl_create(ctx);
if(!ctx->dst.ssl) {
log_err_printf("Error creating SSL for upgrade\n");
return 0;
}
ctx->dst.bev = bufferevent_openssl_filter_new(ctx->evbase, ctx->dst.bev, ctx->dst.ssl, BUFFEREVENT_SSL_CONNECTING, 0);
bufferevent_setcb(ctx->dst.bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
bufferevent_enable(ctx->dst.bev, EV_READ|EV_WRITE);
if(!ctx->dst.bev) {
return 0;
}
if( OPTS_DEBUG(ctx->opts)) {
log_err_printf("Replaced dst bufferevent, new one is %p\n", ctx->dst.bev);
}
ctx->spec->ssl = 1;
ctx->looking_for_client_hello = 0;
ctx->pending_ssl_upgrade = 1;
return 1;
} else {
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("checked buffer, no client hello found\n");
}
return 0;
}
}
return 0;
}
void
pxy_conn_terminate_free(pxy_conn_ctx_t *ctx)
{
@ -1544,6 +1594,12 @@ pxy_bev_readcb(struct bufferevent *bev, void *arg)
exit(EXIT_FAILURE);
}
if(ctx->looking_for_client_hello) {
if(pxy_conn_check_and_upgrade(ctx)) {
return;
}
}
struct evbuffer *inbuf = bufferevent_get_input(bev);
if (other->closed) {
evbuffer_drain(inbuf, evbuffer_get_length(inbuf));
@ -1772,8 +1828,21 @@ pxy_bev_eventcb(struct bufferevent *bev, short events, void *arg)
return;
}
}
if(ctx->pending_ssl_upgrade) {
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("completing ssl upgrade\n");
}
ctx->src.bev = bufferevent_openssl_filter_new(ctx->evbase,
ctx->src.bev, ctx->src.ssl,
BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_DEFER_CALLBACKS);
bufferevent_setcb(ctx->src.bev, pxy_bev_readcb,
pxy_bev_writecb, pxy_bev_eventcb, ctx);
bufferevent_enable(ctx->src.bev, EV_READ|EV_WRITE);
ctx->pending_ssl_upgrade = 0;
} else {
ctx->src.bev = pxy_bufferevent_setup(ctx, ctx->fd,
ctx->src.ssl);
}
if (!ctx->src.bev) {
if (ctx->src.ssl) {
SSL_free(ctx->src.ssl);

82
ssl.c
View File

@ -1902,6 +1902,88 @@ out:
DBG_printf("%zd bytes unparsed\n", n);
return servername;
}
int
ssl_tls_clienthello_identify(const unsigned char *buf, ssize_t *sz)
{
#ifdef DEBUG_SNI_PARSER
#define DBG_printf(...) log_dbg_printf("SNI Parser: " __VA_ARGS__)
#else /* !DEBUG_SNI_PARSER */
#define DBG_printf(...)
#endif /* !DEBUG_SNI_PARSER */
const unsigned char *p = buf;
ssize_t n = *sz;
DBG_printf("buffer length %zd\n", n);
if (n < 1) {
*sz = -1;
goto out2;
}
DBG_printf("byte 0: %02x\n", *p);
/* first byte 0x80, third byte 0x01 is SSLv2 clientHello;
* first byte 0x22, second byte 0x03 is SSLv3/TLSv1.x clientHello */
if (*p != 22) /* record type: handshake protocol */
goto out2;
p++; n--;
if (n < 2) {
*sz = -1;
goto out2;
}
DBG_printf("version: %02x %02x\n", p[0], p[1]);
if (p[0] != 3)
goto out2;
p += 2; n -= 2;
if (n < 2) {
*sz = -1;
goto out2;
}
DBG_printf("length: %02x %02x\n", p[0], p[1]);
#ifdef DEBUG_SNI_PARSER
ssize_t recordlen = p[1] + (p[0] << 8);
DBG_printf("recordlen=%zd\n", recordlen);
#endif /* DEBUG_SNI_PARSER */
p += 2; n -= 2;
if (n < 1) {
*sz = -1;
goto out2;
}
DBG_printf("message type: %i\n", *p);
if (*p != 1) /* message type: ClientHello */
goto out2;
p++; n--;
if (n < 3) {
*sz = -1;
goto out2;
}
DBG_printf("message len: %02x %02x %02x\n", p[0], p[1], p[2]);
ssize_t msglen = p[2] + (p[1] << 8) + (p[0] << 16);
DBG_printf("msglen=%zd\n", msglen);
if (msglen < 4)
goto out2;
p += 3; n -= 3;
if (n < msglen) {
*sz = -1;
goto out2;
}
n = msglen; /* only parse first message */
if (n < 2)
goto out2;
DBG_printf("clienthello version %02x %02x\n", p[0], p[1]);
if (p[0] != 3)
goto out2;
p += 2; n -= 2;
return 1;
out2:
DBG_printf("%zd bytes unparsed\n", n);
return 0;
}
#endif /* !OPENSSL_NO_TLSEXT */
/* vim: set noet ft=c: */

2
ssl.h
View File

@ -167,6 +167,8 @@ int ssl_is_ocspreq(const unsigned char *, size_t) NONNULL(1) WUNRES;
#ifndef OPENSSL_NO_TLSEXT
char * ssl_tls_clienthello_parse_sni(const unsigned char *, ssize_t *)
NONNULL(1,2) MALLOC;
int ssl_tls_clienthello_identify(const unsigned char *, ssize_t *)
NONNULL(1,2);
#endif /* !OPENSSL_NO_TLSEXT */
int ssl_dnsname_match(const char *, size_t, const char *, size_t)
NONNULL(1,3) WUNRES;

View File

@ -332,6 +332,9 @@ SNI DNS lookup):
.br
\fBtcp\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP]
.br
\fBgenericstarttls\fP \fIlistenaddr port\fP
[\fInat-engine\fP|\fIfwdaddr port\fP]
.ad
.TP
\fBhttps\fP
@ -350,6 +353,11 @@ the removal of HPKP, HSTS and Alternate Protocol response headers.
Plain TCP connection without SSL/TLS and without any lower level protocol
decoding; decrypted connection content is treated as opaque stream of bytes
and not modified.
.TP
\fBgenericstarttls\fP
Plain TCP connection until an SSL client hello appears in the byte stream;
then starts SSL/TLS interception.
.TP
.I listenaddr port
IPv4 or IPv6 address and port or service name to listen on. This is the