Skip to content

Commit

Permalink
Merge pull request #25 from fledge-power/mz_refactoring
Browse files Browse the repository at this point in the history
- Implemented basic TLS support
  • Loading branch information
aklira authored Nov 7, 2022
2 parents a3028c6 + 6ea6749 commit d3e7ad6
Show file tree
Hide file tree
Showing 15 changed files with 623 additions and 28 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
1.0.0-rc1
3 changes: 3 additions & 0 deletions include/iec104.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class IEC104Server
static void connectionEventHandler(void* parameter, IMasterConnection con,
CS104_PeerConnectionEvent event);
CS104_Slave m_slave{};
TLSConfiguration m_tlsConfig = nullptr;
CS101_AppLayerParameters alParams;
std::string m_name;
Logger* m_log;
Expand All @@ -142,6 +143,8 @@ class IEC104Server
bool m_started = false;
std::thread* m_monitoringThread = nullptr;
void _monitoringThread();

bool createTLSConfiguration();
};

#endif
11 changes: 11 additions & 0 deletions include/iec104_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class IEC104Config

void importProtocolConfig(const string& protocolConfig);
void importExchangeConfig(const string& exchangeConfig);
void importTlsConfig(const string& tlsConfig);

std::map<int, std::map<int, IEC104DataPoint*>>* getExchangeDefinitions() {return m_exchangeDefinitions;};

Expand Down Expand Up @@ -55,6 +56,11 @@ class IEC104Config

string& CmdDest() {return m_cmdDest;};

std::string& GetPrivateKey() {return m_privateKey;};
std::string& GetOwnCertificate() {return m_ownCertificate;};
std::vector<std::string>& GetRemoteCertificates() {return m_remoteCertificates;};
std::vector<std::string>& GetCaCertificates() {return m_caCertificates;};

private:

static bool isValidIPAddress(const string& addrStr);
Expand Down Expand Up @@ -96,6 +102,11 @@ class IEC104Config

std::map<int, std::map<int, IEC104DataPoint*>>* m_exchangeDefinitions = nullptr;
std::map<int, int> m_allowedOriginators;

std::string m_privateKey;
std::string m_ownCertificate;
std::vector<std::string> m_remoteCertificates;
std::vector<std::string> m_caCertificates;
};

#endif /* IEC104_CONFIG_H */
146 changes: 141 additions & 5 deletions src/iec104.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <utils.h>


using namespace std;
Expand Down Expand Up @@ -57,17 +58,136 @@ IEC104Server::m_getDataPoint(int ca, int ioa, int typeId)
return dp;
}

bool
IEC104Server::createTLSConfiguration()
{
TLSConfiguration tlsConfig = TLSConfiguration_create();

if (tlsConfig)
{
bool tlsConfigOk = true;

string certificateStore = getDataDir() + string("/etc/certs/");

if (m_config->GetOwnCertificate().length() == 0 || m_config->GetPrivateKey().length() == 0) {
Logger::getLogger()->error("No private key and/or certificate configured for client");
tlsConfigOk = false;
}

if (m_config->GetOwnCertificate().empty() == false)
{
string ownCertFile = certificateStore + m_config->GetOwnCertificate();

if (access(ownCertFile.c_str(), R_OK) == 0) {

if (TLSConfiguration_setOwnCertificateFromFile(tlsConfig, ownCertFile.c_str()) == false) {
Logger::getLogger()->error("Failed to load own certificate from file: %s", ownCertFile.c_str());
tlsConfigOk = false;
}

}
else {
Logger::getLogger()->error("Failed to access own certificate file: %s", ownCertFile.c_str());
tlsConfigOk = false;
}
}

if (m_config->GetPrivateKey().empty() == false)
{
string privateKeyFile = certificateStore + m_config->GetPrivateKey();

if (access(privateKeyFile.c_str(), R_OK) == 0) {

if (TLSConfiguration_setOwnKeyFromFile(tlsConfig, privateKeyFile.c_str(), NULL) == false) {
Logger::getLogger()->error("Failed to load private key from file: %s", privateKeyFile.c_str());
tlsConfigOk = false;
}

}
else {
Logger::getLogger()->error("Failed to access private key file: %s", privateKeyFile.c_str());
tlsConfigOk = false;
}
}

if (m_config->GetRemoteCertificates().size() > 0) {
TLSConfiguration_setAllowOnlyKnownCertificates(tlsConfig, true);

for (std::string& remoteCert : m_config->GetRemoteCertificates())
{
string remoteCertFile = certificateStore + remoteCert;

if (access(remoteCertFile.c_str(), R_OK) == 0) {
if (TLSConfiguration_addAllowedCertificateFromFile(tlsConfig, remoteCertFile.c_str()) == false) {
Logger::getLogger()->warn("Failed to load remote certificate file: %s -> ignore certificate", remoteCertFile.c_str());
}
}
else {
Logger::getLogger()->warn("Failed to access remote certificate file: %s -> ignore certificate", remoteCertFile.c_str());
}

}
}
else {
TLSConfiguration_setAllowOnlyKnownCertificates(tlsConfig, false);
}

if (m_config->GetCaCertificates().size() > 0) {
TLSConfiguration_setChainValidation(tlsConfig, true);

for (std::string& caCert : m_config->GetCaCertificates())
{
string caCertFile = certificateStore + caCert;

if (access(caCertFile.c_str(), R_OK) == 0) {
if (TLSConfiguration_addCACertificateFromFile(tlsConfig, caCertFile.c_str()) == false) {
Logger::getLogger()->warn("Failed to load CA certificate file: %s -> ignore certificate", caCertFile.c_str());
}
}
else {
Logger::getLogger()->warn("Failed to access CA certificate file: %s -> ignore certificate", caCertFile.c_str());
}

}
}
else {
TLSConfiguration_setChainValidation(tlsConfig, false);
}

if (tlsConfigOk) {
m_tlsConfig = tlsConfig;
}
else {
TLSConfiguration_destroy(tlsConfig);
m_tlsConfig = nullptr;
}

return tlsConfigOk;
}
else {
return false;
}
}

void
IEC104Server::setJsonConfig(const std::string& stackConfig,
const std::string& dataExchangeConfig,
const std::string& dataExchangeConfig,
const std::string& tlsConfig)
{
m_config->importExchangeConfig(dataExchangeConfig);
m_config->importProtocolConfig(stackConfig);
m_config->importTlsConfig(tlsConfig);

m_exchangeDefinitions = *m_config->getExchangeDefinitions();

m_slave = CS104_Slave_create(m_config->AsduQueueSize(), 100);
if (m_config->UseTLS()) {
if (createTLSConfiguration()) {
m_slave = CS104_Slave_createSecure(m_config->AsduQueueSize(), 100, m_tlsConfig);
}
}
else {
m_slave = CS104_Slave_create(m_config->AsduQueueSize(), 100);
}

if (m_slave)
{
Expand Down Expand Up @@ -180,7 +300,14 @@ IEC104Server::configure(const ConfigCategory* config)

const std::string dataExchange = config->getValue("exchanged_data");

const std::string tlsConfig = std::string("");
std::string tlsConfig = "";

if (config->itemExists("tls_conf") == false) {
m_log->error("Missing TLS configuration");
}
else {
tlsConfig = config->getValue("tls_conf");
}

setJsonConfig(protocolStack, dataExchange, tlsConfig);
}
Expand Down Expand Up @@ -1025,7 +1152,10 @@ IEC104Server::send(const vector<Reading*>& readings)
// update internal value
m_updateDataPoint(dp, (IEC60870_5_TypeID)type, value, ts, qd);

if (cot == CS101_COT_PERIODIC || cot == CS101_COT_SPONTANEOUS) {
if (cot == CS101_COT_PERIODIC || cot == CS101_COT_SPONTANEOUS ||
cot == CS101_COT_RETURN_INFO_REMOTE || cot == CS101_COT_RETURN_INFO_LOCAL ||
cot == CS101_COT_BACKGROUND_SCAN)
{
m_enqueueSpontDatapoint(dp, cot, (IEC60870_5_TypeID)type);
}
}
Expand Down Expand Up @@ -1597,6 +1727,12 @@ IEC104Server::stop()
m_slave = nullptr;
}

if (m_tlsConfig)
{
TLSConfiguration_destroy(m_tlsConfig);
m_tlsConfig = nullptr;
}

if (m_started == true)
{
m_started = false;
Expand All @@ -1607,4 +1743,4 @@ IEC104Server::stop()
m_monitoringThread = nullptr;
}
}
}
}
59 changes: 59 additions & 0 deletions src/iec104_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,65 @@ IEC104Config::importExchangeConfig(const string& exchangeConfig)
m_exchangeConfigComplete = true;
}

void
IEC104Config::importTlsConfig(const string& tlsConfig)
{
Document document;

if (document.Parse(const_cast<char*>(tlsConfig.c_str())).HasParseError()) {
Logger::getLogger()->fatal("Parsing error in TLS configuration");

return;
}

if (!document.IsObject())
return;

if (!document.HasMember("tls_conf") || !document["tls_conf"].IsObject()) {
return;
}

const Value& tlsConf = document["tls_conf"];

if (tlsConf.HasMember("private_key") && tlsConf["private_key"].IsString()) {
m_privateKey = tlsConf["private_key"].GetString();
}

if (tlsConf.HasMember("own_cert") && tlsConf["own_cert"].IsString()) {
m_ownCertificate = tlsConf["own_cert"].GetString();
}

if (tlsConf.HasMember("ca_certs") && tlsConf["ca_certs"].IsArray()) {

const Value& caCerts = tlsConf["ca_certs"];

for (const Value& caCert : caCerts.GetArray()) {
if (caCert.HasMember("cert_file")) {
if (caCert["cert_file"].IsString()) {
string certFileName = caCert["cert_file"].GetString();

m_caCertificates.push_back(certFileName);
}
}
}
}

if (tlsConf.HasMember("remote_certs") && tlsConf["remote_certs"].IsArray()) {

const Value& remoteCerts = tlsConf["remote_certs"];

for (const Value& remoteCert : remoteCerts.GetArray()) {
if (remoteCert.HasMember("cert_file")) {
if (remoteCert["cert_file"].IsString()) {
string certFileName = remoteCert["cert_file"].GetString();

m_remoteCertificates.push_back(certFileName);
}
}
}
}
}

int IEC104Config::TcpPort()
{
if (m_tcpPort == -1) {
Expand Down
24 changes: 18 additions & 6 deletions src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,17 +175,29 @@ static const char* default_config = QUOTE({
}
})
},
"tls" : {
"tls_conf" : {
"description" : "tls parameters",
"type" : "JSON",
"displayName" : "TLS parameters",
"order" : "4",
"default" : QUOTE({
"tls_conf:" : {
"private_key" : "server-key.pem",
"server_cert" : "server.cer",
"ca_cert" : "root.cer"
}
"tls_conf" : {
"private_key" : "iec104_server.key",
"own_cert" : "iec104_server.cer",
"ca_certs" : [
{
"cert_file": "iec104_ca.cer"
},
{
"cert_file": "iec104_ca2.cer"
}
],
"remote_certs" : [
{
"cert_file": "iec104_client.cer"
}
]
}
})
}
});
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ include_directories(${FLEDGE_INCLUDE_DIRS})
# Add Fledge lib path
link_directories(${FLEDGE_LIB_DIRS})

# copy certificates to output
file(COPY data DESTINATION ${CMAKE_BINARY_DIR}/tests)

# Link runTests with what we want to test and the GTest and pthread library
add_executable(RunTests ${unittests} ${SOURCES} version.h)

Expand Down
Binary file added tests/data/etc/certs/iec104_ca.cer
Binary file not shown.
Binary file added tests/data/etc/certs/iec104_client.cer
Binary file not shown.
27 changes: 27 additions & 0 deletions tests/data/etc/certs/iec104_client.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxAMUXdnem99n5J8Z8Wa0fdPtoMCTdkQJrOO6WJ4XePrpQgHU
HDziSmdIZDDkpJ3Ey0Byy+b+iiRDmOuIZGSCsI0ehggWaia12h2osUK0BLyThuZ/
RQw54K0dy61eviNaYsftiBcxHYKyKmWch/wLLYxdd1qLzd0reAbSUaHDkDzrj9hO
8qr7DhKpqx7PoVh1gFhAKPKuY6b+4xqv5eZvt8QflQTWYGxaxmUIEinqCnzh1l5d
tp0rhnBsz9Y4y8dXjh0m7pbXmRNY6opMxXatqgYEqsntLy1N6x7DvWLBqtVvEmox
Tc5bbAoRW3eEToClDdFQBzLsMVcSEX8vwttk3QIDAQABAoIBABHr1ijeiqPlwTH9
+flAUrBOeCOCd/kQL3JHP/pqOestxbXrROFwD6CN4OiIL999LUkIE3bhH9SxjByn
LElBh1FtFaVbh/EcqPPQUmQinSLxuutSl8BQZdpM+bRtnYP054awkN8of60bDf8i
WzVzrfH0K3eGJ9Iirp7CwOgFykOdpQyxsI+HG8grcwA87x1ZsAIfHhiKmQByliNl
BkbJmYBOtfVgXje5QdxTptlTNljFSbZcaCXv1P3aOqctcgJMQjg0T+E37Y8Cav80
6SuXbpv/cdacG695MAT7Vtywue0Axh59DvxAzc+deyQT70Hzw+Mo6pgi0clFnwzU
Y5ViDWECgYEAxxhRKzpz7klnmGob5CZvrbqDfC3JUEOxKH0e342S/HmT05bTI21w
N8A0KStNjQXS1mmkAkY/OO1Zutmf6yjqsxAIEO5UMTCSEP7YLRB7qBdN7dOt3JaK
4wxErMCljdT68Vj5Qj8YzIXJkWPk871oFTvVNe2qxgrCUigE5ai2I8cCgYEA/Akv
E0L+2uXayEucEamzO3n9xVziNanjyHilnJJvduvO9gd+crBbxSKqaXSdfPnp2mSa
+e3N7elxP2b/kPrGkzZekSaMh1nPH4Upu+ISK117r1x+vmnxZHRpehrVh1QzOQ5p
Ljt+GaXa3ur3P/6uW5KMbtGGW6MEgDwLMLvpqjsCgYA5pnfyfYWOTWEbCDa1VM/n
zWc/cP6nKELHR5vF/fe+9fFxRm4zBwCElDpGZYyaNkJ75bEhG3g5Irll2phs/rcf
TJgZVvm4GKljFHhCbFByNvVQ1Ye1pT3oSugj4dDOhgp4Elxy61Rh/KeGWxez4Heg
FmhBqmVV3U2xfncUjUrYhwKBgQCKtPM3gpOIHSA/Q31tKxv9C7JiQDAuoIU/+0YJ
2X2G0VhhhtZMgErBP8bRquBRu6i8DMpN6lZ/LQ6qeiEExT8sHawF7lVA2GhpTHwf
btfZDeXYKOuIF/5F7ttt2/7QL8LRD+FLFGrd6q1+KYpRqfSDaS/ofV+YZys+98yg
0YpTqQKBgQCWJpV2ySgXcKJzAUh14VNpLTRzJOMSpsU576nwz0TLUREVjiH6rvKr
gxllDEe1bVNMEekaZ+dOqiJX+4aTnEbIrEXki5Dvz0vq8biImW9RRdPEHgYVTv/d
qBOPHiIq2JiY6abD9XNPM3VQ/z8em6/4mkC8COCJRd2mA89FOYRxOQ==
-----END RSA PRIVATE KEY-----
Binary file added tests/data/etc/certs/iec104_server.cer
Binary file not shown.
Loading

0 comments on commit d3e7ad6

Please sign in to comment.