From 58b4d6c107dd884a28c23fa69183a90571473b5a Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 13 Aug 2018 11:51:36 +0200 Subject: [PATCH] -IEC 61850 server: added ReadAccessHandler to control read access --- config/stack_config.h | 3 + config/stack_config.h.cmake | 3 + .../server_example_password_auth.c | 18 ++++ src/common/inc/linked_list.h | 5 +- src/iec61850/inc/iec61850_server.h | 63 +++++++++++++- src/iec61850/inc_private/mms_mapping.h | 3 + .../inc_private/mms_mapping_internal.h | 5 ++ src/iec61850/server/impl/ied_server.c | 6 ++ src/iec61850/server/mms_mapping/mms_mapping.c | 85 ++++++++++++++++++- 9 files changed, 184 insertions(+), 7 deletions(-) diff --git a/config/stack_config.h b/config/stack_config.h index 3e79f121..07917e0b 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -161,6 +161,9 @@ /* include support for IEC 61850 log services */ #define CONFIG_IEC61850_LOG_SERVICE 1 +/* allow user to control read access by callback */ +#define CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL 1 + /* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index 621c359f..b3fcadf7 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -151,6 +151,9 @@ /* include support for IEC 61850 log services */ #cmakedefine01 CONFIG_IEC61850_LOG_SERVICE +/* allow user to control read access by callback */ +#cmakedefine01 CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL + /* Force memory alignment - required for some platforms (required more memory for buffered reporting) */ #define CONFIG_IEC61850_FORCE_MEMORY_ALIGNMENT 1 diff --git a/examples/server_example_password_auth/server_example_password_auth.c b/examples/server_example_password_auth/server_example_password_auth.c index 46715b1b..0e94522f 100644 --- a/examples/server_example_password_auth/server_example_password_auth.c +++ b/examples/server_example_password_auth/server_example_password_auth.c @@ -143,6 +143,21 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect return DATA_ACCESS_ERROR_SUCCESS; } +static MmsDataAccessError +readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) +{ + void* securityToken = ClientConnection_getSecurityToken(connection); + + if (securityToken != password2) { + + if ((dataObject == IEDMODEL_GenericIO_GGIO1_Ind1) || (dataObject == IEDMODEL_GenericIO_GGIO1_Ind2)) { + printf(" Access denied\n"); + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + return DATA_ACCESS_ERROR_SUCCESS; +} int main(int argc, char** argv) { @@ -178,6 +193,9 @@ int main(int argc, char** argv) { /* Set write access handler */ IedServer_handleWriteAccess(iedServer, IEDMODEL_GenericIO_LLN0_ModAuto_setVal, writeAccessHandler, NULL); + /* Set read access handler */ + IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); + /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, 102); diff --git a/src/common/inc/linked_list.h b/src/common/inc/linked_list.h index 1c8a6848..4cb6b451 100644 --- a/src/common/inc/linked_list.h +++ b/src/common/inc/linked_list.h @@ -1,7 +1,7 @@ /* * linked_list.h * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2018 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,7 +24,8 @@ #ifndef LINKED_LIST_H_ #define LINKED_LIST_H_ -#include "libiec61850_common_api.h" +#include +#include #ifdef __cplusplus extern "C" { diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index d5f7b954..7e146468 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -94,6 +94,12 @@ IedServerConfig_setReportBufferSize(IedServerConfig self, int reportBufferSize); int IedServerConfig_getReportBufferSize(IedServerConfig self); +void +IedServerConfig_setMaxMmsConnections(IedServerConfig self, int maxConnections); + +int +IedServerConfig_getMaxMmsConnections(IedServerConfig self); + /** * \brief Set the basepath of the file services * @@ -124,6 +130,12 @@ IedServerConfig_enableFileService(IedServerConfig self, bool enable); bool IedServerConfig_isFileServiceEnabled(IedServerConfig self); +void +IedServerConfig_enableFileWriteService(IedServerConfig self, bool enable); + +bool +IedServerConfig_isFileWriteServiceEnabled(IedServerConfig self); + /** * \brief Enable/disable the dynamic data set service for MMS * @@ -140,6 +152,22 @@ IedServerConfig_enableDynamicDataSetService(IedServerConfig self, bool enable); bool IedServerConfig_isDynamicDataSetServiceEnabled(IedServerConfig self); + +void +IedServerConfig_setMaxAssociationSpecificDataSets(IedServerConfig self, int maxDataSets); + +void +IedServerConfig_setMaxDomainSpecificDataSets(IedServerConfig self, int maxDataSets); + +void +IedServerConfig_setMaxDataSetEntries(IedServerConfig self, int maxDataSetEntries); + +void +IedServerConfig_enableWriteDataSetService(IedServerConfig self, bool enable); + +bool +IedServerConfig_isWriteDataSetServiceEnabled(IedServerConfig self); + /** * \brief Enable/disable the log service for MMS * @@ -1135,7 +1163,7 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler **************************************************************************/ /** - * \brief callback handler to intercept/control client access to data attributes + * \brief callback handler to intercept/control client write access to data attributes * * User provided callback function to intercept/control MMS client access to * IEC 61850 data attributes. The application can install the same handler @@ -1150,7 +1178,7 @@ IedServer_setSVCBHandler(IedServer self, SVControlBlock* svcb, SVCBEventHandler * \param connection the connection object of the client connection that invoked the write operation * \param parameter the user provided parameter * - * \return true if access is accepted, false if access is denied. + * \return DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied. */ typedef MmsDataAccessError (*WriteAccessHandler) (DataAttribute* dataAttribute, MmsValue* value, ClientConnection connection, void* parameter); @@ -1190,6 +1218,37 @@ typedef enum { void IedServer_setWriteAccessPolicy(IedServer self, FunctionalConstraint fc, AccessPolicy policy); +/** + * \brief callback handler to control client read access to data attributes + * + * User provided callback function to control MMS client read access to IEC 61850 + * data objects. The application is to allow read access to data objects for specific clients only. + * It can be used to implement a role based access control (RBAC). + * + * \param ld the logical device the client wants to access + * \param ln the logical node the client wants to access + * \param dataObject the data object the client wants to access + * \param fc the functional constraint of the access + * \param connection the client connection that causes the access + * \param parameter the user provided parameter + * + * \return DATA_ACCESS_ERROR_SUCCESS if access is accepted, DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED if access is denied. + */ +typedef MmsDataAccessError +(*ReadAccessHandler) (LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter); + +/** + * \brief Install the global read access handler + * + * The read access handler will be called for every read access before the server grants access to the client. + * + * \param self the instance of IedServer to operate on. + * \param handler the callback function that is invoked if a client tries to read a data object. + * \param parameter a user provided parameter that is passed to the callback function. + */ +void +IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/mms_mapping.h b/src/iec61850/inc_private/mms_mapping.h index a97e51d6..9c6f3cea 100644 --- a/src/iec61850/inc_private/mms_mapping.h +++ b/src/iec61850/inc_private/mms_mapping.h @@ -145,6 +145,9 @@ MmsMapping_setLogStorage(MmsMapping* self, const char* logRef, LogStorage logSto void MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttribute, WriteAccessHandler handler, void* parameter); +void +MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* paramter); + MmsDataAccessError Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsValue* value, MmsServerConnection connection); diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index f136a39a..24cc22c1 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -54,6 +54,11 @@ struct sMmsMapping { LinkedList attributeAccessHandlers; +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + ReadAccessHandler readAccessHandler; + void* readAccessHandlerParameter; +#endif + #if (CONFIG_IEC61850_SETTING_GROUPS == 1) LinkedList settingGroups; #endif diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c5f9a745..d93d8f68 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1329,6 +1329,12 @@ IedServer_handleWriteAccess(IedServer self, DataAttribute* dataAttribute, WriteA MmsMapping_installWriteAccessHandler(self->mmsMapping, dataAttribute, handler, parameter); } +void +IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter) +{ + MmsMapping_installReadAccessHandler(self->mmsMapping, handler, parameter); +} + void IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index e7958bb6..c6e28b0e 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2175,6 +2175,15 @@ MmsMapping_installWriteAccessHandler(MmsMapping* self, DataAttribute* dataAttrib accessHandler->handler = handler; } +void +MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, void* parameter) +{ +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + self->readAccessHandler = handler; + self->readAccessHandlerParameter = parameter; +#endif +} + #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) static MmsValue* @@ -2334,6 +2343,9 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo } #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ + /* handle read access to other objects */ + + exit_function: return retValue; } @@ -2419,9 +2431,6 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS char* separator = strchr(variableId, '$'); - if (separator == NULL) - return DATA_ACCESS_ERROR_SUCCESS; - #if (CONFIG_IEC61850_SETTING_GROUPS == 1) if (isFunctionalConstraintSE(separator)) { @@ -2437,6 +2446,76 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ +#if (CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL == 1) + if (self->readAccessHandler != NULL) + { + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld != NULL) { + + char str[65]; + + FunctionalConstraint fc; + + if (separator != NULL) { + fc = FunctionalConstraint_fromString(separator + 1); + + if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || + fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || + fc == IEC61850_FC_LG) + { + return DATA_ACCESS_ERROR_SUCCESS; + } + else { + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln != NULL) { + + + char* doStart = strchr(separator + 1, '$'); + + + if (doStart != NULL) { + + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else { + doEnd--; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) { + + if (dobj->modelType == DataObjectModelType) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + return self->readAccessHandler(ld, ln, (DataObject*) dobj, fc, clientConnection, + self->readAccessHandlerParameter); + } + } + } + } + } + } + } + + return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + } +#endif /* CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL */ + return DATA_ACCESS_ERROR_SUCCESS; }