From e1f47bbd07a149f49d3d25a34af5dade1ea4bd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Mon, 13 Mar 2017 14:28:39 +0100 Subject: [PATCH 1/6] net: Made an almost general stream interface (based on TCP) --- api/net/stream.hpp | 191 +++++++++++++++++++++++++++++++++++++ api/net/tcp/connection.hpp | 110 ++++++++++----------- 2 files changed, 240 insertions(+), 61 deletions(-) create mode 100644 api/net/stream.hpp diff --git a/api/net/stream.hpp b/api/net/stream.hpp new file mode 100644 index 0000000000..4afa3b9b57 --- /dev/null +++ b/api/net/stream.hpp @@ -0,0 +1,191 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef NET_STREAM_HPP +#define NET_STREAM_HPP + +#include +#include +#include +#include +#include "tcp/socket.hpp" + +namespace net { + class Stream; + using Stream_ptr = std::unique_ptr; + /** + * @brief An abstract network Stream interface based on TCP. + */ + class Stream { + public: + using buffer_t = std::shared_ptr; + using ptr = Stream_ptr; + + /** Called when the stream is ready to be used. */ + using ConnectCallback = delegate; + /** + * @brief Event when the stream is connected/established/ready to be used. + * + * @param[in] cb The connect callback + */ + virtual void on_connect(ConnectCallback cb) = 0; + + /** Called with a shared buffer and the length of the data when received. */ + using ReadCallback = delegate; + /** + * @brief Event when data is received. + * + * @param[in] n The size of the receive buffer + * @param[in] cb The read callback + */ + virtual void on_read(size_t n, ReadCallback cb) = 0; + + /** Called with nothing ¯\_(ツ)_/¯ */ + using CloseCallback = delegate; + /** + * @brief Event for when the Stream is being closed. + * + * @param[in] cb The close callback + */ + virtual void on_close(CloseCallback cb) = 0; + + /** Called with the number of bytes written. */ + using WriteCallback = delegate; + /** + * @brief Event for when data has been written. + * + * @param[in] cb The write callback + */ + virtual void on_write(WriteCallback cb) = 0; + + /** + * @brief Async write of a data with a length. + * + * @param[in] buf data + * @param[in] n length + */ + virtual void write(const void* buf, size_t n) = 0; + + /** + * @brief Async write of a chunk. + * + * @param[in] c A chunk + */ + virtual void write(Chunk c) = 0; + + /** + * @brief Async write of a shared buffer with a length. + * + * @param[in] buffer shared buffer + * @param[in] n length + */ + virtual void write(buffer_t buf, size_t n) = 0; + + /** + * @brief Async write of a string. + * + * @param[in] str The string + */ + virtual void write(const std::string& str) = 0; + + /** + * @brief Closes the stream. + */ + virtual void close() = 0; + + /** + * @brief Aborts (terminates) the stream. + */ + virtual void abort() = 0; + + /** + * @brief Resets all callbacks. + */ + virtual void reset_callbacks() = 0; + + /** + * @brief Returns the streams local socket. + * + * @return A TCP Socket + */ + virtual tcp::Socket local() const = 0; + + /** + * @brief Returns the streams remote socket. + * + * @return A TCP Socket + */ + virtual tcp::Socket remote() const = 0; + + /** + * @brief Returns the local port. + * + * @return A TCP port + */ + virtual uint16_t local_port() const = 0; + + /** + * @brief Returns a string representation of the stream. + * + * @return String representation of the stream. + */ + virtual std::string to_string() const = 0; + + /** + * @brief Determines if connected (established). + * + * @return True if connected, False otherwise. + */ + virtual bool is_connected() const noexcept = 0; + + /** + * @brief Determines if writable. (write is allowed) + * + * @return True if writable, False otherwise. + */ + virtual bool is_writable() const noexcept = 0; + + /** + * @brief Determines if readable. (data can be received) + * + * @return True if readable, False otherwise. + */ + virtual bool is_readable() const noexcept = 0; + + /** + * @brief Determines if closing. + * + * @return True if closing, False otherwise. + */ + virtual bool is_closing() const noexcept = 0; + + /** + * @brief Determines if closed. + * + * @return True if closed, False otherwise. + */ + virtual bool is_closed() const noexcept = 0; + + Stream() = default; + virtual ~Stream() {} + + }; // < class Stream + +} // < namespace net + +#endif // < NET_STREAM_HPP diff --git a/api/net/tcp/connection.hpp b/api/net/tcp/connection.hpp index 576b74b839..0a271cd0ef 100644 --- a/api/net/tcp/connection.hpp +++ b/api/net/tcp/connection.hpp @@ -1,6 +1,6 @@ // This file is a part of the IncludeOS unikernel - www.includeos.org // -// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences +// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences // and Alfred Bratterud // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ #include #include +#include namespace net { // Forward declaration of the TCP object @@ -221,7 +222,7 @@ class Connection : public std::enable_shared_from_this { * @brief Exposes a TCP Connection as a Stream with only the most necessary features. * May be overrided by extensions like TLS etc for additional functionality. */ - class Stream { + class Stream : public net::Stream { public: /** * @brief Construct a Stream for a Connection ptr @@ -229,53 +230,45 @@ class Connection : public std::enable_shared_from_this { * @param[in] conn The connection */ Stream(Connection_ptr conn) - : ptr{std::move(conn)} + : tcp{std::move(conn)} {} - /** Called when the stream is ready to use. */ - using ConnectCallback = delegate; /** * @brief Event when the stream is connected/established/ready to use. * * @param[in] cb The connect callback */ - virtual void on_connect(ConnectCallback cb) + virtual void on_connect(ConnectCallback cb) override { - ptr->on_connect(Connection::ConnectCallback::make_packed( + tcp->on_connect(Connection::ConnectCallback::make_packed( [this, cb] (Connection_ptr) { cb(*this); })); } - /** Called with a shared buffer and the length of the data when received. */ - using ReadCallback = delegate; /** * @brief Event when data is received. * * @param[in] n The size of the receive buffer * @param[in] cb The read callback */ - virtual void on_read(size_t n, ReadCallback cb) - { ptr->on_read(n, cb); } + virtual void on_read(size_t n, ReadCallback cb) override + { tcp->on_read(n, cb); } - /** Called with nothing ¯\_(ツ)_/¯ */ - using CloseCallback = delegate; /** * @brief Event for when the Stream is being closed. * * @param[in] cb The close callback */ - virtual void on_close(CloseCallback cb) - { ptr->on_close(cb); } + virtual void on_close(CloseCallback cb) override + { tcp->on_close(cb); } - /** Called with the number of bytes written. */ - using WriteCallback = delegate; /** * @brief Event for when data has been written. * * @param[in] cb The write callback */ - virtual void on_write(WriteCallback cb) - { ptr->on_write(cb); } + virtual void on_write(WriteCallback cb) override + { tcp->on_write(cb); } /** * @brief Async write of a data with a length. @@ -283,16 +276,16 @@ class Connection : public std::enable_shared_from_this { * @param[in] buf data * @param[in] n length */ - virtual void write(const void* buf, size_t n) - { ptr->write(buf, n); } + virtual void write(const void* buf, size_t n) override + { tcp->write(buf, n); } /** * @brief Async write of a chunk. * * @param[in] c A chunk */ - virtual void write(Chunk c) - { ptr->write(c); } + virtual void write(Chunk c) override + { tcp->write(c); } /** * @brief Async write of a shared buffer with a length. @@ -301,8 +294,8 @@ class Connection : public std::enable_shared_from_this { * @param[in] buffer shared buffer * @param[in] n length */ - virtual void write(buffer_t buf, size_t n) - { ptr->write(buf, n); } + virtual void write(buffer_t buf, size_t n) override + { tcp->write(buf, n); } /** * @brief Async write of a string. @@ -310,110 +303,103 @@ class Connection : public std::enable_shared_from_this { * * @param[in] str The string */ - void write(const std::string& str) + virtual void write(const std::string& str) override { write(str.data(), str.size()); } /** * @brief Closes the stream. */ - virtual void close() - { ptr->close(); } + virtual void close() override + { tcp->close(); } /** * @brief Aborts (terminates) the stream. */ - virtual void abort() - { ptr->abort(); } + virtual void abort() override + { tcp->abort(); } /** * @brief Resets all callbacks. */ - virtual void reset_callbacks() - { ptr->reset_callbacks(); } + virtual void reset_callbacks() override + { tcp->reset_callbacks(); } /** * @brief Returns the streams local socket. * * @return A TCP Socket */ - tcp::Socket local() const - { return ptr->local(); } + tcp::Socket local() const override + { return tcp->local(); } /** * @brief Returns the streams remote socket. * * @return A TCP Socket */ - tcp::Socket remote() const - { return ptr->remote(); } + tcp::Socket remote() const override + { return tcp->remote(); } /** * @brief Returns the local port. * * @return A TCP port */ - uint16_t local_port() const - { return ptr->local_port(); } + uint16_t local_port() const override + { return tcp->local_port(); } /** * @brief Returns a string representation of the stream. * * @return String representation of the stream. */ - virtual std::string to_string() const noexcept - { return ptr->to_string(); } + virtual std::string to_string() const override + { return tcp->to_string(); } /** * @brief Determines if connected (established). * * @return True if connected, False otherwise. */ - virtual bool is_connected() const noexcept - { return ptr->is_connected(); } + virtual bool is_connected() const noexcept override + { return tcp->is_connected(); } /** * @brief Determines if writable. (write is allowed) * * @return True if writable, False otherwise. */ - virtual bool is_writable() const noexcept - { return ptr->is_writable(); } + virtual bool is_writable() const noexcept override + { return tcp->is_writable(); } /** * @brief Determines if readable. (data can be received) * * @return True if readable, False otherwise. */ - virtual bool is_readable() const noexcept - { return ptr->is_readable(); } + virtual bool is_readable() const noexcept override + { return tcp->is_readable(); } /** * @brief Determines if closing. * * @return True if closing, False otherwise. */ - virtual bool is_closing() const noexcept - { return ptr->is_closing(); } + virtual bool is_closing() const noexcept override + { return tcp->is_closing(); } /** * @brief Determines if closed. * * @return True if closed, False otherwise. */ - virtual bool is_closed() const noexcept - { return ptr->is_closed(); }; + virtual bool is_closed() const noexcept override + { return tcp->is_closed(); }; - protected: - /** - * @brief Returns the underlying TCP connection. - * - * @return A TCP Connection ptr - */ - Connection_ptr tcp() - { return ptr; }; + virtual ~Stream() {} - private: - Connection_ptr ptr; + protected: + Connection_ptr tcp; }; // < class Connection::Stream @@ -1361,8 +1347,10 @@ class Connection : public std::enable_shared_from_this { }; // < class Connection -} // < namespace net +using Stream = Connection::Stream; + } // < namespace tcp +} // < namespace net #include "connection.inc" From 538537501ad1206597e6d15d4877e43214c3a0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Tue, 14 Mar 2017 12:00:22 +0100 Subject: [PATCH 2/6] examples: Fixed bug in demo_service --- examples/demo_service/service.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/demo_service/service.cpp b/examples/demo_service/service.cpp index 421e9ceb26..a0248a8cf9 100644 --- a/examples/demo_service/service.cpp +++ b/examples/demo_service/service.cpp @@ -115,8 +115,9 @@ void Service::start(const std::string&) printf(" @on_read: %u bytes received.\n", n); try { + std::string data{(const char*)buf.get(), n}; // try to parse the request - http::Request req{(const char*)buf.get(), n}; + http::Request req{data}; // handle the request, getting a matching response auto res = handle_request(req); @@ -126,9 +127,9 @@ void Service::start(const std::string&) conn->write(res); } - catch(...) + catch(const std::exception& e) { - printf(" Unable to parse request.\n"); + printf(" Unable to parse request:\n%s\n", e.what()); } }); conn->on_write([](size_t written) { From 95451521007a872720cb858873284dbeb8f8f0c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Tue, 14 Mar 2017 13:43:35 +0100 Subject: [PATCH 3/6] build: Download and install precompiled bundle of botan --- CMakeLists.txt | 3 +++ cmake/botan.cmake | 25 +++++++++++++++++++++++++ src/CMakeLists.txt | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 cmake/botan.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f9f5a28592..bac35a4299 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -120,6 +120,9 @@ if(diskbuilder) ) endif(diskbuilder) +# Botan Crypto & TLS +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/botan.cmake) + option(examples "Build example unikernels in /examples" OFF) if(examples) add_subdirectory(examples) diff --git a/cmake/botan.cmake b/cmake/botan.cmake new file mode 100644 index 0000000000..1f2a8f0b33 --- /dev/null +++ b/cmake/botan.cmake @@ -0,0 +1,25 @@ +# Download and install precompiled bundle of botan (for includeos) +# https://github.com/randombit/botan + +include(ExternalProject) +ExternalProject_Add(botan + PREFIX botan + URL https://github.com/includeos/botan/releases/download/inc-2.0/botan-includeos.tar.gz + URL_HASH MD5=142801a69fd87b65dae12d395ef2a995 + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + UPDATE_COMMAND "" + INSTALL_COMMAND "" +) + + +set(BOTAN_DIR ${CMAKE_CURRENT_BINARY_DIR}/botan/src/botan) +set(BOTAN_INCLUDE ${BOTAN_DIR}/botan) +set(BOTAN_LIB ${BOTAN_DIR}/libbotan-2.a) + +add_library(libbotan STATIC IMPORTED) +#add_dependencies(libcxx PrecompiledLibraries) +set_target_properties(libbotan PROPERTIES IMPORTED_LOCATION ${BOTAN_LIB}) + +install(FILES ${BOTAN_LIB} DESTINATION includeos/lib) +install(DIRECTORY ${BOTAN_INCLUDE} DESTINATION includeos/include) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4cff5665a..6a7680c6d3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,7 +69,7 @@ set(OS_OBJECTS ) add_library(os STATIC ${OS_OBJECTS} apic_boot.o) -add_dependencies(os PrecompiledLibraries) +add_dependencies(os PrecompiledLibraries botan) file(GLOB CXX_ABI crt/cxxabi/*.cpp) From 7c053f2512f9e0257c26346b9b15991d5f9efd62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Tue, 14 Mar 2017 13:57:08 +0100 Subject: [PATCH 4/6] build: Update botan bundle --- cmake/botan.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/botan.cmake b/cmake/botan.cmake index 1f2a8f0b33..6049934971 100644 --- a/cmake/botan.cmake +++ b/cmake/botan.cmake @@ -5,7 +5,7 @@ include(ExternalProject) ExternalProject_Add(botan PREFIX botan URL https://github.com/includeos/botan/releases/download/inc-2.0/botan-includeos.tar.gz - URL_HASH MD5=142801a69fd87b65dae12d395ef2a995 + URL_HASH MD5=952980834c93e61dc836bb7c400f4d57 CONFIGURE_COMMAND "" BUILD_COMMAND "" UPDATE_COMMAND "" From 6c1a7cb828b6e120e35a1c3e76db7eed3baa070c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85kesson?= Date: Tue, 14 Mar 2017 14:13:52 +0100 Subject: [PATCH 5/6] build: Updated botan bundle with system_rng --- cmake/botan.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/botan.cmake b/cmake/botan.cmake index 6049934971..07729a2573 100644 --- a/cmake/botan.cmake +++ b/cmake/botan.cmake @@ -5,7 +5,7 @@ include(ExternalProject) ExternalProject_Add(botan PREFIX botan URL https://github.com/includeos/botan/releases/download/inc-2.0/botan-includeos.tar.gz - URL_HASH MD5=952980834c93e61dc836bb7c400f4d57 + URL_HASH MD5=5ef7f26047f8fe17219f62755938621d CONFIGURE_COMMAND "" BUILD_COMMAND "" UPDATE_COMMAND "" From 68efdf656aa27803df66f3d4e7c34407cc373c2b Mon Sep 17 00:00:00 2001 From: Gonzo Date: Tue, 14 Mar 2017 14:16:58 +0100 Subject: [PATCH 6/6] Add Botan, TLS stream and Secure HTTP server --- CMakeLists.txt | 7 +- api/https | 27 +++++ api/memdisk | 8 ++ api/net/http/secure_server.hpp | 72 ++++++++++++++ api/net/http/server.hpp | 13 ++- api/net/tls/credman.hpp | 148 +++++++++++++++++++++++++++ api/net/tls/server.hpp | 176 +++++++++++++++++++++++++++++++++ etc/service.cmake | 5 + src/CMakeLists.txt | 3 +- src/net/http/secure_server.cpp | 62 ++++++++++++ src/net/http/server.cpp | 5 +- 11 files changed, 515 insertions(+), 11 deletions(-) create mode 100644 api/https create mode 100644 api/net/http/secure_server.hpp create mode 100644 api/net/tls/credman.hpp create mode 100644 api/net/tls/server.hpp create mode 100644 src/net/http/secure_server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bac35a4299..2772863a4d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,10 @@ set(CMAKE_C_FLAGS "-target i686 -MMD ${CAPABS} ${WARNS} -nostdlib -nostdlibinc - option(from_bundle "Download and use pre-compiled libraries for cross-comilation" ON) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cross_compiled_libraries.txt) +# Botan Crypto & TLS +# Note: Include order matters! +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/botan.cmake) + # # Subprojects # @@ -120,9 +124,6 @@ if(diskbuilder) ) endif(diskbuilder) -# Botan Crypto & TLS -include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/botan.cmake) - option(examples "Build example unikernels in /examples" OFF) if(examples) add_subdirectory(examples) diff --git a/api/https b/api/https new file mode 100644 index 0000000000..95d0fd3450 --- /dev/null +++ b/api/https @@ -0,0 +1,27 @@ +// -*- C++ -*- +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015-2016 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef API_HTTPS_HEADER +#define API_HTTPS_HEADER + +#include "net/http/response.hpp" +#include "net/http/request.hpp" +#include "net/http/secure_server.hpp" + +#endif diff --git a/api/memdisk b/api/memdisk index 724f85e5bb..39811711f6 100644 --- a/api/memdisk +++ b/api/memdisk @@ -25,7 +25,15 @@ namespace fs { + // new singleton interface for the memdisk + inline Disk& memdisk() + { + static MemDisk device; + static Disk disk {device}; + return disk; + } // new_shared_memdisk() very likely contains FAT + // Note: deprecated! inline Disk_ptr new_shared_memdisk() { static MemDisk device; diff --git a/api/net/http/secure_server.hpp b/api/net/http/secure_server.hpp new file mode 100644 index 0000000000..11886d6252 --- /dev/null +++ b/api/net/http/secure_server.hpp @@ -0,0 +1,72 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2016-2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef NET_HTTP_SECURE_SERVER_HPP +#define NET_HTTP_SECURE_SERVER_HPP + +#include +#include +#include + +namespace http { + +class Secure_server : public http::Server +{ +public: + Secure_server( + fs::Dirent& ca_key, + fs::Dirent& ca_cert, + fs::Dirent& server_key, + TCP& tcp, + Request_handler cb + ); + + Secure_server( + Botan::Credentials_Manager* in_credman, + Botan::RandomNumberGenerator& in_rng, + TCP& tcp, + Request_handler cb) + : http::Server(tcp, cb), rng(in_rng), credman(in_credman) + { + on_connect = {this, &Secure_server::secure_connect}; + } + + void secure_connect(TCP_conn conn) + { + auto* ptr = new net::tls::Server(conn, rng, *credman); + + ptr->on_connect( + [this, ptr] (net::Stream&) + { + // create and pass TLS socket + Server::connect(std::unique_ptr(ptr)); + }); + ptr->on_close([ptr] { + printf("Secure_HTTP::on_close on %s\n", ptr->to_string().c_str()); + delete ptr; + }); + } + +private: + Botan::RandomNumberGenerator& rng; + std::unique_ptr credman; +}; + +} // http + +#endif diff --git a/api/net/http/server.hpp b/api/net/http/server.hpp index 580b49e224..b796649a33 100644 --- a/api/net/http/server.hpp +++ b/api/net/http/server.hpp @@ -90,7 +90,11 @@ namespace http { */ Response_ptr create_response(status_t code = http::OK) const; - ~Server(); + virtual ~Server(); + + protected: + delegate on_connect; + void connect(Connection::Stream_ptr stream); private: friend class Server_connection; @@ -109,10 +113,9 @@ namespace http { Stat& stat_req_bad_; Stat& stat_timeouts_; - void connect(TCP_conn conn) - { connect(std::make_unique(conn)); } - - void connect(Connection::Stream_ptr stream); + void connected(TCP_conn conn) { + connect(std::make_unique(conn)); + } void close(Server_connection&); diff --git a/api/net/tls/credman.hpp b/api/net/tls/credman.hpp new file mode 100644 index 0000000000..91a06d83c9 --- /dev/null +++ b/api/net/tls/credman.hpp @@ -0,0 +1,148 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef NET_TLS_CREDMAN_HPP +#define NET_TLS_CREDMAN_HPP + +#include +#include +#include +#include +#include +//#include +#include +#include + +namespace net +{ +typedef std::chrono::duration> years; + +class Credman : public Botan::Credentials_Manager +{ +public: + Credman(const Botan::X509_Certificate server_cert, + const Botan::X509_Certificate ca_cert, + std::unique_ptr server_key) : + m_server_cert(std::move(server_cert)), + m_ca_cert(std::move(ca_cert)), + m_server_key(std::move(server_key)) + { + std::unique_ptr store(new Botan::Certificate_Store_In_Memory(m_ca_cert)); + m_stores.push_back(std::move(store)); + m_provides_client_certs = false; + } + + std::vector + trusted_certificate_authorities(const std::string&, + const std::string&) override + { + std::vector v; + for (auto&& store : m_stores) + v.push_back(store.get()); + return v; + } + + std::vector cert_chain( + const std::vector& cert_key_types, + const std::string& type, + const std::string&) override + { + std::vector chain; + + if (type == "tls-server" || (type == "tls-client" && m_provides_client_certs)) + { + bool have_match = false; + for (size_t i = 0; i != cert_key_types.size(); ++i) + if(cert_key_types[i] == m_server_key->algo_name()) + have_match = true; + + if(have_match) + { + chain.push_back(m_server_cert); + chain.push_back(m_ca_cert); + } + } + return chain; + } + + Botan::Private_Key* private_key_for(const Botan::X509_Certificate&, + const std::string&, + const std::string&) override + { + return m_server_key.get(); + } + + Botan::SymmetricKey psk(const std::string&, + const std::string&, + const std::string&) override + { + //if (type == "tls-server" && context == "session-ticket") + // return Botan::SymmetricKey("AABBCCDDEEFF012345678012345678"); + + return Botan::SymmetricKey("20B602D1475F2DF888FCB60D2AE03AFD"); // PSK key + } + + static Credman* create( + Botan::RandomNumberGenerator& rng, + std::unique_ptr ca_key, + Botan::X509_Certificate ca_cert, + std::unique_ptr server_key); + +public: + Botan::X509_Certificate m_server_cert, m_ca_cert; + std::unique_ptr m_server_key; + std::vector> m_stores; + bool m_provides_client_certs; +}; + +/** + * 3. create private key 2 + * 4. create certificate request with private key 2 + * 5. create CA with key and cert + * 6, create certificate by signing + * +**/ +inline Credman* Credman::create( + Botan::RandomNumberGenerator& rng, + std::unique_ptr ca_key, + Botan::X509_Certificate ca_cert, + std::unique_ptr server_key) +{ + Botan::X509_CA ca(ca_cert, *ca_key, "SHA-256", rng); + + // create server certificate from CA + auto now = std::chrono::system_clock::now(); + Botan::X509_Time start_time(now); + Botan::X509_Time end_time(now + years(1)); + + // create certificate request + Botan::X509_Cert_Options server_opts; + server_opts.common_name = "server.example.com"; + server_opts.country = "VT"; + + auto req = Botan::X509::create_cert_req(server_opts, *server_key, "SHA-256", rng); + + auto server_cert = ca.sign_request(req, rng, start_time, end_time); + + // create credentials manager + return new Credman(server_cert, ca_cert, std::move(server_key)); +} + +} // net + +#endif diff --git a/api/net/tls/server.hpp b/api/net/tls/server.hpp new file mode 100644 index 0000000000..1c49745ed3 --- /dev/null +++ b/api/net/tls/server.hpp @@ -0,0 +1,176 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015-2017 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#ifndef NET_TLS_SERVER_STREAM_HPP +#define NET_TLS_SERVER_STREAM_HPP + +#include +#include +#include +#include +#include +#include + +namespace net +{ +namespace tls +{ +class Server : public Botan::TLS::Callbacks, public tcp::Stream +{ +public: + using Connection_ptr = tcp::Connection_ptr; + + + Server(Connection_ptr remote, + Botan::RandomNumberGenerator& rng, + Botan::Credentials_Manager& credman) : + tcp::Stream({remote}), + m_rng(rng), + m_creds(credman), + m_session_manager(m_rng), + m_tls(*this, m_session_manager, m_creds, m_policy, m_rng) + { + assert(tcp->is_connected()); + // default read callback + tcp->on_read(4096, {this, &Server::tls_read}); + } + + void on_read(size_t bs, ReadCallback cb) override + { + tcp->on_read(bs, {this, &Server::tls_read}); + this->o_read = cb; + } + void on_write(WriteCallback cb) override + { + this->o_write = cb; + } + void on_connect(ConnectCallback cb) override + { + this->o_connect = cb; + } + + void write(const void* buf, size_t n) override + { + m_tls.send((uint8_t*) buf, n); + } + void write(const std::string& str) override + { + this->write(str.data(), str.size()); + } + void write(Chunk ch) override + { + m_tls.send(ch.data(), ch.size()); + } + void write(buffer_t buf, size_t n) override + { + m_tls.send(buf.get(), n); + } + + std::string to_string() const override { + return tcp->to_string(); + } + + void reset_callbacks() override + { + o_connect = nullptr; + o_read = nullptr; + o_write = nullptr; + tcp->reset_callbacks(); + } + +protected: + void tls_read(buffer_t buf, const size_t n) + { + this->tls_receive(buf.get(), n); + } + + void tls_receive(const uint8_t* buf, const size_t n) + { + try + { + int rem = m_tls.received_data(buf, n); + (void) rem; + //printf("Finished processing (rem: %u)\n", rem); + } + catch(Botan::Exception& e) + { + printf("Fatal TLS error %s\n", e.what()); + this->close(); + } + catch(...) + { + printf("Unknown error!\n"); + this->close(); + } + } + + void tls_alert(Botan::TLS::Alert alert) override + { + // ignore close notifications + if (alert.type() != Botan::TLS::Alert::CLOSE_NOTIFY) + { + printf("Got a %s alert: %s\n", + (alert.is_fatal() ? "fatal" : "warning"), + alert.type_string().c_str()); + } + } + + bool tls_session_established(const Botan::TLS::Session&) override + { + // return true to store session + return true; + } + + void tls_emit_data(const uint8_t buf[], size_t len) override + { + tcp->write(buf, len); + } + + void tls_record_received(uint64_t, const uint8_t buf[], size_t buf_len) override + { + if (o_read) + { + auto buffff = std::shared_ptr (new uint8_t[buf_len]); + memcpy(buffff.get(), buf, buf_len); + + o_read(buffff, buf_len); + } + } + + void tls_session_activated() override + { + if (o_connect) o_connect(*this); + } + +private: + Stream::ReadCallback o_read; + Stream::WriteCallback o_write; + Stream::ConnectCallback o_connect; + + Botan::RandomNumberGenerator& m_rng; + Botan::Credentials_Manager& m_creds; + Botan::TLS::Strict_Policy m_policy; + Botan::TLS::Session_Manager_In_Memory m_session_manager; + + Botan::TLS::Server m_tls; +}; + +} // tls +} // net + +#endif diff --git a/etc/service.cmake b/etc/service.cmake index b759e48df1..71edb261a4 100644 --- a/etc/service.cmake +++ b/etc/service.cmake @@ -194,6 +194,10 @@ add_library(libos STATIC IMPORTED) set_target_properties(libos PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(libos PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/includeos/lib/libos.a) +add_library(libbotan STATIC IMPORTED) +set_target_properties(libbotan PROPERTIES LINKER_LANGUAGE CXX) +set_target_properties(libbotan PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/includeos/lib/libbotan-2.a) + add_library(libosdeps STATIC IMPORTED) set_target_properties(libosdeps PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(libosdeps PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/includeos/lib/libosdeps.a) @@ -288,6 +292,7 @@ set_target_properties(crtn PROPERTIES IMPORTED_LOCATION $ENV{INCLUDEOS_PREFIX}/i # all the OS and C/C++ libraries + crt end target_link_libraries(service libos + libbotan libosdeps libcxx cxxabi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6a7680c6d3..cb9809f961 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories(${INCLUDEOS_ROOT}/api) include_directories(${INCLUDEOS_ROOT}/mod/) include_directories(${INCLUDEOS_ROOT}/mod/GSL/) include_directories(${INCLUDEOS_ROOT}/mod/uzlib/src) # tinf.h for tar +include_directories(${BOTAN_DIR}) add_custom_command( OUTPUT apic_boot.o @@ -50,7 +51,7 @@ set(OS_OBJECTS net/super_stack.cpp net/http/header.cpp net/http/header_fields.cpp net/http/message.cpp net/http/request.cpp net/http/response.cpp net/http/status_codes.cpp net/http/time.cpp net/http/version.cpp - net/http/mime_types.cpp net/http/cookie.cpp + net/http/mime_types.cpp net/http/cookie.cpp net/http/secure_server.cpp net/http/client_connection.cpp net/http/client.cpp net/http/server_connection.cpp net/http/server.cpp net/http/response_writer.cpp fs/disk.cpp fs/filesystem.cpp fs/mbr.cpp fs/path.cpp diff --git a/src/net/http/secure_server.cpp b/src/net/http/secure_server.cpp new file mode 100644 index 0000000000..46fb12c99a --- /dev/null +++ b/src/net/http/secure_server.cpp @@ -0,0 +1,62 @@ +// This file is a part of the IncludeOS unikernel - www.includeos.org +// +// Copyright 2015 Oslo and Akershus University College of Applied Sciences +// and Alfred Bratterud +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//#include +#include +#include +#include + +inline static auto& get_rng() { return Botan::system_rng(); } + +inline std::unique_ptr read_pkey(fs::Dirent& key_file) +{ + assert(key_file.is_file()); + Botan::DataSource_Memory data{key_file.read()}; + return std::unique_ptr(Botan::PKCS8::load_key(data, get_rng())); +} + +namespace http +{ + Secure_server::Secure_server( + fs::Dirent& file_ca_key, + fs::Dirent& file_ca_cert, + fs::Dirent& file_server_key, + TCP& tcp, + Request_handler cb) + : http::Server(tcp, cb), + rng(get_rng()) + { + on_connect = {this, &Secure_server::secure_connect}; + + // load CA certificate + assert(file_ca_cert.is_valid()); + auto ca_cert = file_ca_cert.read(); + std::vector vca_cert(ca_cert.begin(), ca_cert.end()); + // load CA private key + auto ca_key = read_pkey(file_ca_key); + // load server private key + auto srv_key = read_pkey(file_server_key); + + auto* credman = net::Credman::create( + get_rng(), + std::move(ca_key), + Botan::X509_Certificate(vca_cert), + std::move(srv_key)); + + this->credman.reset(credman); + } +} diff --git a/src/net/http/server.cpp b/src/net/http/server.cpp index cc3dc42729..bbef310838 100644 --- a/src/net/http/server.cpp +++ b/src/net/http/server.cpp @@ -22,7 +22,8 @@ namespace http { const Server::idle_duration Server::DEFAULT_IDLE_TIMEOUT{std::chrono::seconds(60)}; Server::Server(TCP& tcp, Request_handler cb, idle_duration timeout) - : tcp_(tcp), + : on_connect{this, &Server::connected}, + tcp_(tcp), on_request_(std::move(cb)), keep_alive_(true), timer_id_(Timers::UNUSED_ID), @@ -38,7 +39,7 @@ namespace http { { Expects(on_request_ != nullptr); - tcp_.bind(port).on_connect({this, &Server::connect}); + tcp_.bind(port).on_connect(this->on_connect); INFO("HTTP Server", "Listening on port %u", port); using namespace std::chrono;