Hi,

in course of building up a dataset of mappings of User-Agents and their
common SSL ClientHello messages[1] I started to implement a small patch
to nginx 1.13.0 mainline to implement a small transcript of the
handshake process similar to the one enabled in openssl s_client -msg.

The format of this new variable is a series of semicolon-delimited
blocks starting with a direction (C: -> sent by client, S: -> sent by
server) and followed by colon-delimited hexdumps of processed blocks of
data. The string C:00:01;S:0203:0405:0607;C:08090A; denotes 2 blocks of
1 byte each sent by the client, followed by 3 blocks 2 byte each from
the server and finally 1 block of 3 bytes from the client. Blocks
usually conform to how OpenSSL tries to process the data.

I'd appreciate a short review of the patch with comments for possible
improvements, code style and other related things. One yet open aspect
is configurable behaviour to only enable collection on-demand. Also
ideas for migrating this code into a separate module are welcome.

NB: Due to size of transcribed information it is commonly not feasible
to pass this variable to other backend servers via HTTP headers. Trying
to do so will most likely result in the backend responding 400 Bad
Request, sometimes also remarking about the overlong header. I'm still
looking at ways how this can be done in a sane way (abusing memcached
for transporting could be an option) - but this is outside the scope of
this patch. Plainly logging the information of this header to some
custom log will do just fine.

Kind regards,
BenBE.

[1] Original paper on this approach at [2]
[2] https://jhalderm.com/pub/papers/interception-ndss17.pdf
From 5cf530602b0d73e388cecf5cf4b26d1c124ae847 Mon Sep 17 00:00:00 2001
From: Benny Baumann <be...@geshi.org>
Date: Mon, 1 May 2017 18:32:00 +0200
Subject: [PATCH] add: Transcript of SSL Handshake similar to openssl s_client
 -msg

---
 src/event/ngx_event_openssl.c          | 110 +++++++++++++++++++++++++++++++++
 src/event/ngx_event_openssl.h          |   5 ++
 src/http/modules/ngx_http_ssl_module.c |   3 +
 3 files changed, 118 insertions(+)

diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index fdbd0c9..0ad1b34 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1171,6 +1171,9 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
         return NGX_ERROR;
     }
 
+    sc->handshake_transcript = NULL;
+    sc->handshake_transcript_next = &sc->handshake_transcript;
+
     sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
     sc->buffer_size = ssl->buffer_size;
 
@@ -1219,6 +1222,55 @@ ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)
     return NGX_OK;
 }
 
+static void
+ngx_ssl_handshake_transcribe(int write_p, int version, int content_type,
+        const void *buf, size_t len, SSL *ssl, void *arg) {
+    ngx_connection_t* c = (ngx_connection_t*) arg;
+    if ( !c ) {
+        return;
+    }
+
+    ngx_ssl_connection_t* sc = c->ssl;
+    if ( !sc ) {
+        return;
+    }
+
+    // Sanity check:
+    if ( ssl != sc->connection ) {
+        return;
+    }
+
+    ngx_pool_t* p = c->pool;
+
+    ngx_chain_t* cl = ngx_alloc_chain_link( p );
+    if ( !cl ) {
+        return;
+    }
+
+    ngx_buf_t* b = ngx_calloc_buf( p );
+    if ( !b ) {
+        return;
+    }
+
+    b->temporary = 1;
+    b->start = ngx_pcalloc( p, len );
+    if ( !b->start ) {
+        return;
+    }
+
+    b->pos = b->start;
+    b->end = b->start + len;
+
+    b->last = ngx_cpymem( b->pos, buf, len );
+
+    b->tag = (ngx_buf_tag_t)(uintptr_t)!!write_p;
+
+    cl->buf = b;
+    cl->next = NULL;
+
+    *sc->handshake_transcript_next = cl;
+    sc->handshake_transcript_next = &cl->next;
+}
 
 ngx_int_t
 ngx_ssl_handshake(ngx_connection_t *c)
@@ -1228,8 +1280,14 @@ ngx_ssl_handshake(ngx_connection_t *c)
 
     ngx_ssl_clear_error(c->log);
 
+    SSL_set_msg_callback(c->ssl->connection, ngx_ssl_handshake_transcribe);
+    SSL_set_msg_callback_arg(c->ssl->connection, c);
+
     n = SSL_do_handshake(c->ssl->connection);
 
+    SSL_set_msg_callback(c->ssl->connection, NULL);
+    SSL_set_msg_callback_arg(c->ssl->connection, NULL);
+
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
 
     if (n == 1) {
@@ -4088,6 +4146,58 @@ ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
     return NGX_OK;
 }
 
+ngx_int_t ngx_ssl_get_raw_handshake(ngx_connection_t *c, ngx_pool_t *pool,
+        ngx_str_t *s) {
+
+    if(!s) {
+        return NGX_ERROR;
+    }
+
+    ngx_ssl_connection_t* sc = c->ssl;
+    if(!sc) {
+        return NGX_ERROR;
+    }
+
+    ngx_chain_t* hsp = sc->handshake_transcript;
+    if(!hsp) {
+        ngx_str_null(s);
+        return NGX_OK;
+    }
+
+    size_t hs_len = 0;
+
+    for(ngx_chain_t* cp = hsp; cp; cp = cp->next) {
+        hs_len += 2; //Client/Server designation
+        hs_len += 2 * (cp->buf->last - cp->buf->pos); //Paket dump
+        hs_len += 1; //Delimiter
+    }
+
+    s->len = hs_len;
+    s->data = ngx_pnalloc( pool, s->len );
+    if ( s->data == NULL ) {
+        return NGX_ERROR;
+    }
+
+    u_char* p = s->data;
+
+    ngx_buf_tag_t prev = (ngx_buf_tag_t)(uintptr_t)!hsp->buf->tag;
+
+    for(ngx_chain_t* cp = hsp; cp; prev = cp->buf->tag, cp = cp->next) {
+        if(cp->buf->tag != prev) {
+            p = ngx_cpystrn(p, (u_char *)(cp->buf->tag ? "S:" : "C:"), 3);
+        } else {
+            *(p-1) = ':';
+        }
+
+        p = ngx_hex_dump( p, cp->buf->pos, cp->buf->last - cp->buf->pos );
+
+        *p++ = ';';
+    }
+
+    s->len = p - s->data;
+
+    return NGX_OK;
+}
 
 static time_t
 ngx_ssl_parse_time(
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
index 2a14980..b4f8f1f 100644
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -79,6 +79,9 @@ struct ngx_ssl_connection_s {
     ngx_event_handler_pt        saved_read_handler;
     ngx_event_handler_pt        saved_write_handler;
 
+    ngx_chain_t*                handshake_transcript;
+    ngx_chain_t**               handshake_transcript_next;
+
     unsigned                    handshaked:1;
     unsigned                    renegotiation:1;
     unsigned                    buffer:1;
@@ -232,6 +235,8 @@ ngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_raw_handshake(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 
 
 ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
index b466e5d..8de97fa 100644
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -329,6 +329,9 @@ static ngx_http_variable_t  ngx_http_ssl_vars[] = {
     { ngx_string("ssl_client_v_remain"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_raw_handshake"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_raw_handshake, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
-- 
2.12.0.rc0

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel

Reply via email to