PolarSSL wrappers for the SSL context.
This commit is contained in:
parent
f301d052cf
commit
0bdc49221b
195
src/PolarSSL++/BlockingSslClientSocket.cpp
Normal file
195
src/PolarSSL++/BlockingSslClientSocket.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
|
||||
// BlockingSslClientSocket.cpp
|
||||
|
||||
// Implements the cBlockingSslClientSocket class representing a blocking TCP socket with client SSL encryption over it
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BlockingSslClientSocket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBlockingSslClientSocket::cBlockingSslClientSocket(void) :
|
||||
m_IsConnected(false),
|
||||
m_Ssl(*this)
|
||||
{
|
||||
// Nothing needed yet
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockingSslClientSocket::Connect(const AString & a_ServerName, UInt16 a_Port)
|
||||
{
|
||||
// If already connected, report an error:
|
||||
if (m_IsConnected)
|
||||
{
|
||||
// TODO: Handle this better - if connected to the same server and port, and the socket is alive, return success
|
||||
m_LastErrorText = "Already connected";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect the underlying socket:
|
||||
m_Socket.CreateSocket(cSocket::IPv4);
|
||||
if (!m_Socket.ConnectIPv4(a_ServerName.c_str(), a_Port))
|
||||
{
|
||||
Printf(m_LastErrorText, "Socket connect failed: %s", m_Socket.GetLastErrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize the SSL:
|
||||
int ret = m_Ssl.Initialize(true);
|
||||
if (ret != 0)
|
||||
{
|
||||
Printf(m_LastErrorText, "SSL initialization failed: -0x%x", -ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have been assigned a trusted CA root cert store, push it into the SSL context:
|
||||
if (m_CACerts.get() != NULL)
|
||||
{
|
||||
m_Ssl.SetCACerts(m_CACerts, m_ExpectedPeerName);
|
||||
}
|
||||
|
||||
ret = m_Ssl.Handshake();
|
||||
if (ret != 0)
|
||||
{
|
||||
Printf(m_LastErrorText, "SSL handshake failed: -0x%x", -ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_IsConnected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockingSslClientSocket::SetTrustedRootCertsFromString(const AString & a_CACerts, const AString & a_ExpectedPeerName)
|
||||
{
|
||||
// Warn if used multiple times, but don't signal an error:
|
||||
if (m_CACerts.get() != NULL)
|
||||
{
|
||||
LOGWARNING(
|
||||
"SSL: Trying to set multiple trusted CA root cert stores, only the last one will be used. Name: %s",
|
||||
a_ExpectedPeerName.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
// Parse the cert:
|
||||
m_CACerts.reset(new cX509Cert);
|
||||
int ret = m_CACerts->Parse(a_CACerts.data(), a_CACerts.size());
|
||||
if (ret < 0)
|
||||
{
|
||||
Printf(m_LastErrorText, "CA cert parsing failed: -0x%x", -ret);
|
||||
return false;
|
||||
}
|
||||
m_ExpectedPeerName = a_ExpectedPeerName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockingSslClientSocket::Send(const void * a_Data, size_t a_NumBytes)
|
||||
{
|
||||
ASSERT(m_IsConnected);
|
||||
|
||||
// Keep sending the data until all of it is sent:
|
||||
const char * Data = (const char *)a_Data;
|
||||
size_t NumBytes = a_NumBytes;
|
||||
for (;;)
|
||||
{
|
||||
int res = m_Ssl.WritePlain(a_Data, a_NumBytes);
|
||||
if (res < 0)
|
||||
{
|
||||
ASSERT(res != POLARSSL_ERR_NET_WANT_READ); // This should never happen with callback-based SSL
|
||||
ASSERT(res != POLARSSL_ERR_NET_WANT_WRITE); // This should never happen with callback-based SSL
|
||||
Printf(m_LastErrorText, "Data cannot be written to SSL context: -0x%x", -res);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Data += res;
|
||||
NumBytes -= res;
|
||||
if (NumBytes == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBlockingSslClientSocket::Receive(void * a_Data, size_t a_MaxBytes)
|
||||
{
|
||||
ASSERT(m_IsConnected);
|
||||
|
||||
int res = m_Ssl.ReadPlain(a_Data, a_MaxBytes);
|
||||
if (res < 0)
|
||||
{
|
||||
Printf(m_LastErrorText, "Data cannot be read form SSL context: -0x%x", -res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockingSslClientSocket::Disconnect(void)
|
||||
{
|
||||
// Ignore if not connected
|
||||
if (!m_IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Ssl.NotifyClose();
|
||||
m_Socket.CloseSocket();
|
||||
m_IsConnected = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBlockingSslClientSocket::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
int res = m_Socket.Receive((char *)a_Buffer, a_NumBytes, 0);
|
||||
if (res < 0)
|
||||
{
|
||||
// PolarSSL's net routines distinguish between connection reset and general failure, we don't need to
|
||||
return POLARSSL_ERR_NET_RECV_FAILED;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBlockingSslClientSocket::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
int res = m_Socket.Send((const char *)a_Buffer, a_NumBytes);
|
||||
if (res < 0)
|
||||
{
|
||||
// PolarSSL's net routines distinguish between connection reset and general failure, we don't need to
|
||||
return POLARSSL_ERR_NET_SEND_FAILED;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
80
src/PolarSSL++/BlockingSslClientSocket.h
Normal file
80
src/PolarSSL++/BlockingSslClientSocket.h
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
// BlockingSslClientSocket.h
|
||||
|
||||
// Declares the cBlockingSslClientSocket class representing a blocking TCP socket with client SSL encryption over it
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CallbackSslContext.h"
|
||||
#include "../OSSupport/Socket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockingSslClientSocket :
|
||||
protected cCallbackSslContext::cDataCallbacks
|
||||
{
|
||||
public:
|
||||
cBlockingSslClientSocket(void);
|
||||
|
||||
/** Connects to the specified server and performs SSL handshake.
|
||||
Returns true if successful, false on failure. Sets internal error text on failure. */
|
||||
bool Connect(const AString & a_ServerName, UInt16 a_Port);
|
||||
|
||||
/** Sends the specified data over the connection.
|
||||
Returns true if successful, false on failure. Sets the internal error text on failure. */
|
||||
bool Send(const void * a_Data, size_t a_NumBytes);
|
||||
|
||||
/** Receives data from the connection.
|
||||
Blocks until there is any data available, then returns as much as possible.
|
||||
Returns the number of bytes actually received, negative number on failure.
|
||||
Sets the internal error text on failure. */
|
||||
int Receive(void * a_Data, size_t a_MaxBytes);
|
||||
|
||||
/** Disconnects the connection gracefully, if possible.
|
||||
Note that this also frees the internal SSL context, so all the certificates etc. are lost. */
|
||||
void Disconnect(void);
|
||||
|
||||
/** Sets the root certificates that are to be trusted. Forces the connection to use strict cert
|
||||
verification. Needs to be used before calling Connect().
|
||||
a_ExpectedPeerName is the name that we expect to receive in the SSL peer's cert; verification will fail if
|
||||
the presented name is different (possible MITM).
|
||||
Returns true on success, false on failure. Sets internal error text on failure. */
|
||||
bool SetTrustedRootCertsFromString(const AString & a_CACerts, const AString & a_ExpectedPeerName);
|
||||
|
||||
/** Returns the text of the last error that has occurred in this instance. */
|
||||
const AString & GetLastErrorText(void) const { return m_LastErrorText; }
|
||||
|
||||
protected:
|
||||
/** The SSL context used for the socket */
|
||||
cCallbackSslContext m_Ssl;
|
||||
|
||||
/** The underlying socket to the SSL server */
|
||||
cSocket m_Socket;
|
||||
|
||||
/** The trusted CA root cert store, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */
|
||||
cX509CertPtr m_CACerts;
|
||||
|
||||
/** The expected SSL peer's name, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */
|
||||
AString m_ExpectedPeerName;
|
||||
|
||||
/** Text of the last error that has occurred. */
|
||||
AString m_LastErrorText;
|
||||
|
||||
/** Set to true if the connection established successfully. */
|
||||
bool m_IsConnected;
|
||||
|
||||
|
||||
// cCallbackSslContext::cDataCallbacks overrides:
|
||||
virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
62
src/PolarSSL++/BufferedSslContext.cpp
Normal file
62
src/PolarSSL++/BufferedSslContext.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
// BufferedSslContext.cpp
|
||||
|
||||
// Implements the cBufferedSslContext class representing a SSL context with the SSL peer data backed by a cByteBuffer
|
||||
|
||||
#include "Globals.h"
|
||||
#include "BufferedSslContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBufferedSslContext::cBufferedSslContext(size_t a_BufferSize):
|
||||
m_OutgoingData(a_BufferSize),
|
||||
m_IncomingData(a_BufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBufferedSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
// Called when PolarSSL wants to read encrypted data from the SSL peer
|
||||
// Read the data from the buffer inside this object, where the owner has stored them using WriteIncoming():
|
||||
size_t NumBytes = std::min(a_NumBytes, m_IncomingData.GetReadableSpace());
|
||||
if (NumBytes == 0)
|
||||
{
|
||||
return POLARSSL_ERR_NET_WANT_READ;
|
||||
}
|
||||
if (!m_IncomingData.ReadBuf(a_Buffer, NumBytes))
|
||||
{
|
||||
m_IncomingData.ResetRead();
|
||||
return POLARSSL_ERR_NET_RECV_FAILED;
|
||||
}
|
||||
m_IncomingData.CommitRead();
|
||||
return (int)NumBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBufferedSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
// Called when PolarSSL wants to write encrypted data to the SSL peer
|
||||
// Write the data into the buffer inside this object, where the owner can later read them using ReadOutgoing():
|
||||
if (!m_OutgoingData.CanWriteBytes(a_NumBytes))
|
||||
{
|
||||
return POLARSSL_ERR_NET_WANT_WRITE;
|
||||
}
|
||||
if (!m_OutgoingData.Write((const char *)a_Buffer, a_NumBytes))
|
||||
{
|
||||
return POLARSSL_ERR_NET_SEND_FAILED;
|
||||
}
|
||||
return (int)a_NumBytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
52
src/PolarSSL++/BufferedSslContext.h
Normal file
52
src/PolarSSL++/BufferedSslContext.h
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
// BufferedSslContext.h
|
||||
|
||||
// Declares the cBufferedSslContext class representing a SSL context with the SSL peer data backed by a cByteBuffer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SslContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBufferedSslContext :
|
||||
public cSslContext
|
||||
{
|
||||
typedef cSslContext super;
|
||||
|
||||
public:
|
||||
/** Creates a new context with the buffers of specified size for the encrypted / decrypted data. */
|
||||
cBufferedSslContext(size_t a_BufferSize = 64000);
|
||||
|
||||
/** Stores the specified data in the "incoming" buffer, to be process by the SSL decryptor.
|
||||
This is the data received from the SSL peer.
|
||||
Returns the number of bytes actually stored. If 0 is returned, owner should check the error state. */
|
||||
size_t WriteIncoming(const void * a_Data, size_t a_NumBytes);
|
||||
|
||||
/** Retrieves data from the "outgoing" buffer, after being processed by the SSL encryptor.
|
||||
This is the data to be sent to the SSL peer.
|
||||
Returns the number of bytes actually retrieved. */
|
||||
size_t ReadOutgoing(void * a_Data, size_t a_DataMaxSize);
|
||||
|
||||
protected:
|
||||
/** Buffer for the data that has been encrypted into the SSL stream and should be sent out. */
|
||||
cByteBuffer m_OutgoingData;
|
||||
|
||||
/** Buffer for the data that has come in and needs to be decrypted from the SSL stream. */
|
||||
cByteBuffer m_IncomingData;
|
||||
|
||||
|
||||
// cSslContext overrides:
|
||||
virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -5,12 +5,26 @@ project (MCServer)
|
||||
include_directories ("${PROJECT_SOURCE_DIR}/../")
|
||||
|
||||
set(SOURCES
|
||||
"EntropyContext.cpp"
|
||||
"BlockingSslClientSocket.cpp"
|
||||
"BufferedSslSocket.cpp"
|
||||
"CallbackSslContext.cpp"
|
||||
"CtrDrbgContext.cpp"
|
||||
"EntropyContext.cpp"
|
||||
"SslContext.cpp"
|
||||
"X509Cert.cpp"
|
||||
)
|
||||
|
||||
add_library(PolarSSL++ ${SOURCES})
|
||||
set(HEADERS
|
||||
"BlockingSslClientSocket.h"
|
||||
"BufferedSslSocket.h"
|
||||
"CallbackSslContext.h"
|
||||
"CtrDrbgContext.h"
|
||||
"EntropyContext.h"
|
||||
"SslContext.h"
|
||||
"X509Cert.h"
|
||||
)
|
||||
|
||||
add_library(PolarSSL++ ${SOURCES} ${HEADERS})
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(PolarSSL++ polarssl)
|
||||
|
59
src/PolarSSL++/CallbackSslContext.cpp
Normal file
59
src/PolarSSL++/CallbackSslContext.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
|
||||
// CallbackSslContext.cpp
|
||||
|
||||
// Declares the cCallbackSslContext class representing a SSL context wrapper that uses callbacks to read and write SSL peer data
|
||||
|
||||
#include "Globals.h"
|
||||
#include "CallbackSslContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCallbackSslContext::cCallbackSslContext(void)
|
||||
{
|
||||
// Nothing needed, but the constructor needs to exist so
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCallbackSslContext::cCallbackSslContext(cCallbackSslContext::cDataCallbacks & a_Callbacks) :
|
||||
m_Callbacks(&a_Callbacks)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cCallbackSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
if (m_Callbacks == NULL)
|
||||
{
|
||||
LOGWARNING("SSL: Trying to receive data with no callbacks, aborting.");
|
||||
return POLARSSL_ERR_NET_RECV_FAILED;
|
||||
}
|
||||
return m_Callbacks->ReceiveEncrypted(a_Buffer, a_NumBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cCallbackSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
if (m_Callbacks == NULL)
|
||||
{
|
||||
LOGWARNING("SSL: Trying to send data with no callbacks, aborting.");
|
||||
return POLARSSL_ERR_NET_SEND_FAILED;
|
||||
}
|
||||
return m_Callbacks->SendEncrypted(a_Buffer, a_NumBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
61
src/PolarSSL++/CallbackSslContext.h
Normal file
61
src/PolarSSL++/CallbackSslContext.h
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
// CallbackSslContext.h
|
||||
|
||||
// Declares the cCallbackSslContext class representing a SSL context wrapper that uses callbacks to read and write SSL peer data
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SslContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCallbackSslContext :
|
||||
public cSslContext
|
||||
{
|
||||
public:
|
||||
/** Interface used as a data sink for the SSL peer data. */
|
||||
class cDataCallbacks
|
||||
{
|
||||
public:
|
||||
/** Called when PolarSSL wants to read encrypted data from the SSL peer.
|
||||
The returned value is the number of bytes received, or a PolarSSL error on failure.
|
||||
The implementation can return POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE to indicate
|
||||
that there's currently no more data and that there might be more data in the future. In such cases the
|
||||
SSL operation that invoked this call will terminate with the same return value, so that the owner is
|
||||
notified of this condition and can potentially restart the operation later on. */
|
||||
virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) = 0;
|
||||
|
||||
/** Called when PolarSSL wants to write encrypted data to the SSL peer.
|
||||
The returned value is the number of bytes sent, or a PolarSSL error on failure.
|
||||
The implementation can return POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE to indicate
|
||||
that there's currently no more data and that there might be more data in the future. In such cases the
|
||||
SSL operation that invoked this call will terminate with the same return value, so that the owner is
|
||||
notified of this condition and can potentially restart the operation later on. */
|
||||
virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
/** Creates a new SSL context with no callbacks assigned */
|
||||
cCallbackSslContext(void);
|
||||
|
||||
/** Creates a new SSL context with the specified callbacks */
|
||||
cCallbackSslContext(cDataCallbacks & a_Callbacks);
|
||||
|
||||
protected:
|
||||
/** The callbacks to use to send and receive SSL peer data */
|
||||
cDataCallbacks * m_Callbacks;
|
||||
|
||||
// cSslContext overrides:
|
||||
virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
181
src/PolarSSL++/SslContext.cpp
Normal file
181
src/PolarSSL++/SslContext.cpp
Normal file
@ -0,0 +1,181 @@
|
||||
|
||||
// SslContext.cpp
|
||||
|
||||
// Implements the cSslContext class that holds everything a single SSL context needs to function
|
||||
|
||||
#include "Globals.h"
|
||||
#include "SslContext.h"
|
||||
#include "EntropyContext.h"
|
||||
#include "CtrDrbgContext.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSslContext::cSslContext(void) :
|
||||
m_IsValid(false),
|
||||
m_HasHandshaken(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSslContext::~cSslContext()
|
||||
{
|
||||
if (m_IsValid)
|
||||
{
|
||||
ssl_free(&m_Ssl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> & a_CtrDrbg)
|
||||
{
|
||||
// Check double-initialization:
|
||||
if (m_IsValid)
|
||||
{
|
||||
LOGWARNING("SSL: Double initialization is not supported.");
|
||||
return POLARSSL_ERR_SSL_MALLOC_FAILED; // There is no return value well-suited for this, reuse this one.
|
||||
}
|
||||
|
||||
// Set the CtrDrbg context, create a new one if needed:
|
||||
m_CtrDrbg = a_CtrDrbg;
|
||||
if (m_CtrDrbg.get() == NULL)
|
||||
{
|
||||
m_CtrDrbg.reset(new cCtrDrbgContext);
|
||||
m_CtrDrbg->Initialize("MCServer", 8);
|
||||
}
|
||||
|
||||
// Initialize PolarSSL's structures:
|
||||
memset(&m_Ssl, 0, sizeof(m_Ssl));
|
||||
int res = ssl_init(&m_Ssl);
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
ssl_set_endpoint(&m_Ssl, a_IsClient ? SSL_IS_CLIENT : SSL_IS_SERVER);
|
||||
ssl_set_authmode(&m_Ssl, SSL_VERIFY_OPTIONAL);
|
||||
ssl_set_rng(&m_Ssl, ctr_drbg_random, &m_CtrDrbg->m_CtrDrbg);
|
||||
ssl_set_bio(&m_Ssl, ReceiveEncrypted, this, SendEncrypted, this);
|
||||
|
||||
#ifdef _DEBUG
|
||||
ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this);
|
||||
#endif
|
||||
|
||||
m_IsValid = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSslContext::SetCACerts(const cX509CertPtr & a_CACert, const AString & a_ExpectedPeerName)
|
||||
{
|
||||
// Store the data in our internal buffers, to avoid losing the pointers later on
|
||||
// PolarSSL will need these after this call returns, and the caller may move / delete the data before that:
|
||||
m_ExpectedPeerName = a_ExpectedPeerName;
|
||||
m_CACerts = a_CACert;
|
||||
|
||||
// Set the trusted CA root cert store:
|
||||
ssl_set_authmode(&m_Ssl, SSL_VERIFY_REQUIRED);
|
||||
ssl_set_ca_chain(&m_Ssl, m_CACerts->GetInternal(), NULL, m_ExpectedPeerName.empty() ? NULL : m_ExpectedPeerName.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSslContext::WritePlain(const void * a_Data, size_t a_NumBytes)
|
||||
{
|
||||
ASSERT(m_IsValid); // Need to call Initialize() first
|
||||
if (!m_HasHandshaken)
|
||||
{
|
||||
int res = Handshake();
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return ssl_write(&m_Ssl, (const unsigned char *)a_Data, a_NumBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSslContext::ReadPlain(void * a_Data, size_t a_MaxBytes)
|
||||
{
|
||||
ASSERT(m_IsValid); // Need to call Initialize() first
|
||||
if (!m_HasHandshaken)
|
||||
{
|
||||
int res = Handshake();
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return ssl_read(&m_Ssl, (unsigned char *)a_Data, a_MaxBytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSslContext::Handshake(void)
|
||||
{
|
||||
ASSERT(m_IsValid); // Need to call Initialize() first
|
||||
ASSERT(!m_HasHandshaken); // Must not call twice
|
||||
|
||||
int res = ssl_handshake(&m_Ssl);
|
||||
if (res == 0)
|
||||
{
|
||||
m_HasHandshaken = true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSslContext::NotifyClose(void)
|
||||
{
|
||||
return ssl_close_notify(&m_Ssl);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
void cSslContext::SSLDebugMessage(void * a_UserParam, int a_Level, const char * a_Text)
|
||||
{
|
||||
if (a_Level > 3)
|
||||
{
|
||||
// Don't want the trace messages
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the terminating LF:
|
||||
size_t len = strlen(a_Text) - 1;
|
||||
while ((len > 0) && (a_Text[len] <= 32))
|
||||
{
|
||||
len--;
|
||||
}
|
||||
AString Text(a_Text, len + 1);
|
||||
|
||||
LOGD("SSL (%d): %s", a_Level, Text.c_str());
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
|
||||
|
||||
|
134
src/PolarSSL++/SslContext.h
Normal file
134
src/PolarSSL++/SslContext.h
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
// SslContext.h
|
||||
|
||||
// Declares the cSslContext class that holds everything a single SSL context needs to function
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "polarssl/ssl.h"
|
||||
#include "../ByteBuffer.h"
|
||||
#include "X509Cert.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cCtrDrbgContext;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Acts as a generic SSL encryptor / decryptor between the two endpoints. The "owner" of this class is expected
|
||||
to create it, initialize it and then provide the means of reading and writing data through the SSL link.
|
||||
This is an abstract base class, there are descendants that handle the specific aspects of how the SSL peer
|
||||
data comes into the system:
|
||||
- cBufferedSslContext uses a cByteBuffer to read and write the data
|
||||
- cCallbackSslContext uses callbacks to provide the data
|
||||
*/
|
||||
class cSslContext abstract
|
||||
{
|
||||
public:
|
||||
/** Creates a new uninitialized context */
|
||||
cSslContext(void);
|
||||
|
||||
~cSslContext();
|
||||
|
||||
/** Initializes the context for use as a server or client.
|
||||
Returns 0 on success, PolarSSL error on failure. */
|
||||
int Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> & a_CtrDrbg = SharedPtr<cCtrDrbgContext>());
|
||||
|
||||
/** Returns true if the object has been initialized properly. */
|
||||
bool IsValid(void) const { return m_IsValid; }
|
||||
|
||||
/** Sets a cert chain as the trusted cert store for this context.
|
||||
Calling this will switch the context into strict cert verification mode.
|
||||
a_ExpectedPeerName is the CommonName that we expect the SSL peer to have in its cert,
|
||||
if it is different, the verification will fail. An empty string will disable the CN check. */
|
||||
void SetCACerts(const cX509CertPtr & a_CACert, const AString & a_ExpectedPeerName);
|
||||
|
||||
/** Writes data to be encrypted and sent to the SSL peer. Will perform SSL handshake, if needed.
|
||||
Returns the number of bytes actually written, or PolarSSL error code.
|
||||
If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any
|
||||
cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call
|
||||
this function again with the same parameters. Note that this may repeat a few times before the data is
|
||||
actually written, mainly due to initial handshake. */
|
||||
int WritePlain(const void * a_Data, size_t a_NumBytes);
|
||||
|
||||
/** Reads data decrypted from the SSL stream. Will perform SSL handshake, if needed.
|
||||
Returns the number of bytes actually read, or PolarSSL error code.
|
||||
If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any
|
||||
cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call
|
||||
this function again with the same parameters. Note that this may repeat a few times before the data is
|
||||
actually read, mainly due to initial handshake. */
|
||||
int ReadPlain(void * a_Data, size_t a_MaxBytes);
|
||||
|
||||
/** Performs the SSL handshake.
|
||||
Returns zero on success, PoladSSL error code on failure.
|
||||
If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any
|
||||
cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call
|
||||
this function again. Note that this may repeat a few times before the handshake is completed. */
|
||||
int Handshake(void);
|
||||
|
||||
/** Returns true if the SSL handshake has been completed. */
|
||||
bool HasHandshaken(void) const { return m_HasHandshaken; }
|
||||
|
||||
/** Notifies the SSL peer that the connection is being closed.
|
||||
Returns 0 on success, PolarSSL error code on failure. */
|
||||
int NotifyClose(void);
|
||||
|
||||
protected:
|
||||
/** True if the object has been initialized properly. */
|
||||
bool m_IsValid;
|
||||
|
||||
/** The random generator to use */
|
||||
SharedPtr<cCtrDrbgContext> m_CtrDrbg;
|
||||
|
||||
/** The SSL context that PolarSSL uses. */
|
||||
ssl_context m_Ssl;
|
||||
|
||||
/** True if the SSL handshake has been completed. */
|
||||
bool m_HasHandshaken;
|
||||
|
||||
/** A copy of the trusted CA root cert store that is passed to us in SetCACerts(), so that the pointer
|
||||
stays valid even after the call, when PolarSSL finally uses it. */
|
||||
cX509CertPtr m_CACerts;
|
||||
|
||||
/** Buffer for the expected peer name. We need to buffer it because the caller may free the string they
|
||||
give us before PolarSSL consumes the raw pointer it gets to the CN. */
|
||||
AString m_ExpectedPeerName;
|
||||
|
||||
|
||||
/** The callback used by PolarSSL when it wants to read encrypted data. */
|
||||
static int ReceiveEncrypted(void * a_This, unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
return ((cSslContext *)a_This)->ReceiveEncrypted(a_Buffer, a_NumBytes);
|
||||
}
|
||||
|
||||
/** The callback used by PolarSSL when it wants to write encrypted data. */
|
||||
static int SendEncrypted(void * a_This, const unsigned char * a_Buffer, size_t a_NumBytes)
|
||||
{
|
||||
return ((cSslContext *)a_This)->SendEncrypted(a_Buffer, a_NumBytes);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
/** The callback used by PolarSSL to output debug messages */
|
||||
static void SSLDebugMessage(void * a_UserParam, int a_Level, const char * a_Text);
|
||||
#endif // _DEBUG
|
||||
|
||||
/** Called when PolarSSL wants to read encrypted data. */
|
||||
virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) = 0;
|
||||
|
||||
/** Called when PolarSSL wants to write encrypted data. */
|
||||
virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
class cX509Cert
|
||||
{
|
||||
friend class cSslContext;
|
||||
|
||||
public:
|
||||
cX509Cert(void);
|
||||
~cX509Cert(void);
|
||||
@ -25,13 +27,15 @@ public:
|
||||
Returns 0 on succes, or PolarSSL error code on failure. */
|
||||
int Parse(const void * a_CertContents, size_t a_Size);
|
||||
|
||||
/** Returns the internal cert ptr. Only use in PolarSSL API calls. */
|
||||
OBSOLETE x509_crt * Get(void) { return &m_Cert; }
|
||||
|
||||
protected:
|
||||
x509_crt m_Cert;
|
||||
|
||||
/** Returns the internal cert ptr. Only use in PolarSSL API calls. */
|
||||
x509_crt * GetInternal(void) { return &m_Cert; }
|
||||
} ;
|
||||
|
||||
typedef SharedPtr<cX509Cert> cX509CertPtr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user