nymea/libnymea-core/OpenSSL/OpenSSLConnection.hpp

287 lines
12 KiB
C++

/*
* Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.
*/
/**
* @file OpenSSLConnection.hpp
* @brief Defines a reference implementation for an OpenSSL library wrapper
*/
#pragma once
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"ws2_32")
#else
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <atomic>
#include <condition_variable>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include <string.h>
#include "NetworkConnection.hpp"
#include "ResponseCode.hpp"
namespace awsiotsdk {
namespace network {
/**
* @brief Dummy class for uninitializing the OpenSSL library'
*
* TODO: This is a hotfix and will be updated after design changes
*/
class OpenSSLInitializer {
private:
// Rule of 5 stuff
// Default constructor
OpenSSLInitializer() = default;
// Default Copy constructor
OpenSSLInitializer(const OpenSSLInitializer &) = default;
// Default Move constructor
OpenSSLInitializer(OpenSSLInitializer &&) = default;
// Default Copy assignment operator
OpenSSLInitializer &operator=(const OpenSSLInitializer &) & = default;
// Default Move assignment operator
OpenSSLInitializer &operator=(OpenSSLInitializer &&) & = default;
public:
static OpenSSLInitializer* getInstance();
~OpenSSLInitializer();
};
/**
* @brief OpenSSL Wrapper Class
*
* Defines a reference wrapper for OpenSSL libraries
*/
class OpenSSLConnection : public NetworkConnection {
protected:
static std::atomic_bool is_lib_initialized; ///< Boolean, True = Library is initialized, False otherwise
OpenSSLInitializer *initializer; ///< Pointer to dummy library instance
util::String root_ca_location_; ///< Pointer to string containing the filename (including path) of the root CA file.
util::String device_cert_location_; ///< Pointer to string containing the filename (including path) of the device certificate.
util::String device_private_key_location_; ///< Pointer to string containing the filename (including path) of the device private key file.
bool server_verification_flag_; ///< Boolean. True = perform server certificate hostname validation. False = skip validation \b NOT recommended.
std::atomic_bool is_connected_; ///< Boolean indicating connection status
struct timeval tls_handshake_timeout_; ///< Timeout for TLS handshake command
struct timeval tls_read_timeout_; ///< Timeout for the TLS Read command
struct timeval tls_write_timeout_; ///< Timeout for the TLS Write command
// Endpoint information
uint16_t endpoint_port_; ///< Endpoint port
util::String endpoint_; ///< Endpoint for this connection
SSL_CTX *p_ssl_context_; ///< SSL Context instance
SSL *p_ssl_handle_; ///< SSL Handle
int server_tcp_socket_fd_; ///< Server Socket descriptor
bool certificates_read_flag_;
std::mutex clean_shutdown_action_lock_;
std::condition_variable shutdown_timeout_condition_;
/**
* @brief Wait for socket FDs to become ready for read or write operations
*
* It is assumed that this function will be called only on SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE
*
* @param error_code - error generated by preceding socket operation
* @return int - return code of the select operation
*/
int WaitForSelect(int error_code);
/**
* @brief Set TLS socket to non-blocking mode
*
* @return ResponseCode - successful operation or TLS error
*/
ResponseCode SetSocketToNonBlocking();
/**
* @brief Create a TCP socket and open the connection
*
* Creates an open socket connection
*
* @return ResponseCode - successful connection or TCP error
*/
ResponseCode ConnectTCPSocket();
/**
* @brief Attempt connection
*
* Attempts TLS Connection
*
* @return ResponseCode - successful connection or TLS error
*/
ResponseCode AttemptConnect();
/**
* @brief Create a TLS socket and open the connection
*
* Creates an open socket connection including TLS handshake.
*
* @return ResponseCode - successful connection or TLS error
*/
ResponseCode LoadCerts();
/**
* @brief Perform the connect process after creating the SSL handle and context
*
* Performs the steps necessary for connecting to an endpoint after the creation of the SSL instance
*
* @return ResponseCode - successful connection or TLS error
*/
ResponseCode PerformSSLConnect();
/**
* @brief Create a TLS socket and open the connection
*
* Creates an open socket connection including TLS handshake.
*
* @return ResponseCode - successful connection or TLS error
*/
ResponseCode ConnectInternal();
/**
* @brief Write bytes to the network socket
*
* @param util::String - const reference to buffer which should be written to socket
* @return size_t - number of bytes written or Network error
* @return ResponseCode - successful write or Network error code
*/
ResponseCode WriteInternal(const util::String &buf, size_t &size_written_bytes_out);
/**
* @brief Read bytes from the network socket
*
* @param util::String - reference to buffer where read bytes should be copied
* @param size_t - number of bytes to read
* @param size_t - reference to store number of bytes read
* @return ResponseCode - successful read or TLS error code
*/
ResponseCode ReadInternal(util::Vector<unsigned char> &buf, size_t buf_read_offset,
size_t size_bytes_to_read, size_t &size_read_bytes_out);
/**
* @brief Disconnect from network socket
*
* @return ResponseCode - successful read or TLS error code
*/
ResponseCode DisconnectInternal();
public:
OpenSSLConnection(util::String endpoint, uint16_t endpoint_port,
std::chrono::milliseconds tls_handshake_timeout,
std::chrono::milliseconds tls_read_timeout,
std::chrono::milliseconds tls_write_timeout,
bool server_verification_flag);
/**
* @brief Constructor for the OpenSSL TLS implementation
*
* Performs any initialization required by the TLS layer.
*
* @param util::String endpoint - The target endpoint to connect to
* @param uint16_t endpoint_port - The port on the target to connect to
* @param util::String root_ca_location - Path of the location of the Root CA
* @param util::String device_cert_location - Path to the location of the Device Cert
* @param util::String device_private_key_location - Path to the location of the device private key file
* @param std::chrono::milliseconds tls_handshake_timeout - The value to use for timeout of handshake operation
* @param std::chrono::milliseconds tls_read_timeout - The value to use for timeout of read operation
* @param std::chrono::milliseconds tls_write_timeout - The value to use for timeout of write operation
* @param bool server_verification_flag - used to decide whether server verification is needed or not
*
*/
OpenSSLConnection(util::String endpoint, uint16_t endpoint_port, util::String root_ca_location,
util::String device_cert_location, util::String device_private_key_location,
std::chrono::milliseconds tls_handshake_timeout,
std::chrono::milliseconds tls_read_timeout, std::chrono::milliseconds tls_write_timeout,
bool server_verification_flag);
OpenSSLConnection(util::String endpoint, uint16_t endpoint_port, util::String root_ca_location,
std::chrono::milliseconds tls_handshake_timeout,
std::chrono::milliseconds tls_read_timeout, std::chrono::milliseconds tls_write_timeout,
bool server_verification_flag);
/**
* @brief Initialize the OpenSSL object
*
* Initializes the OpenSSL object
*/
ResponseCode Initialize();
/**
* @brief sets the path to the root CA
*
* Called to change the location of the root CA after the constructor has initialized the OpenSSL object.
*
* @param root_ca_location
*/
void SetRootCAPath(util::String root_ca_location) {
root_ca_location_ = root_ca_location;
certificates_read_flag_ = false;
}
/**
* @brief sets the endpoint and the port
*
* Called to change the endpoint and the port after the constructor has initialized the OpenSSL object.
* @param endpoint
* @param endpoint_port
*/
void SetEndpointAndPort(util::String endpoint, uint16_t endpoint_port) {
endpoint_ = endpoint;
endpoint_port_ = endpoint_port;
}
/**
* @brief Check if TLS layer is still connected
*
* Called to check if the TLS layer is still connected or not.
*
* @return bool - indicating status of network TLS layer connection
*/
bool IsConnected();
/**
* @brief Check if Network Physical layer is still connected
*
* Called to check if the Network Physical layer is still connected or not.
*
* @return bool - indicating status of network physical layer connection
*/
bool IsPhysicalLayerConnected();
virtual ~OpenSSLConnection();
};
}
}