Skip to content

Commit

Permalink
Added TLS API, and an mbedTLS-based implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
snej committed Sep 10, 2019
1 parent 7fefb21 commit e297281
Show file tree
Hide file tree
Showing 5 changed files with 1,095 additions and 2 deletions.
114 changes: 114 additions & 0 deletions include/sockpp/mbedtls_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* @file mbedtls_socket.h
*
* TLS (SSL) socket implementation using mbedTLS.
*
* @author Jens Alfke
* @author Couchbase, Inc.
* @author www.couchbase.com
*
* @date August 2019
*/

// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2014-2019 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// --------------------------------------------------------------------------

#ifndef __sockpp_mbedtls_socket_h
#define __sockpp_mbedtls_socket_h

#include "sockpp/tls_context.h"
#include "sockpp/tls_socket.h"
#include <memory>
#include <string>

struct mbedtls_pk_context;
struct mbedtls_ssl_config;
struct mbedtls_x509_crt;

namespace sockpp {

/**
* A concrete implementation of \ref tls_context, using the mbedTLS library.
* You probably don't want to use this class directly, unless you want to instantiate a
* custom context so you can have different contexts for different sockets.
*/
class mbedtls_context : public tls_context {
public:
mbedtls_context(role_t = CLIENT);
~mbedtls_context() override;

void set_root_certs(const std::string &certData) override;
void allow_invalid_peer_certs(bool) override;
void allow_only_certificate(const std::string &certData) override;

void allow_only_certificate(mbedtls_x509_crt *certificate);

/**
* Sets the identity certificate and private key using mbedTLS objects.
*/
void set_identity(mbedtls_x509_crt *certificate, mbedtls_pk_context *private_key);

void set_identity(const std::string &certificate_data,
const std::string &private_key_data) override;

void set_identity_files(const std::string &certificate_file,
const std::string &private_key_file,
const std::string &private_key_password) override;

std::unique_ptr<tls_socket> wrap_socket(std::unique_ptr<stream_socket> socket,
role_t,
const std::string &peer_name) override;

role_t role();

static mbedtls_x509_crt* get_system_root_certs();

private:
struct cert;

int verify_callback(mbedtls_x509_crt *crt, int depth, uint32_t *flags);
static std::unique_ptr<cert> parse_cert(const std::string &cert_data);

std::unique_ptr<mbedtls_ssl_config> ssl_config_;
std::unique_ptr<cert> root_certs_;
std::unique_ptr<cert> pinned_cert_;

static cert *s_system_root_certs;

friend class mbedtls_socket;
};

}

#endif
4 changes: 2 additions & 2 deletions include/sockpp/socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ class socket
* @param on Whether to turn non-blocking mode on or off.
* @return @em true on success, @em false on failure.
*/
bool set_non_blocking(bool on=true);
virtual bool set_non_blocking(bool on=true);
/**
* Gets a string describing the specified error.
* This is typically the returned message from the system strerror().
Expand Down Expand Up @@ -446,7 +446,7 @@ class socket
* @li SHUT_RDWR (2) Further reads and writes disallowed.
* @return @em true on success, @em false on error.
*/
bool shutdown(int how=SHUT_RDWR);
virtual bool shutdown(int how=SHUT_RDWR);
/**
* Closes the socket.
* After closing the socket, the handle is @em invalid, and can not be
Expand Down
158 changes: 158 additions & 0 deletions include/sockpp/tls_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* @file tls_context.h
*
* Context object for TLS (SSL) sockets.
*
* @author Jens Alfke
* @author Couchbase, Inc.
* @author www.couchbase.com
*
* @date August 2019
*/

// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2014-2019 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// --------------------------------------------------------------------------

#ifndef __sockpp_tls_context_h
#define __sockpp_tls_context_h

#include "sockpp/platform.h"
#include <memory>
#include <string>

namespace sockpp {
class connector;
class stream_socket;
class tls_socket;

/**
* Context / configuration for TLS (SSL) connections; also acts as a factory for
* \ref tls_socket objects.
*
* A single context can be shared by any number of \ref tls_socket instances.
* A context must remain in scope as long as any socket using it remains in scope.
*/
class tls_context
{
public:
enum role_t {
CLIENT = 0,
SERVER = 1
};
/**
* A singleton context that can be used if you don't need any per-connection
* configuration.
*/
static tls_context& default_context();

virtual ~tls_context() =default;

/**
* Tells whether the context is initialized and valid. Check this after constructing
* an instance and do not use if not valid.
* @return Zero if valid, a nonzero error code if initialization failed.
* The code may be a POSIX code, or one specific to the TLS library.
*/
int status() const {
return status_;
}

operator bool() const {
return status_ == 0;
}

/**
* Overrides the set of trusted root certificates used for validation.
*/
virtual void set_root_certs(const std::string &certData) =0;

/**
* Allows connections to peers whose X.509 certificates are not valid.
* **If enabled, you take responsibility for validating the certificate yourself!**
* @param allow Pass true to allow invalid certs to be used, false to disallow
* (default is false.)
*/
virtual void allow_invalid_peer_certs(bool allow) =0;

/**
* Requires that the peer have the exact certificate given.
* This is known as "cert-pinning". It's more secure, but requires that the client
* update its copy of the certificate whenever the server updates it.
* @param certData The X.509 certificate in DER or PEM form; or an empty string for
* no pinning (the default).
*/
virtual void allow_only_certificate(const std::string &certData) =0;

virtual void set_identity(const std::string &certificate_data,
const std::string &private_key_data) =0;

virtual void set_identity_files(const std::string &certificate_file,
const std::string &private_key_file,
const std::string &private_key_password) =0;

/**
* Creates a new \ref tls_socket instance that wraps the given connector socket.
* The \ref tls_socket takes ownership of the base socket and will close it when
* it's closed.
* When this method returns, the TLS handshake will already have completed;
* be sure to check the stream's status, since the handshake may have failed.
* @param socket The underlying connector socket that TLS will use for I/O.
* @param role CLIENT or SERVER mode.
* @param peer_name The peer's canonical hostname, or other distinguished name,
* to be used for certificate validation.
* @return A new \ref tls_socket to use for secure I/O.
*/
virtual std::unique_ptr<tls_socket> wrap_socket(std::unique_ptr<stream_socket> socket,
role_t role,
const std::string &peer_name) =0;

protected:
tls_context() =default;

/**
* Sets the error status of the context. Call this if initialization fails.
*/
void set_status(int s) const {
status_ = s;
}

private:
tls_context(const tls_context&) =delete;

mutable int status_ =0;
};

}

#endif
Loading

0 comments on commit e297281

Please sign in to comment.