You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1064 lines
30 KiB
C
1064 lines
30 KiB
C
/*
|
|
* tls_mbedtls.c
|
|
*
|
|
* TLS API for TCP/IP protocol stacks
|
|
*
|
|
* Copyright 2017-2022 Michael Zillgith
|
|
*
|
|
* Implementation of the TLS abstraction layer for mbedtls
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "tls_socket.h"
|
|
#include "hal_thread.h"
|
|
#include "lib_memory.h"
|
|
#include "hal_time.h"
|
|
#include "linked_list.h"
|
|
|
|
#include "mbedtls/platform.h"
|
|
#include "mbedtls/entropy.h"
|
|
#include "mbedtls/ctr_drbg.h"
|
|
#include "mbedtls/certs.h"
|
|
#include "mbedtls/x509.h"
|
|
#include "mbedtls/ssl.h"
|
|
#include "mbedtls/net_sockets.h"
|
|
#include "mbedtls/error.h"
|
|
#include "mbedtls/debug.h"
|
|
#include "mbedtls/ssl_cache.h"
|
|
|
|
#define SEC_EVENT_ALARM 2
|
|
#define SEC_EVENT_WARNING 1
|
|
#define SEC_EVENT_INFO 0
|
|
|
|
#ifndef CONFIG_DEBUG_TLS
|
|
#define CONFIG_DEBUG_TLS 0
|
|
#endif
|
|
|
|
#if (CONFIG_DEBUG_TLS == 1)
|
|
#define DEBUG_PRINT(appId, fmt, ...) fprintf(stderr, "%s: " fmt, appId, ## __VA_ARGS__);
|
|
#else
|
|
#define DEBUG_PRINT(fmt, ...) {do {} while(0);}
|
|
#endif
|
|
|
|
|
|
struct sTLSConfiguration {
|
|
mbedtls_entropy_context entropy;
|
|
mbedtls_ctr_drbg_context ctr_drbg;
|
|
|
|
mbedtls_x509_crt ownCertificate;
|
|
mbedtls_pk_context ownKey;
|
|
|
|
mbedtls_x509_crt cacerts;
|
|
|
|
mbedtls_x509_crl crl;
|
|
|
|
mbedtls_ssl_config conf;
|
|
LinkedList /* <mbedtls_x509_crt*> */ allowedCertificates;
|
|
|
|
/* session cache for server */
|
|
mbedtls_ssl_cache_context cache;
|
|
|
|
/* client side cached session */
|
|
mbedtls_ssl_session* savedSession;
|
|
uint64_t savedSessionTime;
|
|
|
|
bool chainValidation;
|
|
bool allowOnlyKnownCertificates;
|
|
|
|
/* TLS session renegotiation interval in milliseconds */
|
|
int renegotiationTimeInMs;
|
|
|
|
/* TLS minimum version allowed (default: TLS_VERSION_TLS_1_0) */
|
|
TLSConfigVersion minVersion;
|
|
|
|
/* TLS minimum version allowed (default: TLS_VERSION_TLS_1_2) */
|
|
TLSConfigVersion maxVersion;
|
|
|
|
TLSConfiguration_EventHandler eventHandler;
|
|
void* eventHandlerParameter;
|
|
|
|
/* time of the last CRL update */
|
|
uint64_t crlUpdated;
|
|
|
|
bool setupComplete;
|
|
|
|
bool useSessionResumption;
|
|
int sessionResumptionInterval; /* session resumption interval in seconds */
|
|
};
|
|
|
|
struct sTLSSocket {
|
|
mbedtls_ssl_context ssl;
|
|
Socket socket;
|
|
mbedtls_ssl_config conf;
|
|
TLSConfiguration tlsConfig;
|
|
bool storePeerCert;
|
|
uint8_t* peerCert;
|
|
int peerCertLength;
|
|
|
|
/* time of last session renegotiation (used to calculate next renegotiation time) */
|
|
uint64_t lastRenegotiationTime;
|
|
|
|
/* time of the last CRL update */
|
|
uint64_t crlUpdated;
|
|
};
|
|
|
|
static void
|
|
raiseSecurityEvent(TLSConfiguration config, TLSEventLevel eventCategory, int eventCode, const char* message, TLSSocket socket)
|
|
{
|
|
if (config->eventHandler) {
|
|
config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
compareCertificates(mbedtls_x509_crt *crt1, mbedtls_x509_crt *crt2)
|
|
{
|
|
if (crt1 != NULL && crt2 != NULL) {
|
|
|
|
if (crt1->sig.len == crt2->sig.len) {
|
|
if (memcmp(crt1->sig.p, crt2->sig.p, crt1->sig.len) == 0)
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int
|
|
verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth, uint32_t *flags)
|
|
{
|
|
TLSSocket self = (TLSSocket) parameter;
|
|
|
|
DEBUG_PRINT("TLS", "Verify cert: depth %i\n", certificate_depth);
|
|
|
|
DEBUG_PRINT("TLS", " flags: %08x\n", *flags);
|
|
|
|
char buffer[1024];
|
|
|
|
mbedtls_x509_crt_info(buffer, 1023, " ", crt);
|
|
|
|
DEBUG_PRINT("TLS", "%s\n", buffer);
|
|
|
|
if (self->tlsConfig->chainValidation == false) {
|
|
if (certificate_depth != 0)
|
|
*flags = 0;
|
|
}
|
|
|
|
if (certificate_depth == 0) {
|
|
if (self->tlsConfig->allowOnlyKnownCertificates) {
|
|
|
|
DEBUG_PRINT("TLS", "Check against list of allowed certs\n");
|
|
|
|
bool certMatches = false;
|
|
|
|
LinkedList certList = LinkedList_getNext(self->tlsConfig->allowedCertificates);
|
|
|
|
while (certList) {
|
|
mbedtls_x509_crt* allowedCert = (mbedtls_x509_crt*) LinkedList_getData(certList);
|
|
|
|
DEBUG_PRINT("TLS", "Compare With:\n");
|
|
mbedtls_x509_crt_info(buffer, 1023, " ", allowedCert);
|
|
DEBUG_PRINT("TLS", "%s\n", buffer);
|
|
|
|
if (compareCertificates(allowedCert, crt)) {
|
|
certMatches = true;
|
|
break;
|
|
}
|
|
|
|
certList = LinkedList_getNext(certList);
|
|
}
|
|
|
|
if (certMatches)
|
|
*flags = 0;
|
|
else {
|
|
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: certificate validation: trusted individual certificate not available", self);
|
|
|
|
*flags |= MBEDTLS_X509_BADCERT_OTHER;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (self->storePeerCert) {
|
|
|
|
if (*flags == 0) {
|
|
|
|
self->peerCertLength = 0;
|
|
self->peerCert = (uint8_t*) GLOBAL_MALLOC(crt->raw.len);
|
|
|
|
if (self->peerCert) {
|
|
self->peerCertLength = (int)crt->raw.len;
|
|
memcpy(self->peerCert, crt->raw.p, self->peerCertLength);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Finish configuration when used the first time.
|
|
*/
|
|
static bool
|
|
TLSConfiguration_setupComplete(TLSConfiguration self)
|
|
{
|
|
if (self->setupComplete == false) {
|
|
mbedtls_ssl_conf_ca_chain( &(self->conf), &(self->cacerts), &(self->crl) );
|
|
|
|
if (self->ownCertificate.version > 0) {
|
|
int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey));
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned -0x%x\n", -ret);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (self->useSessionResumption) {
|
|
if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) {
|
|
|
|
}
|
|
else {
|
|
mbedtls_ssl_cache_init( &(self->cache) );
|
|
|
|
self->cache.timeout = self->sessionResumptionInterval;
|
|
|
|
mbedtls_ssl_conf_session_cache( &(self->conf), &(self->cache),
|
|
mbedtls_ssl_cache_get,
|
|
mbedtls_ssl_cache_set );
|
|
}
|
|
}
|
|
|
|
self->setupComplete = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TLSConfiguration
|
|
TLSConfiguration_create()
|
|
{
|
|
TLSConfiguration self = (TLSConfiguration) GLOBAL_CALLOC(1, sizeof(struct sTLSConfiguration));
|
|
|
|
if (self != NULL) {
|
|
mbedtls_ssl_config_init( &(self->conf) );
|
|
mbedtls_x509_crt_init( &(self->ownCertificate) );
|
|
mbedtls_x509_crt_init( &(self->cacerts) );
|
|
mbedtls_x509_crl_init( &(self->crl) );
|
|
mbedtls_pk_init( &(self->ownKey) );
|
|
mbedtls_entropy_init( &(self->entropy) );
|
|
mbedtls_ctr_drbg_init( &(self->ctr_drbg) );
|
|
|
|
/* WARINING is fixed to server! */
|
|
mbedtls_ssl_config_defaults( &(self->conf),
|
|
MBEDTLS_SSL_IS_SERVER,
|
|
MBEDTLS_SSL_TRANSPORT_STREAM,
|
|
MBEDTLS_SSL_PRESET_DEFAULT );
|
|
|
|
mbedtls_ctr_drbg_seed( &(self->ctr_drbg), mbedtls_entropy_func, &(self->entropy), NULL, 0);
|
|
mbedtls_ssl_conf_rng( &(self->conf), mbedtls_ctr_drbg_random, &(self->ctr_drbg) );
|
|
|
|
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
|
|
|
|
mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION);
|
|
|
|
/* static int hashes[] = {3,4,5,6,7,8,0}; */
|
|
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */
|
|
|
|
self->minVersion = TLS_VERSION_TLS_1_0;
|
|
self->maxVersion = TLS_VERSION_NOT_SELECTED;
|
|
|
|
self->renegotiationTimeInMs = -1; /* no automatic renegotiation */
|
|
|
|
self->allowedCertificates = LinkedList_create();
|
|
|
|
/* default behavior is to allow all certificates that are signed by the CA */
|
|
self->chainValidation = true;
|
|
self->allowOnlyKnownCertificates = false;
|
|
self->setupComplete = false;
|
|
|
|
self->eventHandler = NULL;
|
|
self->eventHandlerParameter = NULL;
|
|
|
|
self->useSessionResumption = true;
|
|
self->sessionResumptionInterval = 21600; /* default value: 6h */
|
|
self->savedSession = NULL;
|
|
self->savedSessionTime = 0;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setClientMode(TLSConfiguration self)
|
|
{
|
|
self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_enableSessionResumption(TLSConfiguration self, bool enable)
|
|
{
|
|
self->useSessionResumption = enable;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setSessionResumptionInterval(TLSConfiguration self, int intervalInSeconds)
|
|
{
|
|
self->sessionResumptionInterval = intervalInSeconds;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter)
|
|
{
|
|
self->eventHandler = handler;
|
|
self->eventHandlerParameter = parameter;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setMinTlsVersion(TLSConfiguration self, TLSConfigVersion version)
|
|
{
|
|
self->minVersion = version;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setMaxTlsVersion(TLSConfiguration self, TLSConfigVersion version)
|
|
{
|
|
self->maxVersion = version;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setChainValidation(TLSConfiguration self, bool value)
|
|
{
|
|
self->chainValidation = value;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value)
|
|
{
|
|
self->allowOnlyKnownCertificates = value;
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen)
|
|
{
|
|
int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen);
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename)
|
|
{
|
|
int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename);
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned -0x%x\n", -ret);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword)
|
|
{
|
|
int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*) keyPassword, (keyPassword == NULL) ? 0 : strlen(keyPassword));
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned -0x%x\n", -ret);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword)
|
|
{
|
|
int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword);
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned -0x%x\n", -ret);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certificate, int certLen)
|
|
{
|
|
mbedtls_x509_crt* cert = (mbedtls_x509_crt*) GLOBAL_CALLOC(1, sizeof(mbedtls_x509_crt));
|
|
|
|
int ret = mbedtls_x509_crt_parse(cert, certificate, certLen);
|
|
|
|
if (ret == 0) {
|
|
LinkedList_add(self->allowedCertificates, cert);
|
|
return true;
|
|
}
|
|
else {
|
|
GLOBAL_FREEMEM(cert);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename)
|
|
{
|
|
mbedtls_x509_crt* cert = (mbedtls_x509_crt*) GLOBAL_CALLOC(1, sizeof(mbedtls_x509_crt));
|
|
|
|
int ret = mbedtls_x509_crt_parse_file(cert, filename);
|
|
|
|
if (ret == 0) {
|
|
LinkedList_add(self->allowedCertificates, cert);
|
|
return true;
|
|
}
|
|
else {
|
|
GLOBAL_FREEMEM(cert);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, int certLen)
|
|
{
|
|
int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen);
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
|
|
return false;
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename)
|
|
{
|
|
int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename);
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
static void
|
|
udpatedCRL(TLSConfiguration self)
|
|
{
|
|
self->crlUpdated = Hal_getTimeInMs();
|
|
|
|
/* We need to clean-up resumption cache (if enabled) to make sure we renegotiate as CRL may have changed data */
|
|
if (self->useSessionResumption == false)
|
|
return;
|
|
|
|
if (self->conf.endpoint == MBEDTLS_SSL_IS_SERVER)
|
|
{
|
|
mbedtls_ssl_cache_entry *cur = self->cache.chain;
|
|
|
|
while (cur) {
|
|
cur->timestamp = 0;
|
|
cur = cur->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen)
|
|
{
|
|
int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen);
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret);
|
|
}
|
|
else {
|
|
udpatedCRL(self);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
bool
|
|
TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename)
|
|
{
|
|
int ret = mbedtls_x509_crl_parse_file(&(self->crl), filename);
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret);
|
|
}
|
|
else {
|
|
udpatedCRL(self);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_resetCRL(TLSConfiguration self)
|
|
{
|
|
mbedtls_x509_crl_free(&(self->crl));
|
|
mbedtls_x509_crl_init(&(self->crl));
|
|
self->crlUpdated = Hal_getTimeInMs();
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs)
|
|
{
|
|
self->renegotiationTimeInMs = timeInMs;
|
|
}
|
|
|
|
void
|
|
TLSConfiguration_destroy(TLSConfiguration self)
|
|
{
|
|
if (self->useSessionResumption) {
|
|
if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) {
|
|
if (self->savedSession) {
|
|
mbedtls_ssl_session_free(self->savedSession);
|
|
GLOBAL_FREEMEM(self->savedSession);
|
|
}
|
|
}
|
|
else {
|
|
mbedtls_ssl_cache_free(&(self->cache));
|
|
}
|
|
}
|
|
|
|
mbedtls_x509_crt_free(&(self->ownCertificate));
|
|
mbedtls_x509_crt_free(&(self->cacerts));
|
|
mbedtls_x509_crl_free(&(self->crl));
|
|
mbedtls_pk_free(&(self->ownKey));
|
|
mbedtls_ssl_config_free(&(self->conf));
|
|
|
|
LinkedList certElem = LinkedList_getNext(self->allowedCertificates);
|
|
|
|
while (certElem) {
|
|
mbedtls_x509_crt* cert = (mbedtls_x509_crt*) LinkedList_getData(certElem);
|
|
|
|
mbedtls_x509_crt_free(cert);
|
|
|
|
certElem = LinkedList_getNext(certElem);
|
|
}
|
|
|
|
LinkedList_destroy(self->allowedCertificates);
|
|
|
|
GLOBAL_FREEMEM(self);
|
|
}
|
|
|
|
static void
|
|
createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket)
|
|
{
|
|
if (config->eventHandler == NULL)
|
|
return;
|
|
|
|
switch (ret) {
|
|
case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG:
|
|
raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: Algorithm not supported", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN:
|
|
raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: no matching TLS ciphers", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE:
|
|
raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Alarm: Algorithm not supported", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Alarm: Unsecure communication", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Alarm: certificate unavailable", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Alarm: Bad certificate", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Alarm: TLS certificate size exceeded", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: certificate validation: certificate signature could not be validated", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Alarm: Certificate required", socket);
|
|
break;
|
|
|
|
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
|
|
{
|
|
if (flags & MBEDTLS_X509_BADCERT_EXPIRED) {
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Alarm: expired certificate", socket);
|
|
}
|
|
else if (flags & MBEDTLS_X509_BADCERT_REVOKED) {
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Alarm: revoked certificate", socket);
|
|
}
|
|
else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Alarm: Certificate validation: CA certificate not available", socket);
|
|
}
|
|
else if (flags & MBEDTLS_X509_BADCERT_OTHER) {
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Certificate not configured", socket);
|
|
}
|
|
else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) {
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Insufficient key length", socket);
|
|
}
|
|
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: Certificate verification failed", socket);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Alarm: handshake failed for unknown reason", socket);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
readFunction(void* ctx, unsigned char* buf, size_t len)
|
|
{
|
|
int ret = Socket_read((Socket) ctx, buf, (int)len);
|
|
|
|
if ((ret == 0) && (len > 0)) {
|
|
return MBEDTLS_ERR_SSL_WANT_READ;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
writeFunction(void* ctx, unsigned char* buf, size_t len)
|
|
{
|
|
return Socket_write((Socket)ctx, buf, (int)len);
|
|
}
|
|
|
|
static TLSConfigVersion
|
|
getTLSVersion(int majorVersion, int minorVersion)
|
|
{
|
|
if (majorVersion != 3) {
|
|
return TLS_VERSION_NOT_SELECTED;
|
|
}
|
|
else {
|
|
switch (minorVersion) {
|
|
case 0:
|
|
return TLS_VERSION_SSL_3_0;
|
|
case 1:
|
|
return TLS_VERSION_TLS_1_0;
|
|
case 2:
|
|
return TLS_VERSION_TLS_1_1;
|
|
case 3:
|
|
return TLS_VERSION_TLS_1_2;
|
|
case 4:
|
|
return TLS_VERSION_TLS_1_3;
|
|
default:
|
|
return TLS_VERSION_NOT_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
getMajorVersion(TLSConfigVersion version)
|
|
{
|
|
switch(version) {
|
|
case TLS_VERSION_NOT_SELECTED:
|
|
return 0;
|
|
case TLS_VERSION_SSL_3_0:
|
|
case TLS_VERSION_TLS_1_0:
|
|
case TLS_VERSION_TLS_1_1:
|
|
case TLS_VERSION_TLS_1_2:
|
|
case TLS_VERSION_TLS_1_3:
|
|
return 3;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
getMinorVersion(TLSConfigVersion version)
|
|
{
|
|
switch(version) {
|
|
case TLS_VERSION_NOT_SELECTED:
|
|
return 0;
|
|
case TLS_VERSION_SSL_3_0:
|
|
return 0;
|
|
case TLS_VERSION_TLS_1_0:
|
|
return 1;
|
|
case TLS_VERSION_TLS_1_1:
|
|
return 2;
|
|
case TLS_VERSION_TLS_1_2:
|
|
return 3;
|
|
case TLS_VERSION_TLS_1_3:
|
|
return 4;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
TLSSocket
|
|
TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert)
|
|
{
|
|
TLSSocket self = (TLSSocket) GLOBAL_CALLOC(1, sizeof(struct sTLSSocket));
|
|
|
|
if (self)
|
|
{
|
|
self->socket = socket;
|
|
self->tlsConfig = configuration;
|
|
self->storePeerCert = storeClientCert;
|
|
self->peerCert = NULL;
|
|
self->peerCertLength = 0;
|
|
|
|
TLSConfiguration_setupComplete(configuration);
|
|
|
|
memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config));
|
|
|
|
mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self);
|
|
|
|
int ret;
|
|
|
|
mbedtls_ssl_conf_ca_chain( &(self->conf), &(configuration->cacerts), &(configuration->crl) );
|
|
|
|
self->crlUpdated = configuration->crlUpdated;
|
|
|
|
if (configuration->minVersion != TLS_VERSION_NOT_SELECTED) {
|
|
/* set minimum TLS version */
|
|
|
|
int majorVer = getMajorVersion(configuration->minVersion);
|
|
int minorVer = getMinorVersion(configuration->minVersion);
|
|
|
|
mbedtls_ssl_conf_min_version( &(self->conf), majorVer, minorVer);
|
|
}
|
|
|
|
if (configuration->maxVersion != TLS_VERSION_NOT_SELECTED) {
|
|
/* set maximum TLS version */
|
|
|
|
int majorVer = getMajorVersion(configuration->maxVersion);
|
|
int minorVer = getMinorVersion(configuration->maxVersion);
|
|
|
|
mbedtls_ssl_conf_max_version( &(self->conf), majorVer, minorVer);
|
|
}
|
|
|
|
if (configuration->ownCertificate.version > 0) {
|
|
ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(configuration->ownCertificate), &(configuration->ownKey));
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret);
|
|
}
|
|
|
|
ret = mbedtls_ssl_setup( &(self->ssl), &(self->conf) );
|
|
|
|
if (ret != 0)
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_setup returned %d\n", ret);
|
|
|
|
mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*) writeFunction,
|
|
(mbedtls_ssl_recv_t*) readFunction, NULL);
|
|
|
|
bool reuseSession = false;
|
|
|
|
if (configuration->useSessionResumption) {
|
|
if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) {
|
|
if (configuration->savedSession && configuration->savedSessionTime > 0) {
|
|
|
|
if (Hal_getTimeInMs() < (configuration->savedSessionTime + configuration->sessionResumptionInterval * 1000)) {
|
|
|
|
ret = mbedtls_ssl_set_session(&(self->ssl), configuration->savedSession);
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_set_session returned %d\n", ret);
|
|
configuration->savedSessionTime = 0;
|
|
}
|
|
else {
|
|
DEBUG_PRINT("TLS", "resume TLS session\n");
|
|
reuseSession = true;
|
|
}
|
|
}
|
|
else {
|
|
configuration->savedSessionTime = 0;
|
|
DEBUG_PRINT("TLS", "cached session expired\n");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
while( (ret = mbedtls_ssl_handshake(&(self->ssl)) ) != 0 )
|
|
{
|
|
if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
|
|
{
|
|
DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake returned -0x%x\n", -ret );
|
|
|
|
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
|
|
|
|
createSecurityEvents(configuration, ret, flags, self);
|
|
|
|
mbedtls_ssl_free(&(self->ssl));
|
|
|
|
if (self->peerCert) {
|
|
GLOBAL_FREEMEM(self->peerCert);
|
|
}
|
|
|
|
GLOBAL_FREEMEM(self);
|
|
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (configuration->useSessionResumption) {
|
|
if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) {
|
|
|
|
if (configuration->savedSession == NULL) {
|
|
configuration->savedSession = (mbedtls_ssl_session*)GLOBAL_CALLOC(1, sizeof(mbedtls_ssl_session));
|
|
}
|
|
|
|
if (configuration->savedSession) {
|
|
|
|
if (configuration->savedSessionTime == 0) {
|
|
ret = mbedtls_ssl_get_session(&(self->ssl), configuration->savedSession);
|
|
|
|
if (ret != 0) {
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_get_session returned %d\n", ret);
|
|
}
|
|
else {
|
|
configuration->savedSessionTime = Hal_getTimeInMs();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self->lastRenegotiationTime = Hal_getTimeInMs();
|
|
|
|
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) {
|
|
raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self);
|
|
}
|
|
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
uint8_t*
|
|
TLSSocket_getPeerCertificate(TLSSocket self, int* certSize)
|
|
{
|
|
if (certSize)
|
|
*certSize = self->peerCertLength;
|
|
|
|
return self->peerCert;
|
|
}
|
|
|
|
bool
|
|
TLSSocket_performHandshake(TLSSocket self)
|
|
{
|
|
int ret = mbedtls_ssl_renegotiate(&(self->ssl));
|
|
|
|
if (ret == 0) {
|
|
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) {
|
|
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else {
|
|
DEBUG_PRINT("TLS", "TLSSocket_performHandshake failed -> ret=%i\n", ret);
|
|
|
|
if (self->tlsConfig->eventHandler) {
|
|
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
|
|
|
|
createSecurityEvents(self->tlsConfig, ret, flags, self);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static void
|
|
checkForCRLUpdate(TLSSocket self)
|
|
{
|
|
if (self->crlUpdated == self->tlsConfig->crlUpdated)
|
|
return;
|
|
|
|
DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n");
|
|
|
|
mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) );
|
|
|
|
self->crlUpdated = self->tlsConfig->crlUpdated;
|
|
|
|
/* IEC TS 62351-100-3 Conformance test 6.2.6 requires that upon CRL update a TLS renegotiation should occur */
|
|
self->lastRenegotiationTime = 0;
|
|
}
|
|
|
|
/* true = renegotiation is not needed or it is successfull, false = Failed */
|
|
static bool
|
|
startRenegotiationIfRequired(TLSSocket self)
|
|
{
|
|
if (self->tlsConfig->renegotiationTimeInMs <= 0)
|
|
return true;
|
|
|
|
if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs)
|
|
return true;
|
|
|
|
raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self);
|
|
|
|
if (TLSSocket_performHandshake(self) == false) {
|
|
DEBUG_PRINT("TLS", " renegotiation failed\n");
|
|
return false;
|
|
}
|
|
|
|
DEBUG_PRINT("TLS", " started renegotiation\n");
|
|
self->lastRenegotiationTime = Hal_getTimeInMs();
|
|
|
|
return true;
|
|
}
|
|
|
|
int
|
|
TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
|
|
{
|
|
checkForCRLUpdate(self);
|
|
|
|
if (startRenegotiationIfRequired(self) == false) {
|
|
return -1;
|
|
}
|
|
|
|
int ret = mbedtls_ssl_read(&(self->ssl), buf, size);
|
|
|
|
if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE))
|
|
return 0;
|
|
|
|
if (ret < 0) {
|
|
|
|
switch (ret)
|
|
{
|
|
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
|
|
DEBUG_PRINT("TLS", " connection was closed gracefully\n");
|
|
return -1;
|
|
|
|
case MBEDTLS_ERR_NET_CONN_RESET:
|
|
DEBUG_PRINT("TLS", " connection was reset by peer\n");
|
|
return -1;
|
|
|
|
default:
|
|
DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret);
|
|
|
|
{
|
|
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
|
|
|
|
createSecurityEvents(self->tlsConfig, ret, flags, self);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
TLSSocket_write(TLSSocket self, uint8_t* buf, int size)
|
|
{
|
|
int ret;
|
|
int len = size;
|
|
|
|
checkForCRLUpdate(self);
|
|
|
|
if (startRenegotiationIfRequired(self) == false) {
|
|
return -1;
|
|
}
|
|
|
|
while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0)
|
|
{
|
|
if (ret == MBEDTLS_ERR_NET_CONN_RESET)
|
|
{
|
|
DEBUG_PRINT("TLS", "peer closed the connection\n");
|
|
return -1;
|
|
}
|
|
|
|
if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE))
|
|
{
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
len = ret;
|
|
|
|
return len;
|
|
}
|
|
|
|
void
|
|
TLSSocket_close(TLSSocket self)
|
|
{
|
|
int ret;
|
|
|
|
/* TODO add timeout? */
|
|
|
|
while ((ret = mbedtls_ssl_close_notify(&(self->ssl))) < 0)
|
|
{
|
|
if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE))
|
|
{
|
|
DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned -0x%x\n", -ret);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Thread_sleep(10);
|
|
|
|
mbedtls_ssl_free(&(self->ssl));
|
|
|
|
if (self->peerCert)
|
|
GLOBAL_FREEMEM(self->peerCert);
|
|
|
|
GLOBAL_FREEMEM(self);
|
|
}
|
|
|
|
char*
|
|
TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf)
|
|
{
|
|
TLSSocket socket = (TLSSocket)self;
|
|
|
|
if (peerAddrBuf == NULL) {
|
|
peerAddrBuf = (char*)GLOBAL_MALLOC(61);
|
|
}
|
|
|
|
if (peerAddrBuf)
|
|
return Socket_getPeerAddressStatic(socket->socket, peerAddrBuf);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t*
|
|
TLSConnection_getPeerCertificate(TLSConnection self, int* certSize)
|
|
{
|
|
TLSSocket socket = (TLSSocket)self;
|
|
|
|
return TLSSocket_getPeerCertificate(socket, certSize);
|
|
}
|
|
|
|
TLSConfigVersion
|
|
TLSConnection_getTLSVersion(TLSConnection self)
|
|
{
|
|
TLSSocket socket = (TLSSocket)self;
|
|
|
|
return getTLSVersion(socket->ssl.major_ver, socket->ssl.minor_ver);
|
|
}
|
|
|
|
const char*
|
|
TLSConfigVersion_toString(TLSConfigVersion version)
|
|
{
|
|
switch (version)
|
|
{
|
|
case TLS_VERSION_SSL_3_0:
|
|
return "SSL 3.0";
|
|
case TLS_VERSION_TLS_1_0:
|
|
return "TLS 1.0";
|
|
case TLS_VERSION_TLS_1_1:
|
|
return "TLS 1.1";
|
|
case TLS_VERSION_TLS_1_2:
|
|
return "TLS 1.2";
|
|
case TLS_VERSION_TLS_1_3:
|
|
return "TLS 1.3";
|
|
default:
|
|
return "unknown TLS version";
|
|
}
|
|
}
|