From 9be5f7d521d74929d4f04932b9c5d041ea5487ae Mon Sep 17 00:00:00 2001
From: Olivier Dion <olivier.dion@savoirfairelinux.com>
Date: Thu, 10 Jun 2021 15:12:00 -0400
Subject: [PATCH] fuzzing/gnutls: Add GNU TLS  wrapper

Change-Id: Ide37f73f142cb1cfa86b86c6ddbc76ce80e9414d
---
 test/fuzzing/Makefile.am    |   2 +-
 test/fuzzing/lib/gnutls.cpp | 312 ++++++++++++++++++++++++++++++++++++
 test/fuzzing/lib/gnutls.h   |  51 ++++++
 3 files changed, 364 insertions(+), 1 deletion(-)
 create mode 100644 test/fuzzing/lib/gnutls.cpp
 create mode 100644 test/fuzzing/lib/gnutls.h

diff --git a/test/fuzzing/Makefile.am b/test/fuzzing/Makefile.am
index 836be7f6d8..6c4737fa40 100644
--- a/test/fuzzing/Makefile.am
+++ b/test/fuzzing/Makefile.am
@@ -7,5 +7,5 @@ check_PROGRAMS =
 
 lib_LTLIBRARIES = libfuzz.la
 
-libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp
+libfuzz_la_SOURCES = lib/utils.cpp lib/supervisor.cpp lib/gnutls.cpp
 endif
diff --git a/test/fuzzing/lib/gnutls.cpp b/test/fuzzing/lib/gnutls.cpp
new file mode 100644
index 0000000000..d34a35f7d5
--- /dev/null
+++ b/test/fuzzing/lib/gnutls.cpp
@@ -0,0 +1,312 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include <cstdlib>
+#include <cinttypes>
+
+#include "lib/gnutls.h"
+#include "lib/supervisor.h"
+
+static FILE* redirect_to = nullptr;
+
+static std::atomic<size_t> packet_ID = 0;
+
+static void
+open_redirection(void)
+{
+    const char* supervisor_says = std::getenv(supervisor::env::tls_out);
+
+    if (nullptr == supervisor_says) {
+        redirect_to = stderr;
+    } else {
+        redirect_to = fopen(supervisor_says, "w");
+        assert(redirect_to);
+    }
+}
+
+static void
+print_channel_msg(const ChanneledMessage& msg, const char* direction, bool mutated, size_t packed_id)
+{
+    if (nullptr == redirect_to) {
+        open_redirection();
+    }
+
+    fprintf(redirect_to,
+            "\n"
+            "================================================================================\n"
+            "#:direction %s #:mut %d #:transport TLS #:channel %" PRIu16 " #:ID %" PRIu64   "\n"
+            "--------------------------------------------------------------------------------\n"
+            "%.*s\n"
+            "================================================================================\n",
+            direction,
+            mutated,
+            msg.channel,
+            packed_id,
+            static_cast<int>(msg.data.size()),
+            msg.data.data());
+
+    fflush(redirect_to);
+}
+
+#if 0
+static std::map<std::string;
+
+static void
+register_channel(const std::string& name, uint16_t channel)
+{
+
+}
+#endif
+
+__weak
+bool
+mutate_gnutls_record_send(ChanneledMessage& msg)
+{
+    (void) msg;
+
+    return false;
+}
+
+__weak
+bool
+mutate_gnutls_record_recv(ChanneledMessage& msg)
+{
+    (void) msg;
+
+    return false;
+}
+
+__weak
+void
+pack_gnutls_record_recv(msgpack::sbuffer& buf, const ChanneledMessage& msg)
+{
+    msgpack::packer<msgpack::sbuffer> pk(&buf);
+
+    pk.pack_array(2);
+    pk.pack(msg.channel);
+    pk.pack_bin(msg.data.size());
+    pk.pack_bin_body((const char*) msg.data.data(), msg.data.size());
+}
+
+__weak
+void
+pack_gnutls_record_send(msgpack::sbuffer& buf, const ChanneledMessage& msg)
+{
+    msgpack::packer<msgpack::sbuffer> pk(&buf);
+
+    pk.pack_array(2);
+    pk.pack(msg.channel);
+    pk.pack_bin(msg.data.size());
+    pk.pack_bin_body((const char*) msg.data.data(), msg.data.size());
+}
+
+BEGIN_WRAPPER(ssize_t, gnutls_record_send, gnutls_session_t session, const void* data, size_t data_size)
+{
+    if (data_size > 0) {
+
+        msgpack::unpacker pac {};
+        msgpack::object_handle oh;
+
+        pac.reserve_buffer(data_size);
+
+        memcpy(pac.buffer(), data, data_size);
+
+        pac.buffer_consumed(data_size);
+
+        if (pac.next(oh)) {
+
+                try {
+                        auto msg = oh.get().as<ChanneledMessage>();
+
+                        size_t ID = packet_ID++;
+
+                        bool mutated;
+
+                        print_channel_msg(msg, "TX", false, ID);
+
+                        mutated = mutate_gnutls_record_send(msg);
+
+                        if (not mutated) {
+                                goto no_mut;
+
+                        }
+
+                        print_channel_msg(msg, "TX", true, ID);
+
+                        msgpack::sbuffer buf(16 +msg.data.size());
+
+                        pack_gnutls_record_send(buf, msg);
+
+                        return this_func(session, buf.data(), buf.size());
+
+                } catch (...) { }
+        }
+    }
+
+no_mut:
+    return this_func(session, data, data_size);
+}
+END_WRAPPER();
+
+BEGIN_WRAPPER(ssize_t, gnutls_record_recv, gnutls_session_t session, void* data, size_t data_size)
+{
+    ssize_t ret = this_func(session, data, data_size);
+
+    if (ret > 0) {
+
+        msgpack::unpacker pac {};
+        msgpack::object_handle oh;
+
+        pac.reserve_buffer(ret);
+
+        memcpy(pac.buffer(), data, ret);
+
+        pac.buffer_consumed(ret);
+
+        if (pac.next(oh)) {
+
+            auto msg = oh.get().as<ChanneledMessage>();
+
+            size_t ID = packet_ID++;
+
+            bool mutated;
+#if 0
+            if (CONTROL_CHANNEL == msg.channel) {
+
+                    try {
+                            msgpack::unpacked result;
+                            msgpack::unpack(result, (const char*) msg.data.data(), msg.data.size(), 0);
+                            auto obj = result.get();
+
+                            auto req = obj.as<ChannelRequest>();
+
+                            if (ChannelRequestState::ACCEPT == req.state) {
+                                    register_channel(req.name, req.channel);
+                            }
+
+                    } catch (...) {
+
+                    }
+            }
+#endif
+            print_channel_msg(msg, "RX", false, ID);
+
+            mutated = mutate_gnutls_record_recv(msg);
+
+            if (not mutated) {
+                goto no_mut;
+            }
+
+            print_channel_msg(msg, "RX", true, ID);
+
+            msgpack::sbuffer buf(16 + msg.data.size());
+
+            pack_gnutls_record_recv(buf, msg);
+
+            memcpy(data, buf.data(), buf.size());
+
+            /* Respect GNU TLS API! */
+            assert(buf.size() <= data_size);
+
+            ret = buf.size();
+        }
+    }
+
+no_mut:
+    return ret;
+}
+END_WRAPPER();
+
+__weak
+void
+post_gnutls_init_hook(gnutls_session_t session)
+{
+    (void)session;
+}
+
+__weak
+void
+pre_gnutls_deinit_hook(gnutls_session_t session)
+{
+    (void)session;
+}
+
+BEGIN_WRAPPER(int, gnutls_init, gnutls_session_t * session, unsigned int flags)
+{
+        int ret;
+
+        ret = this_func(session, flags);
+
+        post_gnutls_init_hook(*session);
+
+        return ret;
+}
+END_WRAPPER();
+
+BEGIN_WRAPPER(ssize_t, gnutls_record_recv_seq, gnutls_session_t session, void * data, size_t data_size, unsigned char * seq)
+{
+    ssize_t ret = this_func(session, data, data_size, seq);
+
+    if (ret > 0) {
+
+        msgpack::unpacker pac {};
+        msgpack::object_handle oh;
+
+        pac.reserve_buffer(ret);
+
+        memcpy(pac.buffer(), data, ret);
+
+        pac.buffer_consumed(ret);
+
+        if (pac.next(oh)) {
+
+            auto msg = oh.get().as<ChanneledMessage>();
+
+            size_t ID = packet_ID++;
+
+            bool mutated;
+
+            print_channel_msg(msg, "RX", false, ID);
+
+            mutated = mutate_gnutls_record_recv(msg);
+
+            if (not mutated) {
+                goto no_mut;
+            }
+
+            print_channel_msg(msg, "RX", true, ID);
+
+            msgpack::sbuffer buf(16 + msg.data.size());
+
+            pack_gnutls_record_recv(buf, msg);
+
+            memcpy(data, buf.data(), buf.size());
+
+            /* Respect GNU TLS API! */
+            assert(buf.size() <= data_size);
+
+            ret = buf.size();
+        }
+    }
+
+no_mut:
+    return ret;
+}
+END_WRAPPER();
diff --git a/test/fuzzing/lib/gnutls.h b/test/fuzzing/lib/gnutls.h
new file mode 100644
index 0000000000..a9fe781236
--- /dev/null
+++ b/test/fuzzing/lib/gnutls.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (C) 2021 Savoir-faire Linux Inc.
+ *
+ *  Author: Olivier Dion <olivier.dion>@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#pragma once
+
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+
+#include <msgpack.hpp>
+
+/* TODO - Import this from jami */
+struct ChanneledMessage
+{
+    uint16_t channel;
+    std::vector<uint8_t> data;
+    MSGPACK_DEFINE(channel, data)
+};
+
+extern "C" {
+
+extern void inject_into_gnutls(const ChanneledMessage& msg);
+
+extern void post_gnutls_init_hook(gnutls_session_t session);
+extern void pre_gnutls_deinit_hook(gnutls_session_t session);
+
+extern void pre_mutate_gnutls_record_send_hook(const ChanneledMessage& msg);
+extern void post_mutate_gnutls_record_send_hook(const ChanneledMessage& msg, bool hasMutated);
+extern bool mutate_gnutls_record_send(ChanneledMessage& msg);
+
+extern void pre_mutate_gnutls_record_recv_hook(const ChanneledMessage& msg);
+extern void post_mutate_gnutls_record_recv_hook(const ChanneledMessage& msg, bool hasMutated);
+extern bool mutate_gnutls_record_recv(ChanneledMessage& msg);
+
+};
-- 
GitLab