Add a (D)TLS dumper BIO
authorMatt Caswell <matt@openssl.org>
Mon, 4 Jul 2016 13:51:56 +0000 (14:51 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 19 Aug 2016 12:52:40 +0000 (13:52 +0100)
Dump out the records passed over the BIO. Only works for DTLS at the
moment but could easily be extended to TLS.

Reviewed-by: Richard Levitte <levitte@openssl.org>
test/ssltestlib.c
test/ssltestlib.h

index 6d638a2..42141fc 100644 (file)
@@ -9,6 +9,225 @@
 
 #include "ssltestlib.h"
 
+static int tls_dump_new(BIO *bi);
+static int tls_dump_free(BIO *a);
+static int tls_dump_read(BIO *b, char *out, int outl);
+static int tls_dump_write(BIO *b, const char *in, int inl);
+static long tls_dump_ctrl(BIO *b, int cmd, long num, void *ptr);
+static int tls_dump_gets(BIO *bp, char *buf, int size);
+static int tls_dump_puts(BIO *bp, const char *str);
+
+/* Choose a sufficiently large type likely to be unused for this custom BIO */
+# define BIO_TYPE_TLS_DUMP_FILTER  (0x80 | BIO_TYPE_FILTER)
+
+# define BIO_TYPE_MEMPACKET_TEST      0x81
+
+static BIO_METHOD *method_tls_dump = NULL;
+static BIO_METHOD *method_mempacket_test = NULL;
+
+/* Note: Not thread safe! */
+const BIO_METHOD *bio_f_tls_dump_filter(void)
+{
+    if (method_tls_dump == NULL) {
+        method_tls_dump = BIO_meth_new(BIO_TYPE_TLS_DUMP_FILTER,
+                                        "TLS dump filter");
+        if (   method_tls_dump == NULL
+            || !BIO_meth_set_write(method_tls_dump, tls_dump_write)
+            || !BIO_meth_set_read(method_tls_dump, tls_dump_read)
+            || !BIO_meth_set_puts(method_tls_dump, tls_dump_puts)
+            || !BIO_meth_set_gets(method_tls_dump, tls_dump_gets)
+            || !BIO_meth_set_ctrl(method_tls_dump, tls_dump_ctrl)
+            || !BIO_meth_set_create(method_tls_dump, tls_dump_new)
+            || !BIO_meth_set_destroy(method_tls_dump, tls_dump_free))
+            return NULL;
+    }
+    return method_tls_dump;
+}
+
+void bio_f_tls_dump_filter_free(void)
+{
+    BIO_meth_free(method_tls_dump);
+}
+
+static int tls_dump_new(BIO *bio)
+{
+    BIO_set_init(bio, 1);
+    return 1;
+}
+
+static int tls_dump_free(BIO *bio)
+{
+    BIO_set_init(bio, 0);
+
+    return 1;
+}
+
+static void copy_flags(BIO *bio)
+{
+    int flags;
+    BIO *next = BIO_next(bio);
+
+    flags = BIO_test_flags(next, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS);
+    BIO_clear_flags(bio, BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_RWS);
+    BIO_set_flags(bio, flags);
+}
+
+#define RECORD_CONTENT_TYPE     0
+#define RECORD_VERSION_HI       1
+#define RECORD_VERSION_LO       2
+#define RECORD_EPOCH_HI         3
+#define RECORD_EPOCH_LO         4
+#define RECORD_SEQUENCE_START   5
+#define RECORD_SEQUENCE_END     10
+#define RECORD_LEN_HI           11
+#define RECORD_LEN_LO           12
+
+#define MSG_TYPE                0
+#define MSG_LEN_HI              1
+#define MSG_LEN_MID             2
+#define MSG_LEN_LO              3
+#define MSG_SEQ_HI              4
+#define MSG_SEQ_LO              5
+#define MSG_FRAG_OFF_HI         6
+#define MSG_FRAG_OFF_MID        7
+#define MSG_FRAG_OFF_LO         8
+#define MSG_FRAG_LEN_HI         9
+#define MSG_FRAG_LEN_MID        10
+#define MSG_FRAG_LEN_LO         11
+
+
+static void dump_data(const char *data, int len)
+{
+    int rem, i, content, reclen, msglen, fragoff, fraglen, epoch;
+    unsigned char *rec;
+
+    printf("---- START OF PACKET ----\n");
+
+    rem = len;
+    rec = (unsigned char *)data;
+
+    while (rem > 0) {
+        if (rem != len)
+            printf("*\n");
+        printf("*---- START OF RECORD ----\n");
+        if (rem < DTLS1_RT_HEADER_LENGTH) {
+            printf("*---- RECORD TRUNCATED ----\n");
+            break;
+        }
+        content = rec[RECORD_CONTENT_TYPE];
+        printf("** Record Content-type: %d\n", content);
+        printf("** Record Version: %02x%02x\n",
+               rec[RECORD_VERSION_HI], rec[RECORD_VERSION_LO]);
+        epoch = (rec[RECORD_EPOCH_HI] << 8) | rec[RECORD_EPOCH_LO];
+        printf("** Record Epoch: %d\n", epoch);
+        printf("** Record Sequence: ");
+        for (i = RECORD_SEQUENCE_START; i <= RECORD_SEQUENCE_END; i++)
+            printf("%02x", rec[i]);
+        reclen = (rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO];
+        printf("\n** Record Length: %d\n", reclen);
+
+        /* Now look at message */
+        rec += DTLS1_RT_HEADER_LENGTH;
+        rem -= DTLS1_RT_HEADER_LENGTH;
+        if (content == SSL3_RT_HANDSHAKE) {
+            printf("**---- START OF HANDSHAKE MESSAGE FRAGMENT ----\n");
+            if (epoch > 0) {
+                printf("**---- HANDSHAKE MESSAGE FRAGMENT ENCRYPTED ----\n");
+            } else if (rem < DTLS1_HM_HEADER_LENGTH
+                    || reclen < DTLS1_HM_HEADER_LENGTH) {
+                printf("**---- HANDSHAKE MESSAGE FRAGMENT TRUNCATED ----\n");
+            } else {
+                printf("*** Message Type: %d\n", rec[MSG_TYPE]);
+                msglen = (rec[MSG_LEN_HI] << 16) | (rec[MSG_LEN_MID] << 8)
+                         | rec[MSG_LEN_LO];
+                printf("*** Message Length: %d\n", msglen);
+                printf("*** Message sequence: %d\n",
+                       (rec[MSG_SEQ_HI] << 8) | rec[MSG_SEQ_LO]);
+                fragoff = (rec[MSG_FRAG_OFF_HI] << 16)
+                          | (rec[MSG_FRAG_OFF_MID] << 8)
+                          | rec[MSG_FRAG_OFF_LO];
+                printf("*** Message Fragment offset: %d\n", fragoff);
+                fraglen = (rec[MSG_FRAG_LEN_HI] << 16)
+                          | (rec[MSG_FRAG_LEN_MID] << 8)
+                          | rec[MSG_FRAG_LEN_LO];
+                printf("*** Message Fragment len: %d\n", fraglen);
+                if (fragoff + fraglen > msglen)
+                    printf("***---- HANDSHAKE MESSAGE FRAGMENT INVALID ----\n");
+                else if(reclen < fraglen)
+                    printf("**---- HANDSHAKE MESSAGE FRAGMENT TRUNCATED ----\n");
+                else
+                    printf("**---- END OF HANDSHAKE MESSAGE FRAGMENT ----\n");
+            }
+        }
+        if (rem < reclen) {
+            printf("*---- RECORD TRUNCATED ----\n");
+            rem = 0;
+        } else {
+            rec += reclen;
+            rem -= reclen;
+            printf("*---- END OF RECORD ----\n");
+        }
+    }
+    printf("---- END OF PACKET ----\n\n");
+    fflush(stdout);
+}
+
+static int tls_dump_read(BIO *bio, char *out, int outl)
+{
+    int ret;
+    BIO *next = BIO_next(bio);
+
+    ret = BIO_read(next, out, outl);
+    copy_flags(bio);
+
+    if (ret > 0) {
+        dump_data(out, ret);
+    }
+
+    return ret;
+}
+
+static int tls_dump_write(BIO *bio, const char *in, int inl)
+{
+    int ret;
+    BIO *next = BIO_next(bio);
+
+    ret = BIO_write(next, in, inl);
+    copy_flags(bio);
+
+    return ret;
+}
+
+static long tls_dump_ctrl(BIO *bio, int cmd, long num, void *ptr)
+{
+    long ret;
+    BIO *next = BIO_next(bio);
+
+    if (next == NULL)
+        return 0;
+
+    switch (cmd) {
+    case BIO_CTRL_DUP:
+        ret = 0L;
+        break;
+    default:
+        ret = BIO_ctrl(next, cmd, num, ptr);
+        break;
+    }
+    return ret;
+}
+
+static int tls_dump_gets(BIO *bio, char *buf, int size)
+{
+    /* We don't support this - not needed anyway */
+    return -1;
+}
+
+static int tls_dump_puts(BIO *bio, const char *str)
+{
+    return tls_dump_write(bio, str, strlen(str));
+}
+
 int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm,
                         SSL_CTX **sctx, SSL_CTX **cctx, char *certfile,
                         char *privkeyfile)
index f05878a..f0f32c8 100644 (file)
@@ -18,4 +18,8 @@ int create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm,
 int create_ssl_connection(SSL_CTX *serverctx, SSL_CTX *clientctx, SSL **sssl,
                           SSL **cssl, BIO *s_to_c_fbio, BIO *c_to_s_fbio);
 
+/* Note: Not thread safe! */
+const BIO_METHOD *bio_f_tls_dump_filter(void);
+void bio_f_tls_dump_filter_free(void);
+
 #endif /* HEADER_SSLTESTLIB_H */