From 570ca71570a9eced4d993f9f91f62d5d00c8ba99 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 10 Dec 2017 12:47:01 +0100 Subject: [PATCH] - IEC 61850/MMS: integrated TLS for MMS client/server --- CMakeLists.txt | 19 + Makefile | 11 + make/stack_includes.mk | 1 + src/CMakeLists.txt | 9 + src/iec61850/inc/iec61850_server.h | 27 +- src/iec61850/inc_private/ied_server_private.h | 2 +- src/iec61850/server/impl/ied_server.c | 66 +-- src/mms/inc/iso_connection_parameters.h | 22 +- src/mms/inc/iso_server.h | 7 +- src/mms/inc/mms_client_connection.h | 14 +- src/mms/inc/mms_device_model.h | 2 +- src/mms/inc/mms_server.h | 10 +- src/mms/inc/mms_type_spec.h | 12 + src/mms/inc_private/acse.h | 7 +- src/mms/inc_private/cotp.h | 6 + src/mms/inc_private/iso_server_private.h | 1 + src/mms/inc_private/mms_server_connection.h | 12 +- src/mms/inc_private/mms_value_cache.h | 2 +- src/mms/iso_acse/acse.c | 36 +- src/mms/iso_client/iso_client_connection.c | 38 +- .../iso_common/iso_connection_parameters.c | 11 +- src/mms/iso_cotp/cotp.c | 35 +- .../iso_mms/client/mms_client_connection.c | 28 + src/mms/iso_mms/common/mms_type_spec.c | 21 + src/mms/iso_mms/server/mms_device.c | 2 +- src/mms/iso_mms/server/mms_server.c | 29 +- src/mms/iso_mms/server/mms_value_cache.c | 14 +- src/mms/iso_server/iso_connection.c | 40 +- src/mms/iso_server/iso_server.c | 41 +- src/tls/mbedtls/mbedtls_config.h | 59 +++ src/tls/mbedtls/tls_mbedtls.c | 497 ++++++++++++++++++ src/tls/tls_api.h | 115 ++++ src/vs/libiec61850-wo-goose.def | 4 +- src/vs/libiec61850.def | 4 +- third_party/mbedtls/README | 6 + 35 files changed, 1113 insertions(+), 97 deletions(-) create mode 100644 src/tls/mbedtls/mbedtls_config.h create mode 100644 src/tls/mbedtls/tls_mbedtls.c create mode 100644 src/tls/tls_api.h create mode 100644 third_party/mbedtls/README diff --git a/CMakeLists.txt b/CMakeLists.txt index 7457fe4f..cab1dff1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ include_directories( src/mms/inc_private src/mms/iso_mms/asn1c src/logging + src/tls ) set(API_HEADERS @@ -110,6 +111,7 @@ set(API_HEADERS src/sampled_values/sv_publisher_deprecated.h src/sampled_values/sv_subscriber_deprecated.h src/logging/logging_api.h + src/tls/tls_api.h ${CMAKE_CURRENT_BINARY_DIR}/config/stack_config.h ) @@ -119,6 +121,23 @@ if(MSVC) ) endif(MSVC) +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0) +set(WITH_MBEDTLS 1) +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0) + +if(WITH_MBEDTLS) +include_directories( + src/tls/mbedtls + third_party/mbedtls/mbedtls-2.6.0/include +) + +file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.6.0/library/*.c) + +add_definitions(-DCONFIG_MMS_SUPPORT_TLS=1) +add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") + +endif(WITH_MBEDTLS) + # write the detected stuff to this file configure_file( ${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake diff --git a/Makefile b/Makefile index 2aec4a61..2890447e 100644 --- a/Makefile +++ b/Makefile @@ -60,10 +60,20 @@ LIB_INCLUDE_DIRS += src/sampled_values LIB_INCLUDE_DIRS += src/iec61850/inc LIB_INCLUDE_DIRS += src/iec61850/inc_private LIB_INCLUDE_DIRS += src/logging +LIB_INCLUDE_DIRS += src/tls ifeq ($(HAL_IMPL), WIN32) LIB_INCLUDE_DIRS += third_party/winpcap/Include endif +ifdef WITH_MBEDTLS +LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.6.0/library +LIB_SOURCE_DIRS += src/tls/mbedtls +LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.6.0/include +LIB_INCLUDE_DIRS += src/tls/mbedtls +MBEDTLS_CONFIG_FILE = "mbedtls_config.h" +CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1' +endif + LIB_INCLUDES = $(addprefix -I,$(LIB_INCLUDE_DIRS)) ifndef INSTALL_PREFIX @@ -102,6 +112,7 @@ LIB_API_HEADER_FILES += src/goose/goose_publisher.h LIB_API_HEADER_FILES += src/sampled_values/sv_subscriber.h LIB_API_HEADER_FILES += src/sampled_values/sv_publisher.h LIB_API_HEADER_FILES += src/logging/logging_api.h +LIB_API_HEADER_FILES += src/tls/tls_api.h get_sources_from_directory = $(wildcard $1/*.c) get_sources = $(foreach dir, $1, $(call get_sources_from_directory,$(dir))) diff --git a/make/stack_includes.mk b/make/stack_includes.mk index f1afbeb3..8812255b 100644 --- a/make/stack_includes.mk +++ b/make/stack_includes.mk @@ -9,3 +9,4 @@ INCLUDES += -I$(LIBIEC_HOME)/src/hal/inc INCLUDES += -I$(LIBIEC_HOME)/src/goose INCLUDES += -I$(LIBIEC_HOME)/src/sampled_values INCLUDES += -I$(LIBIEC_HOME)/src/logging +INCLUDES += -I$(LIBIEC_HOME)/src/tls diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e8a4eef4..81fbd3cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,6 +80,15 @@ set (lib_common_SRCS ./logging/log_storage.c ) +if(WITH_MBEDTLS) +set (lib_common_SRCS ${lib_common_SRCS} +./tls/mbedtls/tls_mbedtls.c +) + +list (APPEND lib_common_SRCS ${tls_SRCS}) + +endif(WITH_MBEDTLS) + set (lib_asn1c_SRCS ./mms/iso_mms/asn1c/DataAccessError.c ./mms/iso_mms/asn1c/DeleteNamedVariableListRequest.c diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 8b08e149..9080a6b1 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -61,12 +61,23 @@ typedef struct sClientConnection* ClientConnection; /** * \brief Create a new IedServer instance * - * \param iedModel reference to the IedModel data structure to be used as IEC 61850 model of the device + * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device * - * \return the newly generated IedServer instance + * \return the new IedServer instance */ IedServer -IedServer_create(IedModel* iedModel); +IedServer_create(IedModel* dataModel); + +/** + * \brief Create a new IedServer with TLS support + * + * \param dataModel reference to the IedModel data structure to be used as IEC 61850 data model of the device + * \param tlsConfiguration TLS configuration object, or NULL to not use TLS + * + * \return the new IedServer instance + */ +IedServer +IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration); /** * \brief Destroy an IedServer instance and release all resources (memory, TCP sockets) @@ -208,16 +219,6 @@ IedServer_isRunning(IedServer self); MmsServer IedServer_getMmsServer(IedServer self); -/** - * \brief get the IsoServer instance for this IedServer object - * - * \param self the instance of IedServer to operate on. - * - * \return the IsoServer instance of this IedServer object - */ -IsoServer -IedServer_getIsoServer(IedServer self); - /** * \brief Enable all GOOSE control blocks. * diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index a9e5d9f3..07e2633b 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -38,7 +38,7 @@ struct sIedServer IedModel* model; MmsDevice* mmsDevice; MmsServer mmsServer; - IsoServer isoServer; + // IsoServer isoServer; char* localIpAddress; MmsMapping* mmsMapping; LinkedList clientConnections; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 60778bb2..76cbdf1a 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -398,57 +398,66 @@ updateDataSetsWithCachedValues(IedServer self) } IedServer -IedServer_create(IedModel* iedModel) +IedServer_createWithTlsSupport(IedModel* dataModel, TLSConfiguration tlsConfiguration) { IedServer self = (IedServer) GLOBAL_CALLOC(1, sizeof(struct sIedServer)); - self->model = iedModel; + if (self) { - // self->running = false; /* not required due to CALLOC */ - // self->localIpAddress = NULL; /* not required due to CALLOC */ + self->model = dataModel; + + self->running = false; + self->localIpAddress = NULL; #if (CONFIG_MMS_THREADLESS_STACK != 1) - self->dataModelLock = Semaphore_create(1); + self->dataModelLock = Semaphore_create(1); #endif - self->mmsMapping = MmsMapping_create(iedModel); + self->mmsMapping = MmsMapping_create(dataModel); - self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); + self->mmsDevice = MmsMapping_getMmsDeviceModel(self->mmsMapping); - self->isoServer = IsoServer_create(); + // self->isoServer = IsoServer_create(); - self->mmsServer = MmsServer_create(self->isoServer, self->mmsDevice); + self->mmsServer = MmsServer_create(self->mmsDevice, tlsConfiguration); - MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer); + MmsMapping_setMmsServer(self->mmsMapping, self->mmsServer); - MmsMapping_installHandlers(self->mmsMapping); + MmsMapping_installHandlers(self->mmsMapping); - MmsMapping_setIedServer(self->mmsMapping, self); + MmsMapping_setIedServer(self->mmsMapping, self); - createMmsServerCache(self); + createMmsServerCache(self); - iedModel->initializer(); + dataModel->initializer(); - installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */ + installDefaultValuesInCache(self); /* This will also connect cached MmsValues to DataAttributes */ - updateDataSetsWithCachedValues(self); + updateDataSetsWithCachedValues(self); - self->clientConnections = LinkedList_create(); + self->clientConnections = LinkedList_create(); - /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */ - self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE; + /* default write access policy allows access to SP, SE and SV FCDAs but denies access to DC and CF FCDAs */ + self->writeAccessPolicies = ALLOW_WRITE_ACCESS_SP | ALLOW_WRITE_ACCESS_SV | ALLOW_WRITE_ACCESS_SE; #if (CONFIG_IEC61850_REPORT_SERVICE == 1) - Reporting_activateBufferedReports(self->mmsMapping); + Reporting_activateBufferedReports(self->mmsMapping); #endif #if (CONFIG_IEC61850_SETTING_GROUPS == 1) - MmsMapping_configureSettingGroups(self->mmsMapping); + MmsMapping_configureSettingGroups(self->mmsMapping); #endif + } return self; } +IedServer +IedServer_create(IedModel* dataModel) +{ + return IedServer_createWithTlsSupport(dataModel, NULL); +} + void IedServer_destroy(IedServer self) { @@ -463,7 +472,6 @@ IedServer_destroy(IedServer self) } MmsServer_destroy(self->mmsServer); - IsoServer_destroy(self->isoServer); if (self->localIpAddress != NULL) GLOBAL_FREEMEM(self->localIpAddress); @@ -505,12 +513,6 @@ IedServer_getMmsServer(IedServer self) return self->mmsServer; } -IsoServer -IedServer_getIsoServer(IedServer self) -{ - return self->isoServer; -} - #if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_SINGLE_THREADED == 1) static void @@ -574,10 +576,7 @@ IedServer_start(IedServer self, int tcpPort) bool IedServer_isRunning(IedServer self) { - if (IsoServer_getState(self->isoServer) == ISO_SVR_STATE_RUNNING) - return true; - else - return false; + return MmsServer_isRunning(self->mmsServer); } IedModel* @@ -618,7 +617,8 @@ IedServer_setLocalIpAddress(IedServer self, const char* localIpAddress) GLOBAL_FREEMEM(self->localIpAddress); self->localIpAddress = StringUtils_copyString(localIpAddress); - IsoServer_setLocalIpAddress(self->isoServer, self->localIpAddress); + + MmsServer_setLocalIpAddress(self->mmsServer, self->localIpAddress); } diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h index 6ab22704..8f4ce65e 100644 --- a/src/mms/inc/iso_connection_parameters.h +++ b/src/mms/inc/iso_connection_parameters.h @@ -28,6 +28,8 @@ extern "C" { #endif +#include "tls_api.h" + /** * \addtogroup mms_client_api_group */ @@ -39,8 +41,13 @@ extern "C" { */ typedef enum { + /** Neither ACSE nor TLS authentication used */ ACSE_AUTH_NONE = 0, + + /** Use ACSE password for client authentication */ ACSE_AUTH_PASSWORD = 1, + + /** Use ACSE certificate for client authentication */ ACSE_AUTH_CERTIFICATE = 2, /** Use TLS certificate for client authentication */ @@ -53,19 +60,20 @@ typedef struct sAcseAuthenticationParameter* AcseAuthenticationParameter; struct sAcseAuthenticationParameter { AcseAuthenticationMechanism mechanism; + union { struct { uint8_t* octetString; int passwordLength; - } password; + } password; /* for mechanism = ACSE_AUTH_PASSWORD */ struct { uint8_t* buf; int length; - } certificate; + } certificate; /* for mechanism = ACSE_AUTH_CERTIFICATE or ACSE_AUTH_TLS */ } value; }; @@ -115,6 +123,10 @@ struct sIsoConnectionParameters { AcseAuthenticationParameter acseAuthParameter; +//#if (CONFIG_MMS_SUPPORT_TLS == 1) + TLSConfiguration tlsConfiguration; +//#endif + const char* hostname; int tcpPort; @@ -158,6 +170,10 @@ IsoConnectionParameters_create(void); void IsoConnectionParameters_destroy(IsoConnectionParameters self); + +void +IsoConnectionParameters_setTlsConfiguration(IsoConnectionParameters self, TLSConfiguration tlsConfig); + /** * \brief set the authentication parameter * @@ -226,7 +242,7 @@ IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, uint32_ * \param aeQualifier the AP-qualifier */ void -IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, char* apTitle, int aeQualifier); +IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier); /** * \brief set local addresses for the lower layers diff --git a/src/mms/inc/iso_server.h b/src/mms/inc/iso_server.h index 8c1615f8..a5da6495 100644 --- a/src/mms/inc/iso_server.h +++ b/src/mms/inc/iso_server.h @@ -89,13 +89,13 @@ void IsoConnection_sendMessage(IsoConnection self, ByteBuffer* message, bool handlerMode); IsoServer -IsoServer_create(void); +IsoServer_create(TLSConfiguration tlsConfiguration); void IsoServer_setTcpPort(IsoServer self, int port); void -IsoServer_setLocalIpAddress(IsoServer self, char* ipAddress); +IsoServer_setLocalIpAddress(IsoServer self, const char* ipAddress); IsoServerState IsoServer_getState(IsoServer self); @@ -113,6 +113,9 @@ IsoServer_getAuthenticator(IsoServer self); void* IsoServer_getAuthenticatorParameter(IsoServer self); +TLSConfiguration +IsoServer_getTLSConfiguration(IsoServer self); + void IsoServer_startListening(IsoServer self); diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index b0a08084..aa33938c 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -41,6 +41,8 @@ extern "C" { #include "iso_connection_parameters.h" #include "linked_list.h" +#include "tls_api.h" + /** * Contains MMS layer specific parameters */ @@ -79,6 +81,16 @@ typedef struct sMmsConnection* MmsConnection; MmsConnection MmsConnection_create(void); +/** + * \brief Create a new secure (TLS enabled) MmsConnection instance + * + * \param tlsConfig TLS configuration parameters and certificates + * + * \return the newly created instance. + */ +MmsConnection +MmsConnection_createSecure(TLSConfiguration tlsConfig); + /** * \brief Callback function to intercept raw MMS messages * @@ -217,7 +229,7 @@ MmsConnection_destroy(MmsConnection self); * \param self MmsConnection instance to operate on * \param mmsError user provided variable to store error code * \param serverName hostname or IP address of the server to connect - * \param serverPort TCP port number of the server to connect (usually 102) + * \param serverPort TCP port number of the server to connect or -1 to use default port (102 for MMS or 3872 for MMS over TLS) * * \return true on success. false if the connection attempt failed. */ diff --git a/src/mms/inc/mms_device_model.h b/src/mms/inc/mms_device_model.h index f4c9be1d..9c697e75 100644 --- a/src/mms/inc/mms_device_model.h +++ b/src/mms/inc/mms_device_model.h @@ -182,7 +182,7 @@ MmsDevice_destroy(MmsDevice* self); * \return the new MmsDevice instance */ MmsDomain* -MmsDevice_getDomain(MmsDevice* self, char* domainId); +MmsDevice_getDomain(MmsDevice* self, const char* domainId); /** * \brief Get the MmsTypeSpecification instance of a MMS named variable of VMD scope diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 23d128d4..553f3355 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -64,7 +64,7 @@ typedef void (*MmsConnectionHandler)(void* parameter, MmsServerConnection connection, MmsServerEvent event); MmsServer -MmsServer_create(IsoServer isoServer, MmsDevice* device); +MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration); void MmsServer_destroy(MmsServer self); @@ -80,6 +80,12 @@ void MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler, void* parameter); +void +MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress); + +bool +MmsServer_isRunning(MmsServer self); + /** * A connection handler will be invoked whenever a new client connection is opened or closed */ @@ -94,7 +100,7 @@ MmsDevice* MmsServer_getDevice(MmsServer self); MmsValue* -MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, char* itemId); +MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, const char* itemId); bool MmsServer_isLocked(MmsServer self); diff --git a/src/mms/inc/mms_type_spec.h b/src/mms/inc/mms_type_spec.h index 20693584..575e31fa 100644 --- a/src/mms/inc/mms_type_spec.h +++ b/src/mms/inc/mms_type_spec.h @@ -131,6 +131,18 @@ MmsVariableSpecification_getSize(MmsVariableSpecification* self); MmsVariableSpecification* MmsVariableSpecification_getChildSpecificationByIndex(MmsVariableSpecification* self, int index); +/** + * \brief return the MmsVariableSpecification of a structure element with the given name + * + * \param self the MmsVariableSpecification object + * \param name the name of the component (structure element) + * \param index (OUT) if not NULL the index of the structure element will be stored there + * + * \return the type specification of the component or NULL if the component was not found + */ +MmsVariableSpecification* +MmsVariableSpecification_getChildSpecificationByName(MmsVariableSpecification* self, const char* name, int* index); + MmsVariableSpecification* MmsVariableSpecification_getArrayElementSpecification(MmsVariableSpecification* self); diff --git a/src/mms/inc_private/acse.h b/src/mms/inc_private/acse.h index 9b36bd3c..3f966d00 100644 --- a/src/mms/inc_private/acse.h +++ b/src/mms/inc_private/acse.h @@ -26,6 +26,7 @@ #include "byte_buffer.h" #include "buffer_chain.h" #include "iso_connection_parameters.h" +#include "tls_api.h" #ifndef ACSE_H_ #define ACSE_H_ @@ -57,6 +58,10 @@ typedef struct sAcseConnection IsoApplicationReference applicationReference; void* authenticatorParameter; void* securityToken; + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + TLSSocket tlsSocket; +#endif } AcseConnection; #define ACSE_RESULT_ACCEPT 0 @@ -64,7 +69,7 @@ typedef struct sAcseConnection #define ACSE_RESULT_REJECT_TRANSIENT 2 void -AcseConnection_init(AcseConnection* self, AcseAuthenticator authenticator, void* parameter); +AcseConnection_init(AcseConnection* self, AcseAuthenticator authenticator, void* parameter, TLSSocket tlsSocket); void AcseConnection_destroy(AcseConnection* self); diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h index 81ef122d..4b6346b7 100644 --- a/src/mms/inc_private/cotp.h +++ b/src/mms/inc_private/cotp.h @@ -29,6 +29,7 @@ #include "buffer_chain.h" #include "hal_socket.h" #include "iso_connection_parameters.h" +#include "tls_api.h" typedef struct { TSelector tSelSrc; @@ -41,7 +42,12 @@ typedef struct { int remoteRef; int localRef; int protocolClass; + Socket socket; +//#if (CONFIG_MMS_SUPPORT_TLS == 1) + TLSSocket tlsSocket; +//#endif + CotpOptions options; bool isLastDataUnit; ByteBuffer* payload; diff --git a/src/mms/inc_private/iso_server_private.h b/src/mms/inc_private/iso_server_private.h index f5c3669f..4ae78808 100644 --- a/src/mms/inc_private/iso_server_private.h +++ b/src/mms/inc_private/iso_server_private.h @@ -25,6 +25,7 @@ #define ISO_SERVER_PRIVATE_H_ #include "hal_socket.h" +#include "tls_api.h" IsoConnection IsoConnection_create(Socket socket, IsoServer isoServer); diff --git a/src/mms/inc_private/mms_server_connection.h b/src/mms/inc_private/mms_server_connection.h index e65c78ac..8d00abbd 100644 --- a/src/mms/inc_private/mms_server_connection.h +++ b/src/mms/inc_private/mms_server_connection.h @@ -1,5 +1,5 @@ /* - * mms_connection.h + * mms_server_connection.h * * Copyright 2013 Michael Zillgith * @@ -39,6 +39,10 @@ #include "linked_list.h" #include "byte_buffer.h" +#ifdef __cplusplus +extern "C" { +#endif + MmsServerConnection MmsServerConnection_init(MmsServerConnection connection, MmsServer server, IsoConnection isoCon); @@ -67,7 +71,7 @@ MmsServerConnection_parseMessage(MmsServerConnection connection, ByteBuffer* mes */ void MmsServerConnection_sendInformationReportSingleVariableVMDSpecific(MmsServerConnection self, - char* itemId, MmsValue* value, bool handlerMode); + char* itemId, MmsValue* value, bool handlerMode); /** \brief send information report for a VMD specific named variable list @@ -104,6 +108,10 @@ MmsServerConnection_getNextRequestInvokeId(MmsServerConnection self); const char* MmsServerConnection_getFilesystemBasepath(MmsServerConnection self); +#ifdef __cplusplus +} +#endif + #endif /* MMS_SERVER_CONNECTION_H_ */ diff --git a/src/mms/inc_private/mms_value_cache.h b/src/mms/inc_private/mms_value_cache.h index c7b9a7cb..bca5185d 100644 --- a/src/mms/inc_private/mms_value_cache.h +++ b/src/mms/inc_private/mms_value_cache.h @@ -36,7 +36,7 @@ void MmsValueCache_insertValue(MmsValueCache self, char* itemId, MmsValue* value); MmsValue* -MmsValueCache_lookupValue(MmsValueCache self, char* itemId); +MmsValueCache_lookupValue(MmsValueCache self, const char* itemId); void MmsValueCache_destroy(MmsValueCache self); diff --git a/src/mms/iso_acse/acse.c b/src/mms/iso_acse/acse.c index 7b872dc4..92ba37a1 100644 --- a/src/mms/iso_acse/acse.c +++ b/src/mms/iso_acse/acse.c @@ -68,9 +68,10 @@ authenticateClient(AcseConnection* self, AcseAuthenticationMechanism mechanism, authParameter->value.password.octetString = authValue; authParameter->value.password.passwordLength = authValueLen; } - - //TODO Check if we are in a TLS connection: if mechanism == ACSE_AUTH_NONE provide client certificate if present - // --> mechanism = ACSE_AUTH_TLS + else if (mechanism == ACSE_AUTH_TLS) { + authParameter->value.certificate.buf = authValue; + authParameter->value.certificate.length = authValueLen; + } return self->authenticator(self->authenticatorParameter, authParameter, &(self->securityToken), &(self->applicationReference)); } @@ -84,6 +85,26 @@ checkAuthentication(AcseConnection* self, uint8_t* authMechanism, int authMechLe AcseAuthenticationMechanism mechanism = checkAuthMechanismName(authMechanism, authMechLen); + if (mechanism == ACSE_AUTH_NONE) { + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->tlsSocket) { + + int certLen; + + uint8_t* certBuf = TLSSocket_getPeerCertificate(self->tlsSocket, &certLen); + + if (certBuf) { + mechanism = ACSE_AUTH_TLS; + authValue = certBuf; + authValueLen = certLen; + } + + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + + } + return authenticateClient(self, mechanism, authValue, authValueLen); } else @@ -328,14 +349,19 @@ parseAarqPdu(AcseConnection* self, uint8_t* buffer, int bufPos, int maxBufPos) } void -AcseConnection_init(AcseConnection* self, AcseAuthenticator authenticator, void* parameter) +AcseConnection_init(AcseConnection* self, AcseAuthenticator authenticator, void* parameter, TLSSocket tlsSocket) { self->state = idle; self->nextReference = 0; self->userDataBuffer = NULL; self->userDataBufferSize = 0; - self->authenticator= authenticator; + self->authenticator = authenticator; self->authenticatorParameter = parameter; + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + self->tlsSocket = tlsSocket; +#endif + memset(&(self->applicationReference), 0, sizeof(self->applicationReference)); } diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index 6f6d48f6..242eaa09 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -34,6 +34,8 @@ #include "iso_presentation.h" #include "iso_client_connection.h" #include "acse.h" +#include "tls_api.h" + #ifndef DEBUG_ISO_CLIENT #ifdef DEBUG @@ -56,7 +58,13 @@ struct sIsoClientConnection IsoIndicationCallback callback; void* callbackParameter; volatile int state; + Socket socket; + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + TLSSocket tlsSocket; +#endif + CotpConnection* cotpConnection; IsoPresentation* presentation; IsoSession* session; @@ -159,6 +167,11 @@ connectionHandlingThread(IsoClientConnection self) self->state = STATE_IDLE; +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->cotpConnection->tlsSocket) + TLSSocket_close(self->cotpConnection->tlsSocket); +#endif + Socket_destroy(self->socket); if (DEBUG_ISO_CLIENT) @@ -251,6 +264,27 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters /* COTP (ISO transport) handshake */ CotpConnection_init(self->cotpConnection, self->socket, self->receiveBuffer, self->cotpReadBuffer, self->cotpWriteBuffer); + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (params->tlsConfiguration) { + + /* create TLSSocket and start TLS authentication */ + TLSSocket tlsSocket = TLSSocket_create(self->socket, params->tlsConfiguration, false); + + if (tlsSocket) + self->cotpConnection->tlsSocket = tlsSocket; + else { + + if (DEBUG_ISO_CLIENT) + printf("TLS handshake failed!\n"); + + goto returnError; + } + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + + + /* COTP (ISO transport) handshake */ CotpIndication cotpIndication = CotpConnection_sendConnectionRequestMessage(self->cotpConnection, params); @@ -280,7 +314,7 @@ IsoClientConnection_associate(IsoClientConnection self, IsoConnectionParameters acsePayload->length = payload->size; acsePayload->nextPart = NULL; - AcseConnection_init(&(self->acseConnection), NULL, NULL); + AcseConnection_init(&(self->acseConnection), NULL, NULL, NULL); AcseAuthenticationParameter authParameter = params->acseAuthParameter; @@ -383,7 +417,7 @@ returnError: Socket_destroy(self->socket); self->socket = NULL; - Semaphore_post(self->transmitBufferMutex); //TODO check + Semaphore_post(self->transmitBufferMutex); return; } diff --git a/src/mms/iso_common/iso_connection_parameters.c b/src/mms/iso_common/iso_connection_parameters.c index 816b0c2f..07d343fb 100644 --- a/src/mms/iso_common/iso_connection_parameters.c +++ b/src/mms/iso_common/iso_connection_parameters.c @@ -78,6 +78,15 @@ IsoConnectionParameters_destroy(IsoConnectionParameters self) GLOBAL_FREEMEM(self); } +void +IsoConnectionParameters_setTlsConfiguration(IsoConnectionParameters self, TLSConfiguration tlsConfig) +{ +//#if (CONFIG_MMS_SUPPORT_TLS == 1) + self->tlsConfiguration = tlsConfig; +//#endif +} + + void IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters self, AcseAuthenticationParameter acseAuthParameter) @@ -113,7 +122,7 @@ IsoConnectionParameters_setRemoteAddresses(IsoConnectionParameters self, uint32_ void -IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, char* apTitle, int aeQualifier) +IsoConnectionParameters_setLocalApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier) { if (apTitle == NULL) self->localApTitleLen = 0; diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c index c9165afc..7ce8ecc4 100644 --- a/src/mms/iso_cotp/cotp.c +++ b/src/mms/iso_cotp/cotp.c @@ -161,6 +161,20 @@ writeDataTpduHeader(CotpConnection* self, int isLastUnit) self->writeBuffer->size = 7; } +static inline int +writeToSocket(CotpConnection* self, uint8_t* buf, int size) +{ +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->tlsSocket) + return TLSSocket_write(self->tlsSocket, buf, size); + else + return Socket_write(self->socket, buf, size); +#else + return Socket_write(self->socket, buf, size); +#endif +} + + static bool sendBuffer(CotpConnection* self) { @@ -171,7 +185,7 @@ sendBuffer(CotpConnection* self) int sentBytes; do { - sentBytes = Socket_write(self->socket, ByteBuffer_getBuffer(self->writeBuffer), writeBufferPosition); + sentBytes = writeToSocket(self, ByteBuffer_getBuffer(self->writeBuffer), writeBufferPosition); if (sentBytes == -1) goto exit_function; @@ -435,6 +449,7 @@ CotpConnection_init(CotpConnection* self, Socket socket, { self->state = 0; self->socket = socket; + self->tlsSocket = NULL; self->remoteRef = -1; self->localRef = 1; self->protocolClass = -1; @@ -634,6 +649,20 @@ CotpConnection_resetPayload(CotpConnection* self) self->payload->size = 0; } + +static inline int +readFromSocket(CotpConnection* self, uint8_t* buf, int size) +{ +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->tlsSocket) + return TLSSocket_read(self->tlsSocket, buf, size); + else + return Socket_read(self->socket, buf, size); +#else + return Socket_read(self->socket, buf, size); +#endif +} + TpktState CotpConnection_readToTpktBuffer(CotpConnection* self) { @@ -647,7 +676,7 @@ CotpConnection_readToTpktBuffer(CotpConnection* self) if (bufPos < 4) { - readBytes = Socket_read(self->socket, buffer + bufPos, 4 - bufPos); + readBytes = readFromSocket(self, buffer + bufPos, 4 - bufPos); if (readBytes < 0) goto exit_closed; @@ -680,7 +709,7 @@ CotpConnection_readToTpktBuffer(CotpConnection* self) goto exit_waiting; } - readBytes = Socket_read(self->socket, buffer + bufPos, self->packetSize - bufPos); + readBytes = readFromSocket(self, buffer + bufPos, self->packetSize - bufPos); if (readBytes < 0) goto exit_closed; diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 7111b657..67592ffa 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -28,6 +28,8 @@ #include "mms_client_internal.h" #include "stack_config.h" +#include "tls_api.h" + #include #include "byte_buffer.h" @@ -1052,6 +1054,25 @@ MmsConnection_create() return self; } +MmsConnection +MmsConnection_createSecure(TLSConfiguration tlsConfig) +{ + MmsConnection self = MmsConnection_create(); + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self != NULL) { + IsoConnectionParameters connectionParameters = MmsConnection_getIsoConnectionParameters(self); + + TLSConfiguration_setClientMode(tlsConfig); + + IsoConnectionParameters_setTlsConfiguration(connectionParameters, tlsConfig); + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + + return self; +} + + void MmsConnection_destroy(MmsConnection self) { @@ -1184,6 +1205,13 @@ waitForConnectResponse(MmsConnection self) bool MmsConnection_connect(MmsConnection self, MmsError* mmsError, const char* serverName, int serverPort) { + if (serverPort == -1) { + if (self->isoParameters->tlsConfiguration) + serverPort = 3782; + else + serverPort = 102; + } + IsoConnectionParameters_setTcpParameters(self->isoParameters, serverName, serverPort); if (self->parameters.maxPduSize == -1) diff --git a/src/mms/iso_mms/common/mms_type_spec.c b/src/mms/iso_mms/common/mms_type_spec.c index 95f17ba9..bfccf0c7 100644 --- a/src/mms/iso_mms/common/mms_type_spec.c +++ b/src/mms/iso_mms/common/mms_type_spec.c @@ -276,6 +276,27 @@ MmsVariableSpecification_getChildSpecificationByIndex(MmsVariableSpecification* return self->typeSpec.structure.elements[index]; } +MmsVariableSpecification* +MmsVariableSpecification_getChildSpecificationByName(MmsVariableSpecification* self, const char* name, int* index) +{ + if (self->type != MMS_STRUCTURE) + return NULL; + + int i; + + for (i = 0; i < self->typeSpec.structure.elementCount; i++) { + if (!strcmp(self->typeSpec.structure.elements[i]->name, name)) { + + if (index != NULL) + *index = i; + + return self->typeSpec.structure.elements[i]; + } + } + + return NULL; +} + MmsVariableSpecification* MmsVariableSpecification_getArrayElementSpecification(MmsVariableSpecification* self) { diff --git a/src/mms/iso_mms/server/mms_device.c b/src/mms/iso_mms/server/mms_device.c index 36db62c6..37127f22 100644 --- a/src/mms/iso_mms/server/mms_device.c +++ b/src/mms/iso_mms/server/mms_device.c @@ -63,7 +63,7 @@ MmsDevice_destroy(MmsDevice* self) } MmsDomain* -MmsDevice_getDomain(MmsDevice* self, char* domainId) +MmsDevice_getDomain(MmsDevice* self, const char* domainId) { int i; diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index f3c2cf89..577590b9 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -48,11 +48,11 @@ createValueCaches(MmsDevice* device) } MmsServer -MmsServer_create(IsoServer isoServer, MmsDevice* device) +MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) { MmsServer self = (MmsServer) GLOBAL_CALLOC(1, sizeof(struct sMmsServer)); - self->isoServer = isoServer; + self->isoServer = IsoServer_create(tlsConfiguration); self->device = device; self->openConnections = Map_create(); self->valueCaches = createValueCaches(device); @@ -64,12 +64,26 @@ MmsServer_create(IsoServer isoServer, MmsDevice* device) self->modelMutex = Semaphore_create(1); self->transmitBufferMutex = Semaphore_create(1); - IsoServer_setUserLock(isoServer, self->modelMutex); + IsoServer_setUserLock(self->isoServer, self->modelMutex); #endif return self; } +void +MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress) +{ + IsoServer_setLocalIpAddress(self->isoServer, localIpAddress); +} + +bool +MmsServer_isRunning(MmsServer self) +{ + if (IsoServer_getState(self->isoServer) == ISO_SVR_STATE_RUNNING) + return true; + else + return false; +} void MmsServer_setFilestoreBasepath(MmsServer self, const char* basepath) @@ -225,6 +239,8 @@ deleteSingleCache(MmsValueCache cache) void MmsServer_destroy(MmsServer self) { + IsoServer_destroy(self->isoServer); + Map_deleteDeep(self->openConnections, false, closeConnection); Map_deleteDeep(self->valueCaches, false, (void (*) (void*)) deleteSingleCache); @@ -244,7 +260,7 @@ MmsServer_destroy(MmsServer self) } MmsValue* -MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, char* itemId) +MmsServer_getValueFromCache(MmsServer self, MmsDomain* domain, const char* itemId) { MmsValueCache cache = (MmsValueCache) Map_getEntry(self->valueCaches, domain); @@ -360,7 +376,10 @@ void MmsServer_startListening(MmsServer server, int tcpPort) { IsoServer_setConnectionHandler(server->isoServer, isoConnectionIndicationHandler, (void*) server); - IsoServer_setTcpPort(server->isoServer, tcpPort); + + if (tcpPort != -1) + IsoServer_setTcpPort(server->isoServer, tcpPort); + IsoServer_startListening(server->isoServer); } diff --git a/src/mms/iso_mms/server/mms_value_cache.c b/src/mms/iso_mms/server/mms_value_cache.c index d124b0a1..f66c69a9 100644 --- a/src/mms/iso_mms/server/mms_value_cache.c +++ b/src/mms/iso_mms/server/mms_value_cache.c @@ -82,19 +82,19 @@ getParentSubString(char* itemId) return NULL; } -static char* -getChildSubString (char* itemId, char* parentId) +static const char* +getChildSubString (const char* itemId, char* parentId) { return itemId + strlen(parentId) + 1; } static MmsValue* -searchCacheForValue(MmsValueCache self, char* itemId, char* parentId) +searchCacheForValue(MmsValueCache self, const char* itemId, char* parentId) { MmsValueCacheEntry* cacheEntry; MmsValue* value = NULL; - cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, parentId); + cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) parentId); if (cacheEntry == NULL) { char* parentItemId = getParentSubString(parentId); @@ -105,7 +105,7 @@ searchCacheForValue(MmsValueCache self, char* itemId, char* parentId) } else { - char* childId = getChildSubString(itemId, parentId); + const char* childId = getChildSubString(itemId, parentId); MmsVariableSpecification* typeSpec = MmsDomain_getNamedVariable(self->domain, parentId); value = MmsVariableSpecification_getChildValue(typeSpec, cacheEntry->value, childId); @@ -115,7 +115,7 @@ searchCacheForValue(MmsValueCache self, char* itemId, char* parentId) } MmsValue* -MmsValueCache_lookupValue(MmsValueCache self, char* itemId) +MmsValueCache_lookupValue(MmsValueCache self, const char* itemId) { // get value for first matching key substring! // Then iterate the value for the exact value. @@ -124,7 +124,7 @@ MmsValueCache_lookupValue(MmsValueCache self, char* itemId) MmsValueCacheEntry* cacheEntry; - cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, itemId); + cacheEntry = (MmsValueCacheEntry*) Map_getEntry(self->map, (void*) itemId); if (cacheEntry == NULL) { char* itemIdCopy = StringUtils_copyString(itemId); diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c index 12ca8349..eb7fe48e 100644 --- a/src/mms/iso_server/iso_connection.c +++ b/src/mms/iso_server/iso_connection.c @@ -69,6 +69,11 @@ struct sIsoConnection IsoServer isoServer; Socket socket; + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + TLSSocket tlsSocket; +#endif + int state; IsoSession* session; IsoPresentation* presentation; @@ -445,6 +450,23 @@ IsoConnection_create(Socket socket, IsoServer isoServer) { IsoConnection self = (IsoConnection) GLOBAL_CALLOC(1, sizeof(struct sIsoConnection)); self->socket = socket; + +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (IsoServer_getTLSConfiguration(isoServer) != NULL) { + self->tlsSocket = TLSSocket_create(socket, IsoServer_getTLSConfiguration(isoServer), true); + + if (self->tlsSocket == NULL) { + if (DEBUG_ISO_SERVER) + printf("ISO_SERVER: IsoConnection - TLS initialization failed\n"); + + GLOBAL_FREEMEM(self); + + return NULL; + } + } +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + + self->receiveBuffer = (uint8_t*) GLOBAL_MALLOC(RECEIVE_BUF_SIZE); self->sendBuffer = (uint8_t*) GLOBAL_MALLOC(SEND_BUF_SIZE); self->msgRcvdHandler = NULL; @@ -469,6 +491,11 @@ IsoConnection_create(Socket socket, IsoServer isoServer) self->cotpConnection = (CotpConnection*) GLOBAL_CALLOC(1, sizeof(CotpConnection)); CotpConnection_init(self->cotpConnection, self->socket, &(self->rcvBuffer), &(self->cotpReadBuffer), &(self->cotpWriteBuffer)); +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->tlsSocket) + self->cotpConnection->tlsSocket = self->tlsSocket; +#endif /* (CONFIG_MMS_SUPPORT_TLS == 1) */ + self->session = (IsoSession*) GLOBAL_CALLOC(1, sizeof(IsoSession)); IsoSession_init(self->session); @@ -476,8 +503,14 @@ IsoConnection_create(Socket socket, IsoServer isoServer) IsoPresentation_init(self->presentation); self->acseConnection = (AcseConnection*) GLOBAL_CALLOC(1, sizeof(AcseConnection)); + +#if (CONFIG_MMS_SUPPORT_TLS == 1) AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer), - IsoServer_getAuthenticatorParameter(self->isoServer)); + IsoServer_getAuthenticatorParameter(self->isoServer), self->tlsSocket); +#else + AcseConnection_init(self->acseConnection, IsoServer_getAuthenticator(self->isoServer), + IsoServer_getAuthenticatorParameter(self->isoServer), NULL); +#endif if (DEBUG_ISO_SERVER) printf("ISO_SERVER: IsoConnection: Start to handle connection for client %s\n", self->clientAddress); @@ -572,6 +605,11 @@ IsoConnection_close(IsoConnection self) self->state = ISO_CON_STATE_STOPPED; self->socket = NULL; +#if (CONFIG_MMS_SUPPORT_TLS == 1) + if (self->tlsSocket) + TLSSocket_close(self->tlsSocket); +#endif + Socket_destroy(socket); } } diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c index f32bc31d..6799af1c 100644 --- a/src/mms/iso_server/iso_server.c +++ b/src/mms/iso_server/iso_server.c @@ -63,7 +63,9 @@ struct sIsoServer { Socket serverSocket; int tcpPort; - char* localIpAddress; + const char* localIpAddress; + + TLSConfiguration tlsConfiguration; #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) LinkedList openClientConnections; @@ -349,10 +351,14 @@ handleIsoConnections(IsoServer self) IsoConnection isoConnection = IsoConnection_create(connectionSocket, self); - addClientConnection(self, isoConnection); + if (isoConnection) { - self->connectionHandler(ISO_CONNECTION_OPENED, self->connectionHandlerParameter, - isoConnection); + addClientConnection(self, isoConnection); + + self->connectionHandler(ISO_CONNECTION_OPENED, self->connectionHandlerParameter, + isoConnection); + + } } @@ -385,11 +391,14 @@ handleIsoConnectionsThreadless(IsoServer self) IsoConnection isoConnection = IsoConnection_create(connectionSocket, self); - addClientConnection(self, isoConnection); + if (isoConnection) { - self->connectionHandler(ISO_CONNECTION_OPENED, self->connectionHandlerParameter, - isoConnection); + addClientConnection(self, isoConnection); + + self->connectionHandler(ISO_CONNECTION_OPENED, self->connectionHandlerParameter, + isoConnection); + } } handleClientConnections(self); @@ -431,12 +440,18 @@ isoServerThread(void* isoServerParam) #endif IsoServer -IsoServer_create() +IsoServer_create(TLSConfiguration tlsConfiguration) { IsoServer self = (IsoServer) GLOBAL_CALLOC(1, sizeof(struct sIsoServer)); self->state = ISO_SVR_STATE_IDLE; - self->tcpPort = TCP_PORT; + + if (tlsConfiguration == NULL) + self->tcpPort = TCP_PORT; + else + self->tcpPort = SECURE_TCP_PORT; + + self->tlsConfiguration = tlsConfiguration; #if (CONFIG_MAXIMUM_TCP_CLIENT_CONNECTIONS == -1) self->openClientConnections = LinkedList_create(); @@ -462,7 +477,7 @@ IsoServer_setTcpPort(IsoServer self, int port) } void -IsoServer_setLocalIpAddress(IsoServer self, char* ipAddress) +IsoServer_setLocalIpAddress(IsoServer self, const char* ipAddress) { self->localIpAddress = ipAddress; } @@ -492,6 +507,12 @@ IsoServer_getAuthenticatorParameter(IsoServer self) return self->authenticatorParameter; } +TLSConfiguration +IsoServer_getTLSConfiguration(IsoServer self) +{ + return self->tlsConfiguration; +} + #if (CONFIG_MMS_THREADLESS_STACK != 1) void IsoServer_startListening(IsoServer self) diff --git a/src/tls/mbedtls/mbedtls_config.h b/src/tls/mbedtls/mbedtls_config.h new file mode 100644 index 00000000..e3473138 --- /dev/null +++ b/src/tls/mbedtls/mbedtls_config.h @@ -0,0 +1,59 @@ +#ifndef MBEDTLS_CONFIG_H +#define MBEDTLS_CONFIG_H + +/* System support */ +#define MBEDTLS_HAVE_ASM +#define MBEDTLS_HAVE_TIME +#define MBEDTLS_NO_UDBL_DIVISION +#define MBEDTLS_PLATFORM_C +#define MBEDTLS_DEBUG_C + +/* mbed TLS feature support */ +#define MBEDTLS_CIPHER_MODE_CBC +#define MBEDTLS_PKCS1_V15 +#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED +#define MBEDTLS_SSL_PROTO_TLS1_1 +#define MBEDTLS_SSL_PROTO_TLS1 +#define MBEDTLS_SSL_RENEGOTIATION + +#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_CERTIFICATES + +/* mbed TLS modules */ +#define MBEDTLS_AES_C +#define MBEDTLS_ASN1_PARSE_C +#define MBEDTLS_ASN1_WRITE_C +#define MBEDTLS_BIGNUM_C +#define MBEDTLS_CIPHER_C +#define MBEDTLS_CTR_DRBG_C +//#define MBEDTLS_DES_C +#define MBEDTLS_ENTROPY_C +#define MBEDTLS_MD_C +#define MBEDTLS_MD5_C +#define MBEDTLS_NET_C +#define MBEDTLS_OID_C +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C +#define MBEDTLS_RSA_C +#define MBEDTLS_SHA1_C +#define MBEDTLS_SHA256_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_X509_CRT_PARSE_C +#define MBEDTLS_X509_USE_C +#define MBEDTLS_SSL_CACHE_C + +/* For test certificates */ +#define MBEDTLS_BASE64_C +#define MBEDTLS_CERTS_C +#define MBEDTLS_PEM_PARSE_C + +#define MBEDTLS_PKCS12_C +#define MBEDTLS_PKCS5_C + +/* For testing with compat.sh */ +#define MBEDTLS_FS_IO + +#include "mbedtls/check_config.h" + +#endif /* MBEDTLS_CONFIG_H */ diff --git a/src/tls/mbedtls/tls_mbedtls.c b/src/tls/mbedtls/tls_mbedtls.c new file mode 100644 index 00000000..919ba867 --- /dev/null +++ b/src/tls/mbedtls/tls_mbedtls.c @@ -0,0 +1,497 @@ +/* + * tls_mbedtls.c + * + * TLS API for TCP/IP protocol stacks + * + * Copyright 2017 MZ Automation GmbH + * + * Implementation of the TLS abstraction layer for mbedtls + * + */ + +#include + +#include "tls_api.h" +#include "hal_thread.h" +#include "lib_memory.h" +#include "linked_list.h" + +//#include "mbedtls/config.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" + +#define CONFIG_DEBUG_TLS 0 + +#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_ssl_config conf; + LinkedList /* */ allowedCertificates; + + bool chainValidation; + bool allowOnlyKnownCertificates; +}; + +struct sTLSSocket { + mbedtls_ssl_context ssl; + Socket socket; + mbedtls_ssl_config conf; + TLSConfiguration tlsConfig; + bool storePeerCert; + uint8_t* peerCert; + int peerCertLength; +}; + +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 + return 1; + } + + if (self->storePeerCert) { + + if (*flags == 0) { + + self->peerCertLength = crt->raw.len; + self->peerCert = (uint8_t*) GLOBAL_MALLOC(self->peerCertLength); + memcpy(self->peerCert, crt->raw.p, self->peerCertLength); + + } + + } + } + + return 0; +} + + + +TLSConfiguration +TLSConfiguration_create() +{ + TLSConfiguration self = (TLSConfiguration) GLOBAL_MALLOC(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_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_RENEGOTIATION_ENABLED); + + //static int hashes[] = {3,4,5,6,7,8,0}; + // mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); + + self->allowedCertificates = LinkedList_create(); + + /* default behavior is to allow all certificates that are signed by the CA */ + self->chainValidation = true; + self->allowOnlyKnownCertificates = false; + } + + return self; +} + +void +TLSConfiguration_setClientMode(TLSConfiguration self) +{ + self->conf.endpoint = MBEDTLS_SSL_IS_CLIENT; +} + +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) + printf("mbedtls_x509_crt_parse returned %d\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 %d\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 %d\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 %d\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 %d\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 %d\n", ret); + + return (ret == 0); +} + +void +TLSConfiguration_destroy(TLSConfiguration self) +{ + mbedtls_x509_crt_free(&(self->ownCertificate)); + mbedtls_x509_crt_free(&(self->cacerts)); + 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 int +readFunction(void* ctx, unsigned char* buf, size_t len) +{ + int ret = Socket_read((Socket) ctx, buf, len); + + if ((ret == 0) && (len > 0)) + return MBEDTLS_ERR_SSL_WANT_READ; + + return ret; +} + +TLSSocket +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert) +{ + TLSSocket self = (TLSSocket) GLOBAL_CALLOC(1, sizeof(struct sTLSSocket)); + + if (self != NULL) { + + self->socket = socket; + self->tlsConfig = configuration; + self->storePeerCert = storeClientCert; + self->peerCert = NULL; + self->peerCertLength = 0; + + 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), NULL ); + + 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*) Socket_write, + (mbedtls_ssl_recv_t*) readFunction, NULL); + + while( (ret = mbedtls_ssl_handshake(&(self->ssl)) ) != 0 ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + DEBUG_PRINT("TLS", "mbedtls_ssl_handshake returned %d\n\n", ret ); + + GLOBAL_FREEMEM(self); + + return NULL; + } + } + } + + return self; +} + +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize) +{ + if (certSize) + *certSize = self->peerCertLength; + + return self->peerCert; +} + +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +{ + int ret; + int len = size; + + do + { + ret = mbedtls_ssl_read( &(self->ssl), buf, len ); + + if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ) + continue; + + if( ret <= 0 ) + { + switch( ret ) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + DEBUG_PRINT("TLS", " connection was closed gracefully\n" ); + len = -1; + break; + + case MBEDTLS_ERR_NET_CONN_RESET: + len = -1; + DEBUG_PRINT("TLS", " connection was reset by peer\n" ); + break; + + default: + DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret ); + len = -1; //TODO is this the correct return value? + break; + } + + break; + } + + len = ret; + + if( ret > 0 ) + break; + } + while( 1 ); + + return len; +} + +int +TLSSocket_write(TLSSocket self, uint8_t* buf, int size) +{ + int ret; + int len = size; + + + 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 %d\n", ret ); + break; + } + } + + Thread_sleep(10); + + mbedtls_ssl_config_free(&(self->conf)); + mbedtls_ssl_free(&(self->ssl)); + + GLOBAL_FREEMEM(self); +} diff --git a/src/tls/tls_api.h b/src/tls/tls_api.h new file mode 100644 index 00000000..3b8cb188 --- /dev/null +++ b/src/tls/tls_api.h @@ -0,0 +1,115 @@ +/* + * tls_api.h + * + * TLS API for TCP/IP protocol stacks + * + * Copyright 2017 MZ Automation GmbH + * + * Abstraction layer for different TLS implementations + * + * Implementation connects the TLS API layer with the socket API layer + * and performs all TLS tasks like handshake, encryption/decryption. + * + */ + +#ifndef SRC_TLS_TLS_API_H_ +#define SRC_TLS_TLS_API_H_ + +#include "hal_socket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct sTLSConfiguration* TLSConfiguration; + +typedef struct sTLSSocket* TLSSocket; + +TLSConfiguration +TLSConfiguration_create(void); + +/* will be called by stack automatically */ +void +TLSConfiguration_setClientMode(TLSConfiguration self); + +void +TLSConfiguration_setChainValidation(TLSConfiguration self, bool value); + +void +TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value); + +bool +TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen); + +bool +TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename); + +bool +TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword); + +bool +TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword); + +bool +TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); + +bool +TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename); + +bool +TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); + +bool +TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename); + +void +TLSConfiguration_destroy(TLSConfiguration self); + +TLSSocket +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert); + +bool +TLSSocket_performHandshake(TLSSocket self); + +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize); + +/** + * \brief read from socket to local buffer (non-blocking) + * + * The function shall return immediately if no data is available. In this case + * the function returns 0. If an error happens the function shall return -1. + * + * Implementation of this function is MANDATORY + * + * NOTE: The behaviour of this function changed with version 0.8! + * + * \param self the client, connection or server socket instance + * \param buf the buffer where the read bytes are copied to + * \param size the maximum number of bytes to read (size of the provided buffer) + * + * \return the number of bytes read or -1 if an error occurred + */ +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size); + +/** + * \brief send a message through the socket + * + * Implementation of this function is MANDATORY + * + * \param self client, connection or server socket instance + * + * \return number of bytes transmitted of -1 in case of an error + */ +int +TLSSocket_write(TLSSocket self, uint8_t* buf, int size); + +void +TLSSocket_close(TLSSocket self); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_TLS_TLS_API_H_ */ diff --git a/src/vs/libiec61850-wo-goose.def b/src/vs/libiec61850-wo-goose.def index e3dcaccb..6b48bb0e 100644 --- a/src/vs/libiec61850-wo-goose.def +++ b/src/vs/libiec61850-wo-goose.def @@ -163,7 +163,6 @@ EXPORTS IedServer_getAttributeValue @416 IedServer_getDataModel @417 IedServer_getFunctionalConstrainedData @418 - IedServer_getIsoServer @419 IedServer_getMmsServer @420 IedServer_handleWriteAccess @421 IedServer_isRunning @422 @@ -573,3 +572,6 @@ EXPORTS IedConnection_writeDataSetValues MmsVariableSpecification_isValueOfType IedServer_udpateDbposValue + MmsServer_setLocalIpAddress + MmsServer_isRunning + IedServer_createWithTlsSupport \ No newline at end of file diff --git a/src/vs/libiec61850.def b/src/vs/libiec61850.def index a69d7634..762a77f8 100644 --- a/src/vs/libiec61850.def +++ b/src/vs/libiec61850.def @@ -187,7 +187,6 @@ EXPORTS IedServer_getAttributeValue @416 IedServer_getDataModel @417 IedServer_getFunctionalConstrainedData @418 - IedServer_getIsoServer @419 IedServer_getMmsServer @420 IedServer_handleWriteAccess @421 IedServer_isRunning @422 @@ -654,3 +653,6 @@ EXPORTS IedConnection_writeDataSetValues MmsVariableSpecification_isValueOfType IedServer_udpateDbposValue + MmsServer_setLocalIpAddress + MmsServer_isRunning + IedServer_createWithTlsSupport \ No newline at end of file diff --git a/third_party/mbedtls/README b/third_party/mbedtls/README new file mode 100644 index 00000000..d0948ceb --- /dev/null +++ b/third_party/mbedtls/README @@ -0,0 +1,6 @@ +README +------ + +For TLS support with mbedtls download the source tarball of version 2.6.0 and extract here in the subfolder + +mbedtls-2.6.0