OVMS3/OVMS.V3/components/ovms_tls/src/ovms_tls.cpp

319 lines
10 KiB
C++

/*
; Project: Open Vehicle Monitor System
; Date: 14th March 2017
;
; Changes:
; 1.0 Initial release
;
; (C) 2011 Michael Stegen / Stegen Electronics
; (C) 2011-2017 Mark Webb-Johnson
; (C) 2011 Sonny Chen @ EPRO/DX
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "ovms_log.h"
static const char *TAG = "tls";
#include "string.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include "ovms_config.h"
#include "ovms_events.h"
#include "ovms_command.h"
#include "ovms_malloc.h"
#include "ovms_tls.h"
#include "mbedtls/x509.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/base64.h"
#include "mbedtls/debug.h"
OvmsTLS MyOvmsTLS __attribute__ ((init_priority (3000)));
void tls_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char *cache = MyOvmsTLS.GetTrustedList();
if (cache != NULL)
{
writer->printf("SSL/TLS has %d trusted CAs, using %d bytes of memory\n",
MyOvmsTLS.Count(),strlen(cache));
}
else
{
writer->printf("SSL/TLS has %d trusted CAs, not currently cached\n", MyOvmsTLS.Count());
}
}
void tls_clear(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
writer->puts("Clearing SSL/TLS trusted CA list");
MyOvmsTLS.Clear();
}
void tls_reload(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
writer->puts("Reloading SSL/TLS trusted CA list");
MyOvmsTLS.Reload();
tls_status(verbosity,writer,cmd,argc,argv);
}
void tls_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
char *buf = (char*)ExternalRamMalloc(1024);
for (auto it = MyOvmsTLS.m_trustlist.begin(); it != MyOvmsTLS.m_trustlist.end(); it++)
{
OvmsTrustedCert* tc = it->second;
mbedtls_x509_crt* crt = tc->GetCert();
writer->printf("%s\n",it->first.c_str());
if ( crt && (mbedtls_x509_crt_info(buf, 1024-1, " ", crt) > 0) )
{
writer->printf("%d byte certificate: %s\n%s\n",crt->raw.len,it->first.c_str(),buf);
}
else
{
writer->printf("Invalid certificate could not be parsed as X509: %s\n\n", it->first.c_str());
}
}
free(buf);
}
OvmsTLS::OvmsTLS()
{
ESP_LOGI(TAG, "Initialising TLS (3000)");
m_trustedcache = NULL;
OvmsCommand* cmd_tls = MyCommandApp.RegisterCommand("tls","SSL/TLS Framework",NULL,"",0,0);
OvmsCommand* cmd_trust = cmd_tls->RegisterCommand("trust","SSL/TLS Trusted CA Framework", tls_status, "", 0, 0, false);
cmd_trust->RegisterCommand("status","Show SSL/TLS Trusted CA status",tls_status, "",0,0);
cmd_trust->RegisterCommand("clear","Clear SSL/TLS Trusted CA list",tls_clear, "",0,0);
cmd_trust->RegisterCommand("reload","Reload SSL/TLS Trusted CA list",tls_reload, "",0,0);
cmd_trust->RegisterCommand("list","Show SSL/TLS Trusted CA list",tls_list, "",0,0);
// Register our callbacks
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(TAG,"config.mounted", std::bind(&OvmsTLS::UpdatedConfig, this, _1, _2));
}
OvmsTLS::~OvmsTLS()
{
Clear();
}
void OvmsTLS::UpdatedConfig(std::string event, void* data)
{
Reload();
}
void OvmsTLS::Clear()
{
for (auto it = m_trustlist.begin(); it != m_trustlist.end(); it++)
{
delete it->second;
}
m_trustlist.clear();
ClearTrustedRaw();
}
void OvmsTLS::Reload()
{
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_debug_set_threshold(1);
#endif //CONFIG_MBEDTLS_DEBUG
Clear();
// Add our embedded trusted CAs
extern const unsigned char usertrust[] asm("_binary_usertrust_crt_start");
extern const unsigned char usertrust_end[] asm("_binary_usertrust_crt_end");
m_trustlist["USERTrust RSA Certification Authority"] = new OvmsTrustedCert(usertrust, usertrust_end - usertrust);
extern const unsigned char digicert_global[] asm("_binary_digicert_global_crt_start");
extern const unsigned char digicert_global_end[] asm("_binary_digicert_global_crt_end");
m_trustlist["DigiCert Global Root CA"] = new OvmsTrustedCert(digicert_global, digicert_global_end - digicert_global);
extern const unsigned char starfield_class2[] asm("_binary_starfield_class2_crt_start");
extern const unsigned char starfield_class2_end[] asm("_binary_starfield_class2_crt_end");
m_trustlist["Starfield Class 2 CA"] = new OvmsTrustedCert(starfield_class2, starfield_class2_end - starfield_class2);
extern const unsigned char baltimore_cybertrust[] asm("_binary_baltimore_cybertrust_crt_start");
extern const unsigned char baltimore_cybertrust_end[] asm("_binary_baltimore_cybertrust_crt_end");
m_trustlist["Baltimore CyberTrust Root CA"] = new OvmsTrustedCert(baltimore_cybertrust, baltimore_cybertrust_end - baltimore_cybertrust);
extern const unsigned char isrg_x1[] asm("_binary_isrg_x1_crt_start");
extern const unsigned char isrg_x1_end[] asm("_binary_isrg_x1_crt_end");
m_trustlist["ISRG X1 CA"] = new OvmsTrustedCert(isrg_x1, isrg_x1_end - isrg_x1);
// Add trusted certs on disk (/store/trustedca)
DIR *dir;
struct dirent *dp;
if ((dir = opendir("/store/trustedca")) != NULL)
{
while ((dp = readdir(dir)) != NULL)
{
std::string path("/store/trustedca/");
path.append(dp->d_name);
FILE* f = fopen(path.c_str(), "r");
if (f != NULL)
{
fseek(f, 0, SEEK_END);
size_t size = ftell(f);
fseek(f, 0, SEEK_SET);
char *buf = (char*)ExternalRamMalloc(size+1);
fread(buf,1,size,f);
buf[size] = 0;
m_trustlist[dp->d_name] = new OvmsTrustedCert(reinterpret_cast<unsigned char*>(buf), size + 1);
free(buf);
}
fclose(f);
}
closedir(dir);
}
ClearTrustedRaw();
BuildTrustedRaw();
}
int OvmsTLS::Count()
{
return m_trustlist.size();
}
char* OvmsTLS::GetTrustedList()
{
if (m_trustedcache == NULL) BuildTrustedRaw();
return m_trustedcache;
}
void OvmsTLS::ClearTrustedRaw()
{
if (m_trustedcache != NULL)
{
ESP_LOGI(TAG, "Clearing trusted CA cache");
free(m_trustedcache);
m_trustedcache = NULL;
}
}
void OvmsTLS::BuildTrustedRaw()
{
const char DASHES[] = "-----";
const char BEGIN[] = "BEGIN";
const char END[] = "END";
const char CERTIFICATE[] = " CERTIFICATE";
if (m_trustedcache != NULL) return;
size_t totalLength = 0u;
size_t count = 0u;
for (auto it = m_trustlist.begin(); it != m_trustlist.end(); ++it)
{
mbedtls_x509_crt* crt = it->second->GetCert();
if (crt)
{
size_t encodedLength;
mbedtls_base64_encode(nullptr, 0, &encodedLength, nullptr, crt->raw.len);
// Add the new lines added every 64 bytes
encodedLength += (encodedLength >> 6);
// Add the header and footer (the NUL will be included as new lines)
encodedLength += sizeof(DASHES) + sizeof(BEGIN) + sizeof(CERTIFICATE) + sizeof(DASHES) - 3;
encodedLength += sizeof(DASHES) + sizeof(END) + sizeof(CERTIFICATE) + sizeof(DASHES) - 3;
totalLength += encodedLength;
++count;
}
}
m_trustedcache = (char*)ExternalRamMalloc(totalLength + 1);
if (m_trustedcache == nullptr)
{
return;
}
auto current = m_trustedcache;
for (auto it = m_trustlist.begin(); it != m_trustlist.end(); ++it)
{
mbedtls_x509_crt* crt = it->second->GetCert();
if (crt)
{
current = std::copy(DASHES, &DASHES[sizeof(DASHES) - 1], current);
current = std::copy(BEGIN, &BEGIN[sizeof(BEGIN) - 1], current);
current = std::copy(CERTIFICATE, &CERTIFICATE[sizeof(CERTIFICATE) - 1], current);
current = std::copy(DASHES, &DASHES[sizeof(DASHES) - 1], current);
*current++ = '\n';
// Encode 48 bytes at a time with new lines between them
const unsigned char* der = crt->raw.p;
size_t derLength = crt->raw.len;
while (derLength)
{
size_t pemLength;
size_t inLength = derLength > 48 ? 48 : derLength;
mbedtls_base64_encode((unsigned char*)current, 65, &pemLength, der, inLength);
current += pemLength;
*current++ = '\n';
der += inLength;
derLength -= inLength;
}
current = std::copy(DASHES, &DASHES[sizeof(DASHES) - 1], current);
current = std::copy(END, &END[sizeof(END) - 1], current);
current = std::copy(CERTIFICATE, &CERTIFICATE[sizeof(CERTIFICATE) - 1], current);
current = std::copy(DASHES, &DASHES[sizeof(DASHES) - 1], current);
*current++ = '\n';
}
}
*current = '\0';
ESP_LOGI(TAG, "Built trusted CA cache (%d entries, %d bytes)",count,totalLength);
}
OvmsTrustedCert::OvmsTrustedCert(const unsigned char* cert, size_t length) :
m_cert(reinterpret_cast<mbedtls_x509_crt*>(ExternalRamMalloc(sizeof(mbedtls_x509_crt))))
{
mbedtls_x509_crt_init(m_cert);
if (mbedtls_x509_crt_parse(m_cert, cert, length) != 0)
{
ESP_LOGE(TAG,"Invalid %d byte trusted CA - ignored", length);
free(m_cert);
m_cert = nullptr;
}
else
{
ESP_LOGD(TAG,"Registered %d byte trusted CA", length);
}
}
OvmsTrustedCert::~OvmsTrustedCert()
{
if (m_cert)
{
ESP_LOGD(TAG,"Freeing trusted certificate");
mbedtls_x509_crt_free(m_cert);
free(m_cert);
}
}
mbedtls_x509_crt* OvmsTrustedCert::GetCert()
{
return m_cert;
}