1
0

Exported TLS server start on cTCPLink to Lua API.

This commit is contained in:
Mattes D 2015-02-13 23:18:22 +01:00
parent b8bf795dd1
commit 557adf3be9
5 changed files with 292 additions and 10 deletions

View File

@ -303,6 +303,7 @@ g_Server = nil
Send = { Params = "Data", Return = "", Notes = "Sends the data (raw string) to the remote peer. The data is sent asynchronously and there is no report on the success of the send operation, other than the connection being closed or reset by the underlying OS." },
Shutdown = { Params = "", Return = "", Notes = "Shuts the socket down for sending data. Notifies the remote peer that there will be no more data coming from us (TCP FIN). The data that is in flight will still be delivered. The underlying socket will be closed when the remote end shuts down as well, or after a timeout." },
StartTLSClient = { Params = "OwnCert, OwnPrivateKey, OwnPrivateKeyPassword", Return = "", Notes = "Starts a TLS handshake on the link, as a client side of the TLS. The Own___ parameters specify the client certificate and its corresponding private key and password; all three parameters are optional and no client certificate is presented to the remote peer if they are not used or all empty. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
StartTLSServer = { Params = "Certificate, PrivateKey, PrivateKeyPassword, StartTLSData", Return = "", Notes = "Starts a TLS handshake on the link, as a server side of the TLS. The plugin needs to specify the server certificate and its corresponding private key and password. The StartTLSData can contain data that the link has already reported as received but it should be used as part of the TLS handshake. Once the TLS handshake is started by this call, all incoming data is first decrypted before being sent to the OnReceivedData callback, and all outgoing data is queued until the TLS handshake completes, and then sent encrypted over the link." },
},
}, -- cTCPLink

View File

@ -19,6 +19,62 @@ local g_Fortunes =
"Empty splashes.txt",
}
-- HTTPS certificate to be used for the SSL server:
local g_HTTPSCert = [[
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIJAOBHN+qOWodcMA0GCSqGSIb3DQEBBQUAMFYxCzAJBgNV
BAYTAmN6MQswCQYDVQQIDAJjejEMMAoGA1UEBwwDbG9jMQswCQYDVQQKDAJfWDEL
MAkGA1UECwwCT1UxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTAxMjQwODQ2MzFa
Fw0yNTAxMjEwODQ2MzFaMFYxCzAJBgNVBAYTAmN6MQswCQYDVQQIDAJjejEMMAoG
A1UEBwwDbG9jMQswCQYDVQQKDAJfWDELMAkGA1UECwwCT1UxEjAQBgNVBAMMCWxv
Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJkFYSElu/jw
nxqjimmj246DejKJK8uy/l9QQibb/Z4kO/3s0gVPOYo0mKv32xUFP7wYIE3XWT61
zyfvK+1jpnlQTCtM8T5xw/7CULKgLmuIzlQx5Dhy7d+tW46kOjFKwQajS9YzwqWu
KBOPnFamQWz6vIzuM05+7aIMXbzamInvW/1x3klIrpGQgALwSB1N+oUzTInTBRKK
21pecUE9t3qrU40Cs5bN0fQBnBjLwbgmnTh6LEplfQZHG5wLvj0IeERVU9vH7luM
e9/IxuEZluCiu5ViF3jqLPpjYOrkX7JDSKme64CCmNIf0KkrwtFjF104Qylike60
YD3+kw8Q+DECAwEAAaNQME4wHQYDVR0OBBYEFHHIDTc7mrLDXftjQ5ejU9Udfdyo
MB8GA1UdIwQYMBaAFHHIDTc7mrLDXftjQ5ejU9UdfdyoMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAHxCJxZPmH9tvx8GKiDV3rgGY++sMItzrW5Uhf0/
bl3DPbVz51CYF8nXiWvSJJzxhH61hKpZiqvRlpyMuovV415dYQ+Xc2d2IrTX6e+d
Z4Pmwfb4yaX+kYqIygjXMoyNxOJyhTnCbJzycV3v5tvncBWN9Wqez6ZonWDdFdAm
J+Moty+atc4afT02sUg1xz+CDr1uMbt62tHwKYCdxXCwT//bOs6W21+mQJ5bEAyA
YrHQPgX76uo8ed8rPf6y8Qj//lzq/+33EIWqf9pnbklQgIPXJU07h+5L+Y63RF4A
ComLkzas+qnQLcEN28Dg8QElXop6hfiD0xq3K0ac2bDnjoU=
-----END CERTIFICATE-----
]]
local g_HTTPSPrivKey = [[
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZBWEhJbv48J8a
o4ppo9uOg3oyiSvLsv5fUEIm2/2eJDv97NIFTzmKNJir99sVBT+8GCBN11k+tc8n
7yvtY6Z5UEwrTPE+ccP+wlCyoC5riM5UMeQ4cu3frVuOpDoxSsEGo0vWM8KlrigT
j5xWpkFs+ryM7jNOfu2iDF282piJ71v9cd5JSK6RkIAC8EgdTfqFM0yJ0wUSitta
XnFBPbd6q1ONArOWzdH0AZwYy8G4Jp04eixKZX0GRxucC749CHhEVVPbx+5bjHvf
yMbhGZbgoruVYhd46iz6Y2Dq5F+yQ0ipnuuAgpjSH9CpK8LRYxddOEMpYpHutGA9
/pMPEPgxAgMBAAECggEAWxQ4m+I54BJYoSJ2YCqHpGvdb/b1emkvvsumlDqc2mP2
0U0ENOTS+tATj0gXvotBRFOX5r0nAYx1oO9a1hFaJRsGOz+w19ofLqO6JJfzCU6E
gNixXmgJ7fjhZiWZ/XzhJ3JK0VQ9px/h+sKf63NJvfQABmJBZ5dlGe8CXEZARNin
03TnE3RUIEK+jEgwShN2OrGjwK9fjcnXMHwEnKZtCBiYEfD2N+pQmS20gIm13L1t
+ZmObIC24NqllXxl4I821qzBdhmcT7+rGmKR0OT5YKbt6wFA5FPKD9dqlzXzlKck
r2VAh+JlCtFKxcScmWtQOnVDtf5+mcKFbP4ck724AQKBgQDLk+RDhvE5ykin5R+B
dehUQZgHb2pPL7N1DAZShfzwSmyZSOPQDFr7c0CMijn6G0Pw9VX6Vrln0crfTQYz
Hli+zxlmcMAD/WC6VImM1LCUzouNRy37rSCnuPtngZyHdsyzfheGnjORH7HlPjtY
JCTLaekg0ckQvt//HpRV3DCdaQKBgQDAbLmIOTyGfne74HLswWnY/kCOfFt6eO+E
lZ724MWmVPWkxq+9rltC2CDx2i8jjdkm90dsgR5OG2EaLnUWldUpkE0zH0ATrZSV
ezJWD9SsxTm8ksbThD+pJKAVPxDAboejF7kPvpaO2wY+bf0AbO3M24rJ2tccpMv8
AcfXBICDiQKBgQCSxp81/I3hf7HgszaC7ZLDZMOK4M6CJz847aGFUCtsyAwCfGYb
8zyJvK/WZDam14+lpA0IQAzPCJg/ZVZJ9uA/OivzCum2NrHNxfOiQRrLPxuokaBa
q5k2tA02tGE53fJ6mze1DEzbnkFxqeu5gd2xdzvpOLfBxgzT8KU8PlQiuQKBgGn5
NvCj/QZhDhYFVaW4G1ArLmiKamL3yYluUV7LiW7CaYp29gBzzsTwfKxVqhJdo5NH
KinCrmr7vy2JGmj22a+LTkjyU/rCZQsyDxXAoDMKZ3LILwH8WocPqa4pzlL8TGzw
urXGE+rXCwhE0Mp0Mz7YRgZHJKMcy06duG5dh11pAoGBALHbsBIDihgHPyp2eKMP
K1f42MdKrTBiIXV80hv2OnvWVRCYvnhrqpeRMzCR1pmVbh+QhnwIMAdWq9PAVTTn
ypusoEsG8Y5fx8xhgjs0D2yMcrmi0L0kCgHIFNoym+4pI+sv6GgxpemfrmaPNcMx
DXi9JpaquFRJLGJ7jMCDgotL
-----END PRIVATE KEY-----
]]
--- Map of all services that can be run as servers
-- g_Services[ServiceName] = function() -> accept-callbacks
local g_Services =
@ -66,7 +122,7 @@ local g_Services =
return
{
OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
LOG("FortuneServer(" .. a_Port .. ": Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
LOG("FortuneServer(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end,
OnReceivedData = function (a_Link, a_Data)
@ -86,11 +142,55 @@ local g_Services =
-- There was an error listening on the port:
OnError = function (a_ErrorCode, a_ErrorMsg)
LOGINFO("FortuneServer(" .. a_Port .. ": Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
LOGINFO("FortuneServer(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end, -- OnError()
} -- Listen callbacks
end, -- fortune
-- HTTPS time - serves current time for each https request received
httpstime = function (a_Port)
return
{
-- A new connection has come, give it new link-callbacks:
OnIncomingConnection = function (a_RemoteIP, a_RemotePort)
local IncomingData = "" -- accumulator for the incoming data, until processed by the http
return
{
OnError = function (a_Link, a_ErrorCode, a_ErrorMsg)
LOG("https-time server(" .. a_Port .. "): Connection to " .. a_Link:GetRemoteIP() .. ":" .. a_Link:GetRemotePort() .. " failed: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end,
OnReceivedData = function (a_Link, a_Data)
IncomingData = IncomingData .. a_Data
if (IncomingData:find("\r\n\r\n")) then
local Content = os.date()
a_Link:Send("HTTP/1.0 200 OK\r\nContent-type: text/plain\r\nContent-length: " .. #Content .. "\r\n\r\n" .. Content)
-- TODO: shutdown is not yet properly implemented in cTCPLink
-- a_Link:Shutdown()
end
end,
OnRemoteClosed = function (a_Link)
end
} -- Link callbacks
end, -- OnIncomingConnection()
-- Start TLS on the new link:
OnAccepted = function (a_Link)
local res, msg = a_Link:StartTLSServer(g_HTTPSCert, g_HTTPSPrivKey, "")
if not(res) then
LOG("https-time server(" .. a_Port .. "): Cannot start TLS server: " .. msg)
a_Link:Close()
end
end, -- OnAccepted()
-- There was an error listening on the port:
OnError = function (a_ErrorCode, a_ErrorMsg)
LOGINFO("https-time server(" .. a_Port .. "): Cannot listen: " .. a_ErrorCode .. " (" .. a_ErrorMsg .. ")")
end, -- OnError()
} -- Listen callbacks
end, -- httpstime
-- TODO: Other services (daytime, ...)
}
@ -229,7 +329,7 @@ function HandleConsoleNetListen(a_Split)
-- Get the params:
local Port = tonumber(a_Split[3] or 1024)
if not(Port) then
return true, "Invalid port: \"" .. Port .. "\"."
return true, "Invalid port: \"" .. a_Split[3] .. "\"."
end
local Service = string.lower(a_Split[4] or "echo")

View File

@ -160,10 +160,14 @@ void cLuaTCPLink::Shutdown(void)
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
if (m_SslContext != nullptr)
{
m_SslContext->NotifyClose();
m_SslContext->ResetSelf();
m_SslContext.reset();
}
Link->Shutdown();
}
Terminated();
}
@ -176,6 +180,12 @@ void cLuaTCPLink::Close(void)
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
if (m_SslContext != nullptr)
{
m_SslContext->NotifyClose();
m_SslContext->ResetSelf();
m_SslContext.reset();
}
Link->Close();
}
@ -228,6 +238,58 @@ AString cLuaTCPLink::StartTLSClient(
}
m_SslContext->SetOwnCert(OwnCert, OwnPrivKey);
}
m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext));
// Start the handshake:
m_SslContext->Handshake();
return "";
}
AString cLuaTCPLink::StartTLSServer(
const AString & a_OwnCertData,
const AString & a_OwnPrivKeyData,
const AString & a_OwnPrivKeyPassword,
const AString & a_StartTLSData
)
{
// Check preconditions:
if (m_SslContext != nullptr)
{
return "TLS is already active on this link";
}
if (a_OwnCertData.empty() || a_OwnPrivKeyData.empty())
{
return "Provide the server certificate and private key";
}
// Create the SSL context:
m_SslContext.reset(new cLinkSslContext(*this));
m_SslContext->Initialize(false);
// Create the peer cert:
auto OwnCert = std::make_shared<cX509Cert>();
int res = OwnCert->Parse(a_OwnCertData.data(), a_OwnCertData.size());
if (res != 0)
{
m_SslContext.reset();
return Printf("Cannot parse server certificate: -0x%x", res);
}
auto OwnPrivKey = std::make_shared<cCryptoKey>();
res = OwnPrivKey->ParsePrivate(a_OwnPrivKeyData.data(), a_OwnPrivKeyData.size(), a_OwnPrivKeyPassword);
if (res != 0)
{
m_SslContext.reset();
return Printf("Cannot parse server private key: -0x%x", res);
}
m_SslContext->SetOwnCert(OwnCert, OwnPrivKey);
m_SslContext->SetSelf(cLinkSslContextWPtr(m_SslContext));
// Push the initial data:
m_SslContext->StoreReceivedData(a_StartTLSData.data(), a_StartTLSData.size());
// Start the handshake:
m_SslContext->Handshake();
@ -254,12 +316,17 @@ void cLuaTCPLink::Terminated(void)
}
// If the link is still open, close it:
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
Link->Close();
m_Link.reset();
cTCPLinkPtr Link = m_Link;
if (Link != nullptr)
{
Link->Close();
m_Link.reset();
}
}
// If the SSL context still exists, free it:
m_SslContext.reset();
}
@ -401,8 +468,29 @@ cLuaTCPLink::cLinkSslContext::cLinkSslContext(cLuaTCPLink & a_Link):
void cLuaTCPLink::cLinkSslContext::SetSelf(cLinkSslContextWPtr & a_Self)
{
m_Self = a_Self;
}
void cLuaTCPLink::cLinkSslContext::ResetSelf(void)
{
m_Self.reset();
}
void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t a_NumBytes)
{
// Hold self alive for the duration of this function
cLinkSslContextPtr Self(m_Self);
m_EncryptedData.append(a_Data, a_NumBytes);
// Try to finish a pending handshake:
@ -418,6 +506,9 @@ void cLuaTCPLink::cLinkSslContext::StoreReceivedData(const char * a_Data, size_t
void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
{
// Hold self alive for the duration of this function
cLinkSslContextPtr Self(m_Self);
// If the handshake didn't complete yet, bail out:
if (!HasHandshaken())
{
@ -429,6 +520,11 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
while ((NumBytes = ReadPlain(Buffer, sizeof(Buffer))) > 0)
{
m_Link.ReceivedCleartextData(Buffer, static_cast<size_t>(NumBytes));
if (m_Self.expired())
{
// The callback closed the SSL context, bail out
return;
}
}
}
@ -438,6 +534,9 @@ void cLuaTCPLink::cLinkSslContext::FlushBuffers(void)
void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void)
{
// Hold self alive for the duration of this function
cLinkSslContextPtr Self(m_Self);
// If the handshake hasn't finished yet, retry:
if (!HasHandshaken())
{
@ -458,6 +557,9 @@ void cLuaTCPLink::cLinkSslContext::TryFinishHandshaking(void)
void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data)
{
// Hold self alive for the duration of this function
cLinkSslContextPtr Self(m_Self);
// If the handshake hasn't completed yet, queue the data:
if (!HasHandshaken())
{
@ -477,6 +579,9 @@ void cLuaTCPLink::cLinkSslContext::Send(const AString & a_Data)
int cLuaTCPLink::cLinkSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes)
{
// Hold self alive for the duration of this function
cLinkSslContextPtr Self(m_Self);
// If there's nothing queued in the buffer, report empty buffer:
if (m_EncryptedData.empty())
{

View File

@ -74,7 +74,27 @@ public:
const AString & a_OwnPrivKeyPassword
);
/** Starts a TLS handshake as a server connection.
Set the server certificate into a_CertData and its corresponding private key to a_OwnPrivKeyData.
a_OwnPrivKeyPassword is the password to be used for decoding PrivKey, empty if not passworded.
a_StartTLSData is any data that should be pushed into the TLS before reading more data from the remote.
This is used mainly for protocols starting TLS in the middle of communication, when the TLS start command
can be received together with the TLS Client Hello message in one OnReceivedData() call, to re-queue the
Client Hello message into the TLS handshake buffer.
Returns empty string on success, non-empty error description on failure. */
AString StartTLSServer(
const AString & a_OwnCertData,
const AString & a_OwnPrivKeyData,
const AString & a_OwnPrivKeyPassword,
const AString & a_StartTLSData
);
protected:
// fwd:
class cLinkSslContext;
typedef SharedPtr<cLinkSslContext> cLinkSslContextPtr;
typedef WeakPtr<cLinkSslContext> cLinkSslContextWPtr;
/** Wrapper around cSslContext that is used when this link is being encrypted by SSL. */
class cLinkSslContext :
public cSslContext
@ -87,9 +107,18 @@ protected:
/** Buffer for storing the outgoing cleartext data until the link has finished handshaking. */
AString m_CleartextData;
/** Shared ownership of self, so that this object can keep itself alive for as long as it needs. */
cLinkSslContextWPtr m_Self;
public:
cLinkSslContext(cLuaTCPLink & a_Link);
/** Shares ownership of self, so that this object can keep itself alive for as long as it needs. */
void SetSelf(cLinkSslContextWPtr & a_Self);
/** Removes the self ownership so that we can detect the SSL closure. */
void ResetSelf(void);
/** Stores the specified block of data into the buffer of the data to be decrypted (incoming from remote).
Also flushes the SSL buffers by attempting to read any data through the SSL context. */
void StoreReceivedData(const char * a_Data, size_t a_NumBytes);
@ -125,7 +154,7 @@ protected:
/** The SSL context used for encryption, if this link uses SSL.
If valid, the link uses encryption through this context. */
UniquePtr<cLinkSslContext> m_SslContext;
cLinkSslContextPtr m_SslContext;
/** Common code called when the link is considered as terminated.

View File

@ -502,6 +502,52 @@ static int tolua_cTCPLink_StartTLSClient(lua_State * L)
/** Binds cLuaTCPLink::StartTLSServer */
static int tolua_cTCPLink_StartTLSServer(lua_State * L)
{
// Function signature:
// LinkInstance:StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData) -> [true] or [nil, ErrMsg]
cLuaState S(L);
if (
!S.CheckParamUserType(1, "cTCPLink") ||
!S.CheckParamString(2, 4) ||
// Param 5 is optional, don't check
!S.CheckParamEnd(6)
)
{
return 0;
}
// Get the link:
cLuaTCPLink * Link;
if (lua_isnil(L, 1))
{
LOGWARNING("cTCPLink:StartTLSServer(): invalid link object. Stack trace:");
S.LogStackTrace();
return 0;
}
Link = *static_cast<cLuaTCPLink **>(lua_touserdata(L, 1));
// Read the params:
AString OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData;
S.GetStackValues(2, OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData);
// Start the TLS handshake:
AString res = Link->StartTLSServer(OwnCert, OwnPrivKey, OwnPrivKeyPassword, StartTLSData);
if (!res.empty())
{
S.PushNil();
S.Push(Printf("Cannot start TLS on link to %s:%d: %s", Link->GetRemoteIP().c_str(), Link->GetRemotePort(), res.c_str()));
return 2;
}
return 1;
}
////////////////////////////////////////////////////////////////////////////////
// cServerHandle bindings (routed through cLuaServerHandle):
@ -616,6 +662,7 @@ void ManualBindings::BindNetwork(lua_State * tolua_S)
tolua_function(tolua_S, "Send", tolua_cTCPLink_Send);
tolua_function(tolua_S, "Shutdown", tolua_cTCPLink_Shutdown);
tolua_function(tolua_S, "StartTLSClient", tolua_cTCPLink_StartTLSClient);
tolua_function(tolua_S, "StartTLSServer", tolua_cTCPLink_StartTLSServer);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cServerHandle");