322 lines
10 KiB
C++
322 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);
|
|
|
|
extern const unsigned char ovms_ca[] asm("_binary_ovms_ca_crt_start");
|
|
extern const unsigned char ovms_ca_end[] asm("_binary_ovms_ca_crt_end");
|
|
m_trustlist["OVMS Bit-Cloud.de CA"] = new OvmsTrustedCert(ovms_ca, ovms_ca_end - ovms_ca);
|
|
|
|
// 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;
|
|
}
|