From 41ed0dd51ae2f7a79ceba4b1b441fa4cf56e9e0e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 10 Mar 2023 17:14:19 +0000 Subject: [PATCH] - implemented access control callbacks for logs and LCBs (LIB61850-392) --- CMakeLists.txt | 5 ++ .../server_example_access_control.c | 17 ++++- .../server_example_logging.c | 26 ++++++++ src/iec61850/inc/iec61850_dynamic_model.h | 6 ++ src/iec61850/inc/iec61850_server.h | 63 ++++++++++++------- src/iec61850/inc_private/logging.h | 2 +- .../inc_private/mms_mapping_internal.h | 6 ++ src/iec61850/server/impl/ied_server.c | 14 +++++ src/iec61850/server/mms_mapping/logging.c | 49 ++++++++++++--- src/iec61850/server/mms_mapping/mms_mapping.c | 35 +++++++++-- src/iec61850/server/model/dynamic_model.c | 12 ++++ src/mms/inc/mms_server.h | 20 ++++++ src/mms/inc_private/mms_server_internal.h | 3 + src/mms/iso_mms/server/mms_journal_service.c | 14 +++++ src/mms/iso_mms/server/mms_server.c | 7 +++ 15 files changed, 239 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28477596..80cb5d17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,11 @@ set(USE_PREBUILD_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) +set(FOUND_SQLITE3_SOURCE 1) +message("Found sqlite3 source in third_party folder -> can compile with log service support") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) set(WITH_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index 2d8bde93..7c18fd46 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -78,7 +78,7 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, /* * This handler is called before the rcbEventHandler and can be use to allow or permit read or write access to the RCB */ -bool +static bool rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation) { printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); @@ -91,6 +91,19 @@ rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conn } } +static bool +lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation) +{ + printf("LCB: %s access: %s\n", LogControlBlock_getName(lcb), operation == LCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); + + if (operation == LCB_EVENT_GET_PARAMETER) { + return true; + } + else { + return false; + } +} + static void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) { @@ -185,6 +198,8 @@ main(int argc, char** argv) IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL); + IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); + IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); /* By default access to variables with FC=DC and FC=CF is not allowed. diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index 51c7a0df..ee388be8 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -115,6 +115,28 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in return true; } +static bool +lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation) +{ + printf("%s access to LCB %s from %s\n", operation == LCB_EVENT_GET_PARAMETER ? "read" : "write", LogControlBlock_getName(lcb), ClientConnection_getPeerAddress(connection)); + + /* only allow read access */ + if (operation == LCB_EVENT_GET_PARAMETER) { + return true; + } + else { + return false; + } +} + +static bool +logAccessHandler(void* parameter, const char* logName, ClientConnection connection) +{ + printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection)); + + return false; +} + int main(int argc, char** argv) { @@ -147,6 +169,10 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); + + IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL); + LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); LogStorage_setMaxLogEntries(statusLog, 10); diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 32f6ff04..693dc7f4 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -395,6 +395,12 @@ LIB61850_API LogControlBlock* LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps, uint32_t intgPd, bool logEna, bool reasonCode); +LIB61850_API const char* +LogControlBlock_getName(LogControlBlock* self); + +LIB61850_API LogicalNode* +LogControlBlock_getParent(LogControlBlock* self); + /** * \brief create a log (used by the IEC 61850 log service) * diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 93bb02e2..ec023b4d 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1622,30 +1622,6 @@ typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* LIB61850_API void IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, void* parameter); - -/** - * \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation - * - * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE - * - * \param parameter user provided parameter - * \param rcb affected report control block - * \param connection client connection that is involved - * \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER - */ -typedef bool -(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation); - -/** - * \brief Set a handler to control read and write access to report control blocks (RCBs) - * - * \param self the instance of IedServer to operate on. - * \param handler the event handler to be used - * \param parameter a user provided parameter that is passed to the handler. - */ -LIB61850_API void -IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter); - /**@}*/ /** @@ -1865,6 +1841,45 @@ typedef MmsDataAccessError LIB61850_API void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); +/** + * \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation + * + * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE + * + * \param parameter user provided parameter + * \param rcb affected report control block + * \param connection client connection that is involved + * \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER + */ +typedef bool +(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation); + +/** + * \brief Set a handler to control read and write access to report control blocks (RCBs) + * + * \param self the instance of IedServer to operate on. + * \param handler the event handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter); + +typedef enum { + LCB_EVENT_GET_PARAMETER, + LCB_EVENT_SET_PARAMETER +} IedServer_LCBEventType; + +typedef bool +(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation); + +LIB61850_API void +IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter); + +typedef bool +(*IedServer_LogAccessHandler) (void* parameter, const char* logName, ClientConnection connection); + +LIB61850_API void +IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter); typedef enum { DATASET_CREATE, diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h index 7684739a..82ef712b 100644 --- a/src/iec61850/inc_private/logging.h +++ b/src/iec61850/inc_private/logging.h @@ -121,7 +121,7 @@ LIB61850_INTERNAL void Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); LIB61850_INTERNAL MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index c42df272..09aba970 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -338,6 +338,12 @@ struct sMmsMapping { IedServer_RCBAccessHandler rcbAccessHandler; void* rcbAccessHandlerParameter; + IedServer_LCBAccessHandler lcbAccessHandler; + void* lcbAccessHandlerParameter; + + IedServer_LogAccessHandler logAccessHandler; + void* logAccessHandlerParameter; + IedServer_DataSetAccessHandler dataSetAccessHandler; void* dataSetAccessHandlerParameter; }; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c398e143..fb39ead3 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -696,6 +696,20 @@ IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler self->mmsMapping->rcbAccessHandlerParameter = parameter; } +void +IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter) +{ + self->mmsMapping->lcbAccessHandler = handler; + self->mmsMapping->lcbAccessHandlerParameter = parameter; +} + +void +IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter) +{ + self->mmsMapping->logAccessHandler = handler; + self->mmsMapping->logAccessHandlerParameter = parameter; +} + void IedServer_destroy(IedServer self) { diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index e841feb2..c55a1ff4 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -1,7 +1,7 @@ /* * logging.c * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,6 +43,8 @@ #if (CONFIG_IEC61850_LOG_SERVICE == 1) +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + LogInstance* LogInstance_create(LogicalNode* parentLN, const char* name) { @@ -520,6 +522,19 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma if (logControl == NULL) { return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; } + else + { + if (self->lcbAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_SET_PARAMETER) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + goto exit_function; + } + } + } if (strcmp(varName, "LogEna") == 0) { bool logEna = MmsValue_getBoolean(value); @@ -699,7 +714,7 @@ exit_function: } MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -723,27 +738,41 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); - if (logControl != NULL) { + if (logControl) + { + bool allowAccess = true; + + if (self->lcbAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - updateLogStatusInLCB(logControl); + if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) { + allowAccess = false; - if (varName != NULL) { - value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + value = &objectAccessDenied; + } } - else { - value = logControl->mmsValue; + + if (allowAccess) { + updateLogStatusInLCB(logControl); + + if (varName) { + value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + } + else { + value = logControl->mmsValue; + } } } return value; } - static char* createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index a91ace26..396fcc96 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3115,7 +3115,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_LOG_SERVICE == 1) /* LOG control block - LG */ if (isLogControlBlock(separator)) { - retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId); + retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3410,9 +3410,7 @@ checkDataSetAccess(MmsMapping* self, MmsServerConnection connection, MmsVariable StringUtils_appendString(dataSetRef, 129, listName); } - accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, - private_IedServer_getClientConnectionByHandle(self->iedServer, connection), - operation, dataSetRef); + accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, clientConnection, operation, dataSetRef); } return accessGranted; @@ -3596,6 +3594,31 @@ variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType return allow; } +#if (CONFIG_IEC61850_LOG_SERVICE == 1) +static bool +mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection) +{ + bool allowAccess = true; + + MmsMapping* self = (MmsMapping*)parameter; + + if (self->logAccessHandler) { + char logReference[130]; + logReference[0] = 0; + + StringUtils_appendString(logReference, 130, MmsDomain_getName(domain)); + StringUtils_appendString(logReference, 130, "/"); + StringUtils_appendString(logReference, 130, logName); + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection); + } + + return allowAccess; +} +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + void MmsMapping_installHandlers(MmsMapping* self) { @@ -3604,6 +3627,10 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) + MmsServer_installReadJournalHandler(self->mmsServer, mmsReadJournalHandler, (void*) self); +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ } void diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 18933c46..9b0a53fd 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -347,6 +347,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe return self; } +const char* +LogControlBlock_getName(LogControlBlock* self) +{ + return self->name; +} + +LogicalNode* +LogControlBlock_getParent(LogControlBlock* self) +{ + return self->parent; +} + static void LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb) { diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index b9c22417..ef4831d1 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -90,6 +90,26 @@ typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariab LIB61850_INTERNAL void MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter); +/** + * \brief callback handler that is called for each received read journal request + * + * \param parameter a user provided parameter + * \param domain the MMS domain the journal is belonging to + * \param logName the name of the journal + * \param connection client connection that is accessing the journal + */ +typedef bool (*MmsReadJournalHandler)(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection); + +/** + * \brief Install callback handler that is called when a journal is accessed by a client + * + * \param self the MmsServer instance to operate on + * \param handler the callback handler function + * \param parameter user provided parameter that is passed to the callback handler + */ +LIB61850_INTERNAL void +MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter); + /** * \brief ObtainFile service callback handler * diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index fc875597..087ff42c 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -126,6 +126,9 @@ struct sMmsServer { MmsNamedVariableListAccessHandler variableListAccessHandler; void* variableListAccessHandlerParameter; + MmsReadJournalHandler readJournalHandler; + void* readJournalHandlerParameter; + AcseAuthenticator authenticator; void* authenticatorParameter; diff --git a/src/mms/iso_mms/server/mms_journal_service.c b/src/mms/iso_mms/server/mms_journal_service.c index 22980f76..0aadde40 100644 --- a/src/mms/iso_mms/server/mms_journal_service.c +++ b/src/mms/iso_mms/server/mms_journal_service.c @@ -462,6 +462,20 @@ mmsServer_handleReadJournalRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name); + MmsServer mmsServer = connection->server; + + if (mmsServer->readJournalHandler) + { + if (mmsServer->readJournalHandler(mmsServer->readJournalHandlerParameter, mmsDomain, logName, connection) == false) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + + /* TODO log access error */ + + return; + } + } + struct sJournalEncoder encoder; encoder.buffer = response->buffer; diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index e6cccfa5..311266c1 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -365,6 +365,13 @@ MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListA self->variableListAccessHandlerParameter = parameter; } +void +MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter) +{ + self->readJournalHandler = handler; + self->readJournalHandlerParameter = parameter; +} + void MmsServer_setClientAuthenticator(MmsServer self, AcseAuthenticator authenticator, void* authenticatorParameter) {