From 3c918ee4e31eddf1ff2308363c700fccc6a4d247 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 14:46:53 +0100 Subject: [PATCH 01/14] - IED server: added IedServer_ListObjectsAccessHandler callback to control LISTOBJECTS access rights (LIB61850-417) --- src/iec61850/inc/iec61850_server.h | 20 +++ .../inc_private/mms_mapping_internal.h | 3 + src/iec61850/server/impl/ied_server.c | 7 + src/iec61850/server/mms_mapping/mms_mapping.c | 137 +++++++++++++++++- src/mms/inc_private/mms_server_internal.h | 6 + src/mms/inc_private/mms_server_libinternal.h | 6 + .../iso_mms/server/mms_get_namelist_service.c | 36 +++-- src/mms/iso_mms/server/mms_server.c | 35 +++++ 8 files changed, 233 insertions(+), 17 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 3db3235e..94324a00 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -2007,6 +2007,26 @@ typedef bool LIB61850_API void IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); +/** + * \brief Callback that is called when a client is invoking a list objects service + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * + * \return true to include the object in the service response, otherwise false + */ +typedef bool +(*IedServer_ListObjectsAccessHandler) (void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc); + +/** + * \brief Set a handler to control which objects are return by the list objects services + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 51714f11..6e3edb52 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -352,6 +352,9 @@ struct sMmsMapping { IedServer_DirectoryAccessHandler directoryAccessHandler; void* directoryAccessHandlerParameter; + + IedServer_ListObjectsAccessHandler listObjectsAccessHandler; + void* listObjectsAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 27d40624..aa8e84cd 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1967,3 +1967,10 @@ IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHan self->mmsMapping->directoryAccessHandler = handler; self->mmsMapping->directoryAccessHandlerParameter = parameter; } + +void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter) +{ + self->mmsMapping->listObjectsAccessHandler = handler; + self->mmsMapping->listObjectsAccessHandlerParameter = parameter; +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 2d76f39c..91e12f62 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3314,6 +3314,122 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE } } +static bool +mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection) +{ + MmsMapping* self = (MmsMapping*) parameter; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsListObjectsAccessHandler: Requested %s\n", variableId); + + bool allowAccess = true; + + if (self->listObjectsAccessHandler) + { + char* separator = strchr(variableId, '$'); + +#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + + if (separator) { + if (isFunctionalConstraint("SE", separator)) { + goto exit_function; + } + } + +#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld) + { + FunctionalConstraint fc = IEC61850_FC_NONE; + + if (separator) { + 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 || fc == IEC61850_FC_GO) + { + goto exit_function; + } + else + { + char str[65]; + + 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); + } + + if (fc == IEC61850_FC_SP) { + if (!strcmp(str, "SGCB")) + goto exit_function; + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) { + + if (dobj->modelType == DataObjectModelType) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, (DataObject*) dobj, fc); + } + } + } + else { + /* no data object but with FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + } + } + } + } + else { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln) { + /* only LN, no FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + } + } + } + else { + /* internal error ? - we should not end up here! */ + } + } + +exit_function: + return allowAccess; +} + static MmsDataAccessError mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3350,11 +3466,9 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS LogicalDevice* ld = IedModel_getDevice(self->model, ldName); - if (ld != NULL) { - - char str[65]; - - FunctionalConstraint fc; + if (ld != NULL) + { + FunctionalConstraint fc = IEC61850_FC_NONE; if (separator != NULL) { fc = FunctionalConstraint_fromString(separator + 1); @@ -3367,6 +3481,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } else { + char str[65]; + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); @@ -3417,6 +3533,16 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } } } + else { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln != NULL) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, + self->readAccessHandlerParameter); + } + } } return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; @@ -3666,6 +3792,7 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadHandler(self->mmsServer, mmsReadHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); + MmsServer_installListAccessHandler(self->mmsServer, mmsListObjectsAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); MmsServer_installGetNameListHandler(self->mmsServer, mmsGetNameListHandler, (void*) self); diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index ec70d359..606e0827 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -120,6 +120,9 @@ struct sMmsServer { MmsWriteVariableHandler writeHandler; void* writeHandlerParameter; + MmsListAccessHandler listAccessHandler; + void* listAccessHandlerParameter; + MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; @@ -422,6 +425,9 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va LIB61850_INTERNAL MmsValue* mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess); +LIB61850_INTERNAL bool +mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection); + LIB61850_INTERNAL void mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index 444989c9..da5de7a5 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -37,6 +37,9 @@ typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter, MmsDomain* domain, char* variableId, MmsValue* value, MmsServerConnection connection); +typedef bool (*MmsListAccessHandler) (void* parameter, MmsDomain* domain, + char* variableId, MmsServerConnection connection); + typedef void (*MmsConnectionHandler)(void* parameter, MmsServerConnection connection, MmsServerEvent event); @@ -63,6 +66,9 @@ LIB61850_INTERNAL void MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler, void* parameter); +LIB61850_INTERNAL void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter); + /** * A connection handler will be invoked whenever a new client connection is opened or closed */ diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 2170f8c1..0a9c6f57 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -129,7 +129,7 @@ appendMmsSubVariable(char* name, char* child) } static LinkedList -addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable) +addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameList, MmsDomain* domain, char* prefix, MmsVariableSpecification* variable) { LinkedList listElement = nameList; @@ -158,14 +158,20 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ if (variableName) - { - listElement = LinkedList_insertAfter(listElement, variableName); + { + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variableName, connection); + + if (accessAllowed) { + + listElement = LinkedList_insertAfter(listElement, variableName); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[index[i]]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[index[i]]); #else - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ + + } } } @@ -233,7 +239,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) bool allowAccess = true; if (connection->server->getNameListHandler) { - allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, domain, connection); } if (allowAccess) { @@ -255,22 +261,28 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) for (i = 0; i < domain->namedVariablesCount; i++) { + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variables[index[i]]->name, connection); + + if (accessAllowed) { + #if (CONFIG_MMS_SORT_NAME_LIST == 1) - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); #else - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); #endif #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1) - char* prefix = variables[index[i]]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); + char* prefix = variables[index[i]]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[index[i]]); #else - char* prefix = variables[i]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); + char* prefix = variables[i]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ + } + } #if (CONFIG_MMS_SORT_NAME_LIST == 1) diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index b9d4c798..3229b21e 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -351,6 +351,13 @@ MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler writeHandl self->writeHandlerParameter = parameter; } +void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter) +{ + self->listAccessHandler = listAccessHandler; + self->listAccessHandlerParameter = parameter; +} + void MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connectionHandler, void* parameter) { @@ -579,6 +586,34 @@ exit_function: return value; } +MmsDataAccessError +mmsServer_checkReadAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess) +{ + MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS; + + printf("mmsServer_checkReadAccess(%s/%s)\n", domain->domainName, itemId); + + if (self->readAccessHandler) { + accessError = + self->readAccessHandler(self->readAccessHandlerParameter, (domain == (MmsDomain*) self->device) ? NULL : domain, + itemId, connection, isDirectAccess); + } + + return accessError; +} + +bool +mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection) +{ + bool allowAccess = true; + + if (self->listAccessHandler) { + allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, domain, itemId, connection); + } + + return allowAccess; +} + MmsDevice* MmsServer_getDevice(MmsServer self) { From 2467605e2358a17d9fc8ba0d24d3303fb8704796 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:28:00 +0100 Subject: [PATCH 02/14] - IED server: apply LISTOBJECTS restrictions to get-variable-access-attributes servic (LIB61850-417) --- .../iso_mms/server/mms_get_namelist_service.c | 3 +++ .../server/mms_get_var_access_service.c | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 0a9c6f57..6d6c1568 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -172,6 +172,9 @@ addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameLis #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ } + else { + GLOBAL_FREEMEM(variableName); + } } } diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index abdf6d02..d936701c 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -1,7 +1,7 @@ /* * mms_get_var_access_service.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -191,7 +191,8 @@ deleteVariableAccessAttributesResponse( getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; @@ -215,8 +216,10 @@ createVariableAccessAttributesResponse( MmsVariableSpecification* namedVariable = NULL; + MmsDomain* domain = NULL; + if (domainId != NULL) { - MmsDomain* domain = MmsDevice_getDomain(device, domainId); + domain = MmsDevice_getDomain(device, domainId); if (domain == NULL) { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); @@ -233,9 +236,20 @@ createVariableAccessAttributesResponse( namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s not known\n", nameId); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + + goto exit_function; + } + + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); + + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); From efec5194a284069921fb37920a3ce033a9285454 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:37:34 +0100 Subject: [PATCH 03/14] - replaced tabs by spaces in mms_get_var_access_service.c --- .../server/mms_get_var_access_service.c | 513 +++++++++--------- 1 file changed, 256 insertions(+), 257 deletions(-) diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index d936701c..840d90b5 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -32,250 +32,250 @@ static int createTypeSpecification ( - MmsVariableSpecification* namedVariable, - TypeSpecification_t* typeSpec) + MmsVariableSpecification* namedVariable, + TypeSpecification_t* typeSpec) { - if (namedVariable->type == MMS_ARRAY) { - typeSpec->present = TypeSpecification_PR_array; - - asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), - (long) namedVariable->typeSpec.array.elementCount); - - typeSpec->choice.array.packed = NULL; - typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, - typeSpec->choice.array.elementType); - } - else if (namedVariable->type == MMS_STRUCTURE) { - - typeSpec->present = TypeSpecification_PR_structure; - - int componentCount = namedVariable->typeSpec.structure.elementCount; - - typeSpec->choice.structure.components.list.count = componentCount; - typeSpec->choice.structure.components.list.size = componentCount; - - typeSpec->choice.structure.components.list.array - = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); - - int i; - - for (i = 0; i < componentCount; i++) { - - typeSpec->choice.structure.components.list.array[i] = - (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName = - (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName->buf = - (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentName->size = - strlen(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentType = - (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.structure.elements[i], - typeSpec->choice.structure.components.list.array[i]->componentType); - } - } - else { - - switch (namedVariable->type) { - case MMS_BOOLEAN: - typeSpec->present = TypeSpecification_PR_boolean; - break; - case MMS_BIT_STRING: - typeSpec->present = TypeSpecification_PR_bitstring; - typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; - break; - case MMS_INTEGER: - typeSpec->present = TypeSpecification_PR_integer; - typeSpec->choice.integer = namedVariable->typeSpec.integer; - break; - case MMS_UNSIGNED: - typeSpec->present = TypeSpecification_PR_unsigned; - typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; - break; - case MMS_FLOAT: - typeSpec->present = TypeSpecification_PR_floatingpoint; - typeSpec->choice.floatingpoint.exponentwidth = - namedVariable->typeSpec.floatingpoint.exponentWidth; - typeSpec->choice.floatingpoint.formatwidth = - namedVariable->typeSpec.floatingpoint.formatWidth; - break; - case MMS_OCTET_STRING: - typeSpec->present = TypeSpecification_PR_octetstring; - typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; - break; - case MMS_VISIBLE_STRING: - typeSpec->present = TypeSpecification_PR_visiblestring; - typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; - break; - case MMS_STRING: - typeSpec->present = TypeSpecification_PR_mMSString; - typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; - break; - case MMS_UTC_TIME: - typeSpec->present = TypeSpecification_PR_utctime; - break; - case MMS_BINARY_TIME: - typeSpec->present = TypeSpecification_PR_binarytime; - - if (namedVariable->typeSpec.binaryTime == 6) - typeSpec->choice.binarytime = 1; - else - typeSpec->choice.binarytime = 0; - - break; - default: - if (DEBUG_MMS_SERVER) - printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); - return -1; - break; - } - } - - return 1; + if (namedVariable->type == MMS_ARRAY) { + typeSpec->present = TypeSpecification_PR_array; + + asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), + (long) namedVariable->typeSpec.array.elementCount); + + typeSpec->choice.array.packed = NULL; + typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, + typeSpec->choice.array.elementType); + } + else if (namedVariable->type == MMS_STRUCTURE) { + + typeSpec->present = TypeSpecification_PR_structure; + + int componentCount = namedVariable->typeSpec.structure.elementCount; + + typeSpec->choice.structure.components.list.count = componentCount; + typeSpec->choice.structure.components.list.size = componentCount; + + typeSpec->choice.structure.components.list.array + = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); + + int i; + + for (i = 0; i < componentCount; i++) { + + typeSpec->choice.structure.components.list.array[i] = + (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName = + (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName->buf = + (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentName->size = + strlen(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentType = + (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.structure.elements[i], + typeSpec->choice.structure.components.list.array[i]->componentType); + } + } + else { + + switch (namedVariable->type) { + case MMS_BOOLEAN: + typeSpec->present = TypeSpecification_PR_boolean; + break; + case MMS_BIT_STRING: + typeSpec->present = TypeSpecification_PR_bitstring; + typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; + break; + case MMS_INTEGER: + typeSpec->present = TypeSpecification_PR_integer; + typeSpec->choice.integer = namedVariable->typeSpec.integer; + break; + case MMS_UNSIGNED: + typeSpec->present = TypeSpecification_PR_unsigned; + typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; + break; + case MMS_FLOAT: + typeSpec->present = TypeSpecification_PR_floatingpoint; + typeSpec->choice.floatingpoint.exponentwidth = + namedVariable->typeSpec.floatingpoint.exponentWidth; + typeSpec->choice.floatingpoint.formatwidth = + namedVariable->typeSpec.floatingpoint.formatWidth; + break; + case MMS_OCTET_STRING: + typeSpec->present = TypeSpecification_PR_octetstring; + typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; + break; + case MMS_VISIBLE_STRING: + typeSpec->present = TypeSpecification_PR_visiblestring; + typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; + break; + case MMS_STRING: + typeSpec->present = TypeSpecification_PR_mMSString; + typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; + break; + case MMS_UTC_TIME: + typeSpec->present = TypeSpecification_PR_utctime; + break; + case MMS_BINARY_TIME: + typeSpec->present = TypeSpecification_PR_binarytime; + + if (namedVariable->typeSpec.binaryTime == 6) + typeSpec->choice.binarytime = 1; + else + typeSpec->choice.binarytime = 0; + + break; + default: + if (DEBUG_MMS_SERVER) + printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); + return -1; + break; + } + } + + return 1; } static void freeTypeSpecRecursive(TypeSpecification_t* typeSpec) { - if (typeSpec->present == TypeSpecification_PR_structure) { - int elementCount = - typeSpec->choice.structure.components.list.count; - - int i; - - for (i = 0; i < elementCount; i++) { - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); - freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); - } - else if (typeSpec->present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); - freeTypeSpecRecursive(typeSpec->choice.array.elementType); - GLOBAL_FREEMEM(typeSpec->choice.array.elementType); - } + if (typeSpec->present == TypeSpecification_PR_structure) { + int elementCount = + typeSpec->choice.structure.components.list.count; + + int i; + + for (i = 0; i < elementCount; i++) { + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); + freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); + } + else if (typeSpec->present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); + freeTypeSpecRecursive(typeSpec->choice.array.elementType); + GLOBAL_FREEMEM(typeSpec->choice.array.elementType); + } } static void deleteVariableAccessAttributesResponse( - GetVariableAccessAttributesResponse_t* getVarAccessAttr) + GetVariableAccessAttributesResponse_t* getVarAccessAttr) { - if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { - int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; - - int i; - for (i = 0; i < count; i++) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); - TypeSpecification_t* typeSpec = - getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; - freeTypeSpecRecursive(typeSpec); - GLOBAL_FREEMEM(typeSpec); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); - - getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; - getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; - getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } - else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; - freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); - - getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; - } + if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { + int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; + + int i; + for (i = 0; i < count; i++) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); + TypeSpecification_t* typeSpec = + getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; + freeTypeSpecRecursive(typeSpec); + GLOBAL_FREEMEM(typeSpec); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); + + getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; + getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; + getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; + freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); + + getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; + } } static void createVariableAccessAttributesResponse( - MmsServerConnection connection, - char* domainId, - char* nameId, - int invokeId, - ByteBuffer* response) + MmsServerConnection connection, + char* domainId, + char* nameId, + int invokeId, + ByteBuffer* response) { - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - MmsVariableSpecification* namedVariable = NULL; + MmsVariableSpecification* namedVariable = NULL; - MmsDomain* domain = NULL; + MmsDomain* domain = NULL; - if (domainId != NULL) { - domain = MmsDevice_getDomain(device, domainId); + if (domainId != NULL) { + domain = MmsDevice_getDomain(device, domainId); - if (domain == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); + if (domain == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_function; + } - namedVariable = MmsDomain_getNamedVariable(domain, nameId); - } + namedVariable = MmsDomain_getNamedVariable(domain, nameId); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else - namedVariable = MmsDevice_getNamedVariable(device, nameId); + else + namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + if (namedVariable == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); - if (!accessAllowed) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); + MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); - mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = - ConfirmedServiceResponse_PR_getVariableAccessAttributes; + mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = + ConfirmedServiceResponse_PR_getVariableAccessAttributes; - GetVariableAccessAttributesResponse_t* getVarAccessAttr; + GetVariableAccessAttributesResponse_t* getVarAccessAttr; - getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. - confirmedServiceResponse.choice.getVariableAccessAttributes); + getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. + confirmedServiceResponse.choice.getVariableAccessAttributes); - getVarAccessAttr->mmsDeletable = 0; + getVarAccessAttr->mmsDeletable = 0; - createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); + createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); - asn_enc_rval_t rval = - der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); + asn_enc_rval_t rval = + der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); - if (rval.encoded == -1) { - response->size = 0; + if (rval.encoded == -1) { + response->size = 0; if (DEBUG_MMS_SERVER) printf("MMS getVariableAccessAttributes: message to large! send error PDU!\n"); @@ -284,81 +284,80 @@ createVariableAccessAttributesResponse( MMS_ERROR_SERVICE_OTHER); goto exit_function; - } + } - deleteVariableAccessAttributesResponse(getVarAccessAttr); + deleteVariableAccessAttributesResponse(getVarAccessAttr); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); exit_function: - return; + return; } int mmsServer_handleGetVariableAccessAttributesRequest( - MmsServerConnection connection, - uint8_t* buffer, int bufPos, int maxBufPos, - uint32_t invokeId, - ByteBuffer* response) + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) { - int retVal = 0; + int retVal = 0; - GetVariableAccessAttributesRequest_t* request = 0; + GetVariableAccessAttributesRequest_t* request = 0; - asn_dec_rval_t rval; /* Decoder return value */ + asn_dec_rval_t rval; /* Decoder return value */ - rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, - (void**) &request, buffer + bufPos, maxBufPos - bufPos); + rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, + (void**) &request, buffer + bufPos, maxBufPos - bufPos); - if (rval.code == RC_OK) { - if (request->present == GetVariableAccessAttributesRequest_PR_name) { - if (request->choice.name.present == ObjectName_PR_domainspecific) { - Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; - Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; + if (rval.code == RC_OK) { + if (request->present == GetVariableAccessAttributesRequest_PR_name) { + if (request->choice.name.present == ObjectName_PR_domainspecific) { + Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; + Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; - char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); - createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(domainIdStr); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(domainIdStr); + GLOBAL_FREEMEM(nameIdStr); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (request->choice.name.present == ObjectName_PR_vmdspecific) { - Identifier_t nameId = request->choice.name.choice.vmdspecific; + else if (request->choice.name.present == ObjectName_PR_vmdspecific) { + Identifier_t nameId = request->choice.name.choice.vmdspecific; - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); - createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(nameIdStr); + } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); - retVal = -1; - } - - asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); - - return retVal; + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); + retVal = -1; + } + + asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); + + return retVal; } #endif /* (MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1) */ - From 98c04dfeda5c81a5f80ed4c9ce8ef713e93b323c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:55:20 +0100 Subject: [PATCH 04/14] - added LISTOBEJCTS access control to server_example_access_control (LIB61850-417) --- .../server_example_access_control.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) 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 e0b789af..365576e8 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -146,6 +146,20 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu return DATA_ACCESS_ERROR_SUCCESS; } +static bool +listObjectsAccessHandler(void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc) +{ + printf("list objects access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject ? dataObject->name : "-", FunctionalConstraint_toString(fc)); + + if (!strcmp(ln->name, "GGIO1")) { + if (dataObject && !strcmp(dataObject->name, "AnIn1")) { + return false; + } + } + + return true; +} + static bool directoryAccessHandler(void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice) { @@ -258,6 +272,9 @@ main(int argc, char** argv) IedServer_setDirectoryAccessHandler(iedServer, directoryAccessHandler, NULL); + /* control visibility of data objects in directory (get-name-list) and variable description (get-variable-access-attributes) services */ + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, tcpPort); From bbe23b4b00f6aa105457529f91f8b545efaa2683 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 14 Aug 2023 18:28:45 +0100 Subject: [PATCH 05/14] - extended ListObjectHandler to support different ACSI classes (LIB61850-417) --- .../server_example_access_control.c | 63 +++++-- .../static_model.c | 14 +- src/iec61850/inc/iec61850_client.h | 14 -- src/iec61850/inc/iec61850_common.h | 15 ++ src/iec61850/inc/iec61850_server.h | 10 +- src/iec61850/server/mms_mapping/mms_mapping.c | 163 ++++++++++++++++-- src/mms/inc_private/mms_server_internal.h | 2 +- src/mms/inc_private/mms_server_libinternal.h | 2 +- .../iso_mms/server/mms_get_namelist_service.c | 28 ++- .../server/mms_get_var_access_service.c | 2 +- src/mms/iso_mms/server/mms_server.c | 4 +- 11 files changed, 261 insertions(+), 56 deletions(-) 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 365576e8..7dca5dc3 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -23,6 +23,38 @@ sigint_handler(int signalId) running = 0; } +static const char* +ACSIClassToStr(ACSIClass acsiClass) +{ + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; + } +} + static ControlHandlerResult controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test) { @@ -65,7 +97,6 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v return CONTROL_RESULT_OK; } - static void connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) { @@ -137,25 +168,35 @@ dataSetAccessHandler(void* parameter, ClientConnection connection, IedServer_Dat static MmsDataAccessError readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter) { - printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name); + printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject ? dataObject->name : "-"); - if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) { - return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + if (dataObject == NULL) { + if (!strcmp(ln->name, "GGIO1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } } return DATA_ACCESS_ERROR_SUCCESS; } static bool -listObjectsAccessHandler(void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc) +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) { - printf("list objects access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject ? dataObject->name : "-", FunctionalConstraint_toString(fc)); + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); - if (!strcmp(ln->name, "GGIO1")) { - if (dataObject && !strcmp(dataObject->name, "AnIn1")) { - return false; - } - } + // if (acsiClass == ACSI_CLASS_BRCB) { + // return true; + // } + + // return false; return true; } diff --git a/examples/server_example_access_control/static_model.c b/examples/server_example_access_control/static_model.c index 26f5f4fc..f38f886e 100644 --- a/examples/server_example_access_control/static_model.c +++ b/examples/server_example_access_control/static_model.c @@ -1,7 +1,7 @@ /* * static_model.c * - * automatically generated from simpleIO_direct_control.cid + * automatically generated from ../../examples/server_example_basic_io/simpleIO_direct_control.cid */ #include "static_model.h" @@ -2169,7 +2169,15 @@ ReportControlBlock iedModel_GenericIO_LLN0_report9 = {&iedModel_GenericIO_LLN0, +extern LogControlBlock iedModel_GenericIO_LLN0_lcb0; +extern LogControlBlock iedModel_GenericIO_LLN0_lcb1; +LogControlBlock iedModel_GenericIO_LLN0_lcb0 = {&iedModel_GenericIO_LLN0, "EventLog", "Events", "GenericIO/LLN0$EventLog", 3, 0, true, true, &iedModel_GenericIO_LLN0_lcb1}; +LogControlBlock iedModel_GenericIO_LLN0_lcb1 = {&iedModel_GenericIO_LLN0, "GeneralLog", NULL, NULL, 3, 0, true, true, NULL}; +extern Log iedModel_GenericIO_LLN0_log0; +extern Log iedModel_GenericIO_LLN0_log1; +Log iedModel_GenericIO_LLN0_log0 = {&iedModel_GenericIO_LLN0, "GeneralLog", &iedModel_GenericIO_LLN0_log1}; +Log iedModel_GenericIO_LLN0_log1 = {&iedModel_GenericIO_LLN0, "EventLog", NULL}; IedModel iedModel = { @@ -2180,8 +2188,8 @@ IedModel iedModel = { NULL, NULL, NULL, - NULL, - NULL, + &iedModel_GenericIO_LLN0_lcb0, + &iedModel_GenericIO_LLN0_log0, initializeValues }; diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index c3a174fe..1326c1e7 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -2467,20 +2467,6 @@ IedConnection_getServerDirectory(IedConnection self, IedClientError* error, bool LIB61850_API LinkedList /**/ IedConnection_getLogicalDeviceDirectory(IedConnection self, IedClientError* error, const char* logicalDeviceName); -typedef enum { - ACSI_CLASS_DATA_OBJECT, - ACSI_CLASS_DATA_SET, - ACSI_CLASS_BRCB, - ACSI_CLASS_URCB, - ACSI_CLASS_LCB, - ACSI_CLASS_LOG, - ACSI_CLASS_SGCB, - ACSI_CLASS_GoCB, - ACSI_CLASS_GsCB, - ACSI_CLASS_MSVCB, - ACSI_CLASS_USVCB -} ACSIClass; - /** * \brief returns a list of all MMS variables that are children of the given logical node * diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index 9a364656..53aa6e86 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -55,6 +55,21 @@ typedef struct { uint8_t dstAddress[6]; } PhyComAddress; +/** IEC 61850 ACSI classes */ +typedef enum { + ACSI_CLASS_DATA_OBJECT, + ACSI_CLASS_DATA_SET, + ACSI_CLASS_BRCB, + ACSI_CLASS_URCB, + ACSI_CLASS_LCB, + ACSI_CLASS_LOG, + ACSI_CLASS_SGCB, + ACSI_CLASS_GoCB, + ACSI_CLASS_GsCB, + ACSI_CLASS_MSVCB, + ACSI_CLASS_USVCB +} ACSIClass; + /** * \brief Control model (represented by "ctlModel" attribute) */ diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 94324a00..2288b207 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -2010,13 +2010,21 @@ IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHan /** * \brief Callback that is called when a client is invoking a list objects service * + * This callback can be used to control the list object access to specific objects and is called for each object that are subject to a client request. + * * \param parameter user provided parameter * \param connection client connection that is involved + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param fc the functional constraint of the object of IEC61850_FC_NONE when the object has no FC. * * \return true to include the object in the service response, otherwise false */ typedef bool -(*IedServer_ListObjectsAccessHandler) (void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc); +(*IedServer_ListObjectsAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc); /** * \brief Set a handler to control which objects are return by the list objects services diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 91e12f62..9b37a81b 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3315,7 +3315,7 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE } static bool -mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection) +mmsListObjectsAccessHandler(void* parameter, MmsGetNameListType listType, MmsDomain* domain, char* variableId, MmsServerConnection connection) { MmsMapping* self = (MmsMapping*) parameter; @@ -3324,20 +3324,75 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId bool allowAccess = true; - if (self->listObjectsAccessHandler) + if (listType == MMS_GETNAMELIST_DATASETS) { - char* separator = strchr(variableId, '$'); + if (self->listObjectsAccessHandler) { -#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + char str[65]; - if (separator) { - if (isFunctionalConstraint("SE", separator)) { - goto exit_function; + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* objectName = variableId; + + char* separator = strchr(variableId, '$'); + + if (separator) { + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } } + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_SET, ld, ln, objectName, NULL, IEC61850_FC_NONE); } -#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + return allowAccess; + } + else if (listType == MMS_GETNAMELIST_JOURNALS) + { + if (self->listObjectsAccessHandler) { + char str[65]; + + char* ldName = MmsDomain_getName(domain); + + char* objectName = variableId; + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + LogicalNode* ln = NULL; + + char* separator = strchr(variableId, '$'); + + if (separator) { + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + objectName = separator + 1; + } + } + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, objectName, NULL, IEC61850_FC_NONE); + } + + return allowAccess; + } + + if (self->listObjectsAccessHandler) + { + char* separator = strchr(variableId, '$'); char* ldName = MmsDomain_getName(domain); LogicalDevice* ld = IedModel_getDevice(self->model, ldName); @@ -3353,11 +3408,76 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || fc == IEC61850_FC_LG || fc == IEC61850_FC_GO) { + char* subObjectName = NULL; + + char str[65]; + char subObjectBuf[65]; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln) { + 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); + + subObjectName = StringUtils_copyStringToBuffer(doEnd + 2, subObjectBuf); + } + } + } + + ACSIClass acsiClass = ACSI_CLASS_USVCB; + + switch (fc) + { + case IEC61850_FC_BR: + acsiClass = ACSI_CLASS_BRCB; + break; + + case IEC61850_FC_RP: + acsiClass = ACSI_CLASS_URCB; + break; + + case IEC61850_FC_GO: + acsiClass = ACSI_CLASS_GoCB; + break; + + case IEC61850_FC_LG: + acsiClass = ACSI_CLASS_LCB; + break; + + case IEC61850_FC_MS: + acsiClass = ACSI_CLASS_MSVCB; + break; + + default: + break; + } + + if (self->listObjectsAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, acsiClass, ld, ln, str, subObjectName, fc); + } + goto exit_function; } else { char str[65]; + char* subObjectName = NULL; + char subObjectBuf[65]; StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); @@ -3378,11 +3498,20 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId doEnd--; StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + + subObjectName = StringUtils_copyStringToBuffer(doEnd + 2, subObjectBuf); } if (fc == IEC61850_FC_SP) { - if (!strcmp(str, "SGCB")) + if (!strcmp(str, "SGCB")) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, subObjectName, fc); + goto exit_function; + } } ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); @@ -3394,7 +3523,9 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, (DataObject*) dobj, fc); + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, dobj->name, subObjectName, fc); + } } } } @@ -3404,7 +3535,9 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } } } } @@ -3417,7 +3550,9 @@ mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + if (self->listObjectsAccessHandler) { + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ACSI_CLASS_DATA_OBJECT, ld, ln, NULL, NULL, fc); + } } } } @@ -3505,8 +3640,10 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } if (fc == IEC61850_FC_SP) { - if (!strcmp(str, "SGCB")) + if (!strcmp(str, "SGCB")) { + //TODO RBAC2 add callback return DATA_ACCESS_ERROR_SUCCESS; + } } ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 606e0827..08d0988a 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -426,7 +426,7 @@ LIB61850_INTERNAL MmsValue* mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess); LIB61850_INTERNAL bool -mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection); +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection); LIB61850_INTERNAL void mmsServer_createMmsWriteResponse(MmsServerConnection connection, diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index da5de7a5..bdaf4b68 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -37,7 +37,7 @@ typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter, MmsDomain* domain, char* variableId, MmsValue* value, MmsServerConnection connection); -typedef bool (*MmsListAccessHandler) (void* parameter, MmsDomain* domain, +typedef bool (*MmsListAccessHandler) (void* parameter, MmsGetNameListType listType, MmsDomain* domain, char* variableId, MmsServerConnection connection); typedef void (*MmsConnectionHandler)(void* parameter, diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 6d6c1568..7ee8a581 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -159,7 +159,7 @@ addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameLis if (variableName) { - bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variableName, connection); + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variableName, connection); if (accessAllowed) { @@ -218,7 +218,11 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName) MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); - LinkedList_add(nameList, (void*) journal->name); + allowAccess = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_JOURNALS, domain, journal->name, connection); + + if (allowAccess) { + LinkedList_add(nameList, (void*) journal->name); + } } } @@ -264,7 +268,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) for (i = 0; i < domain->namedVariablesCount; i++) { - bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variables[index[i]]->name, connection); + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, variables[index[i]]->name, connection); if (accessAllowed) { @@ -301,7 +305,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) #if (MMS_DATA_SET_SERVICE == 1) static LinkedList -createStringsFromNamedVariableList(LinkedList variableLists) +createStringsFromNamedVariableList(LinkedList variableLists, MmsServerConnection connection, MmsDomain* domain) { LinkedList nameList = LinkedList_create(); LinkedList variableListsElement = LinkedList_getNext(variableLists); @@ -310,8 +314,14 @@ createStringsFromNamedVariableList(LinkedList variableLists) MmsNamedVariableList variableList = (MmsNamedVariableList) variableListsElement->data; - LinkedList_add(nameList, - StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + printf("createStringsFromNamedVariableList: %s\n", MmsNamedVariableList_getName(variableList)); + + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATASETS, domain, variableList->name, connection); + + if (accessAllowed) { + LinkedList_add(nameList, + StringUtils_copyString(MmsNamedVariableList_getName(variableList))); + } variableListsElement = LinkedList_getNext(variableListsElement); } @@ -338,7 +348,7 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain if (allowAccess) { LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, domain); } } @@ -354,7 +364,7 @@ getNamedVariableListsVMDSpecific(MmsServerConnection connection) LinkedList variableLists = MmsDevice_getNamedVariableLists(device); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } @@ -367,7 +377,7 @@ getNamedVariableListAssociationSpecific(MmsServerConnection connection) LinkedList variableLists = MmsServerConnection_getNamedVariableLists(connection); - nameList = createStringsFromNamedVariableList(variableLists); + nameList = createStringsFromNamedVariableList(variableLists, connection, NULL); return nameList; } diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index 840d90b5..c27ff76b 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -245,7 +245,7 @@ createVariableAccessAttributesResponse( goto exit_function; } - bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); + bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATA, domain, nameId, connection); if (!accessAllowed) { if (DEBUG_MMS_SERVER) diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 3229b21e..18b95464 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -603,12 +603,12 @@ mmsServer_checkReadAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsSe } bool -mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection) +mmsServer_checkListAccess(MmsServer self, MmsGetNameListType listType, MmsDomain* domain, char* itemId, MmsServerConnection connection) { bool allowAccess = true; if (self->listAccessHandler) { - allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, domain, itemId, connection); + allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, listType, domain, itemId, connection); } return allowAccess; From cb1774f33d3a414c80cc25b1caf1b722bc8a8107 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 15 Aug 2023 12:24:24 +0100 Subject: [PATCH 06/14] - removed debug output (LIB61850-417) --- src/mms/iso_mms/server/mms_get_namelist_service.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 7ee8a581..e42dd25a 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -314,8 +314,6 @@ createStringsFromNamedVariableList(LinkedList variableLists, MmsServerConnection MmsNamedVariableList variableList = (MmsNamedVariableList) variableListsElement->data; - printf("createStringsFromNamedVariableList: %s\n", MmsNamedVariableList_getName(variableList)); - bool accessAllowed = mmsServer_checkListAccess(connection->server, MMS_GETNAMELIST_DATASETS, domain, variableList->name, connection); if (accessAllowed) { From ad267954658fb246e9f23e13a5e4936ecceed579 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 15 Aug 2023 18:19:57 +0100 Subject: [PATCH 07/14] - IED server access control: Added IedServer_ControlBlockAccessHandler to control read and write access to control blocks. Implemented for SGCBs, RCBs, LCBs. (LIB61850-420) --- src/iec61850/inc/iec61850_server.h | 11 ++++ .../inc_private/mms_mapping_internal.h | 3 ++ src/iec61850/server/impl/ied_server.c | 7 +++ src/iec61850/server/mms_mapping/logging.c | 46 +++++++++++++++++ src/iec61850/server/mms_mapping/mms_mapping.c | 50 ++++++++++++++++++- src/iec61850/server/mms_mapping/reporting.c | 43 +++++++++++++++- 6 files changed, 157 insertions(+), 3 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 2288b207..ad88b369 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -2035,6 +2035,17 @@ typedef bool LIB61850_API void IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter); +typedef enum { + IEC61850_CB_ACCESS_TYPE_READ, + IEC61850_CB_ACCESS_TYPE_WRITE +} IedServer_ControlBlockAccessType; + +typedef bool +(*IedServer_ControlBlockAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType); + +LIB61850_API void +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 6e3edb52..4a27db2c 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -355,6 +355,9 @@ struct sMmsMapping { IedServer_ListObjectsAccessHandler listObjectsAccessHandler; void* listObjectsAccessHandlerParameter; + + IedServer_ControlBlockAccessHandler controlBlockAccessHandler; + void* controlBlockAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index aa8e84cd..0ad910eb 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1974,3 +1974,10 @@ IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAcces self->mmsMapping->listObjectsAccessHandler = handler; self->mmsMapping->listObjectsAccessHandlerParameter = parameter; } + +void +IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter) +{ + self->mmsMapping->controlBlockAccessHandler = handler; + self->mmsMapping->controlBlockAccessHandlerParameter = parameter; +} diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index c55a1ff4..0e682f72 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -524,6 +524,7 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma } else { + //TODO RBAC2 remove!? if (self->lcbAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -534,6 +535,32 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma goto exit_function; } } + + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { + goto exit_function; + } + } } if (strcmp(varName, "LogEna") == 0) { @@ -747,6 +774,7 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, { bool allowAccess = true; + //TODO RBAC2 remove?! if (self->lcbAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -758,6 +786,24 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, } } + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + allowAccess = false; + + value = &objectAccessDenied; + } + } + } + } + if (allowAccess) { updateLogStatusInLCB(logControl); diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 9b37a81b..9aaeea3b 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2701,6 +2701,42 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, char* nameId = nextSep + 1; + /* check access permissions */ + if (self->controlBlockAccessHandler) + { + MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); + + if (ld) { + char lnName[65]; + strncpy(lnName, variableId, 64); + lnName[64] = 0; + lnName[lnNameLength] = 0; + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); + + if (ln) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, "SGCB", nameId, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + } + else { + retVal = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + } + + if (retVal != DATA_ACCESS_ERROR_SUCCESS) { + return retVal; + } + } + if (strcmp(nameId, "ActSG") == 0) { SettingGroup* sg = getSettingGroupByMmsDomain(self, domain); MmsDataAccessError retVal = DATA_ACCESS_ERROR_SUCCESS; @@ -2709,6 +2745,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, uint32_t val = MmsValue_toUint32(value); if ((val > 0) && (val <= sg->sgcb->numOfSGs)) { + if (val != sg->sgcb->actSG) { if (sg->actSgChangedHandler) { @@ -3640,8 +3677,17 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } if (fc == IEC61850_FC_SP) { - if (!strcmp(str, "SGCB")) { - //TODO RBAC2 add callback + if (!strcmp(str, "SGCB")) + { + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, "", IEC61850_CB_ACCESS_TYPE_READ) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + return DATA_ACCESS_ERROR_SUCCESS; } } diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 9cbbb4f2..2ad84048 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1781,6 +1781,26 @@ ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerCon clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); } + if (mmsMapping->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; + + LogicalNode* ln = rc->rcb->parent; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + if (mmsMapping->controlBlockAccessHandler(mmsMapping->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->name, elementName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + accessAllowed = false; + } + } + + //TODO RBAC2 remove? if (mmsMapping->rcbAccessHandler) { if (mmsMapping->rcbAccessHandler(mmsMapping->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER) == false) { accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -1890,7 +1910,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - /* check if write access to RCB is allowed on this connection */ + //TODO RBAC2 remove? if (self->rcbAccessHandler) { if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -1899,6 +1919,27 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } } + /* check if write access to RCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (rc->rcb->buffered) + acsiClass = ACSI_CLASS_BRCB; + else + acsiClass = ACSI_CLASS_URCB; + + LogicalNode* ln = rc->rcb->parent; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->name, elementName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + goto exit_function_only_tracking; + } + } + /* check reservation timeout for buffered RCBs */ if (rc->buffered) { From a397eeee85a1c6f4daf7f433d3c521ae10e898e3 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 15 Aug 2023 23:39:01 +0100 Subject: [PATCH 08/14] - IED server: Added read/write access control to GoCBs/SVCBs with IedServer_ControlBlockAccessHandler (LIB61850-420) --- src/iec61850/inc_private/mms_sv.h | 2 +- src/iec61850/server/mms_mapping/logging.c | 1 - src/iec61850/server/mms_mapping/mms_mapping.c | 50 +++++++++++--- src/iec61850/server/mms_mapping/mms_sv.c | 69 ++++++++++++++++--- 4 files changed, 98 insertions(+), 24 deletions(-) diff --git a/src/iec61850/inc_private/mms_sv.h b/src/iec61850/inc_private/mms_sv.h index c150a641..6f8663bf 100644 --- a/src/iec61850/inc_private/mms_sv.h +++ b/src/iec61850/inc_private/mms_sv.h @@ -38,7 +38,7 @@ LIBIEC61850_SV_createSVControlBlocks(MmsMapping* self, MmsDomain* domain, LogicalNode* logicalNode, int svCount, bool unicast); LIB61850_INTERNAL MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig , MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 0e682f72..d6428ee1 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -395,7 +395,6 @@ updateLogStatusInLCB(LogControl* self) } } - static void freeDynamicDataSet(LogControl* self) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 9aaeea3b..ff17dac9 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -2345,7 +2345,7 @@ lookupGCB(MmsMapping* self, MmsDomain* domain, char* lnName, char* objectName) static MmsDataAccessError writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, - MmsValue* value) + MmsValue* value, MmsServerConnection connection) { char variableId[130]; @@ -2377,6 +2377,20 @@ writeAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variable if (mmsGCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + /* check if write access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + if (strcmp(varName, "GoEna") == 0) { if (MmsValue_getType(value) != MMS_BOOLEAN) return DATA_ACCESS_ERROR_TYPE_INCONSISTENT; @@ -2619,7 +2633,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, /* Goose control block - GO */ if (isGooseControlBlock(separator)) - return writeAccessGooseControlBlock(self, domain, variableId, value); + return writeAccessGooseControlBlock(self, domain, variableId, value, connection); #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ @@ -3063,7 +3077,7 @@ MmsMapping_installReadAccessHandler(MmsMapping* self, ReadAccessHandler handler, #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) static MmsValue* -readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -3087,13 +3101,28 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsGooseControlBlock mmsGCB = lookupGCB(self, domain, lnName, objectName); - if (mmsGCB != NULL) { - if (varName != NULL) { + if (mmsGCB) { + + /* check if read access to GoCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + LogicalNode* ln = MmsGooseControlBlock_getLogicalNode(mmsGCB); + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_GoCB, ld, ln, MmsGooseControlBlock_getName(mmsGCB), varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(MmsGooseControlBlock_getMmsValues(mmsGCB), MmsGooseControlBlock_getVariableSpecification(mmsGCB), varName); } @@ -3107,7 +3136,6 @@ readAccessGooseControlBlock(MmsMapping* self, MmsDomain* domain, char* variableI #endif /* (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) */ - static MmsValue* mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3137,7 +3165,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_INCLUDE_GOOSE_SUPPORT == 1) /* GOOSE control blocks - GO */ if (isGooseControlBlock(separator)) { - retValue = readAccessGooseControlBlock(self, domain, variableId); + retValue = readAccessGooseControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3145,7 +3173,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_SAMPLED_VALUES_SUPPORT == 1) /* Sampled Value control blocks - MS/US */ if (isSampledValueControlBlock(separator)) { - retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId); + retValue = LIBIEC61850_SV_readAccessSampledValueControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3683,9 +3711,9 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, "", IEC61850_CB_ACCESS_TYPE_READ) == false) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_SGCB, ld, ln, str, "", IEC61850_CB_ACCESS_TYPE_READ) == false) { return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - } + } } return DATA_ACCESS_ERROR_SUCCESS; diff --git a/src/iec61850/server/mms_mapping/mms_sv.c b/src/iec61850/server/mms_mapping/mms_sv.c index a03652f1..ef2ad775 100644 --- a/src/iec61850/server/mms_mapping/mms_sv.c +++ b/src/iec61850/server/mms_mapping/mms_sv.c @@ -1,7 +1,7 @@ /* * mms_sv.c * - * Copyright 2015-2022 Michael Zillgith + * Copyright 2015-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -32,6 +32,8 @@ #include "mms_sv.h" #include "mms_mapping_internal.h" +#include "ied_server_private.h" +#include "mms_value_internal.h" struct sMmsSampledValueControlBlock { SVControlBlock* svcb; @@ -50,11 +52,12 @@ struct sMmsSampledValueControlBlock { MmsValue* svEnaValue; MmsValue* resvValue; - SVCBEventHandler eventHandler; void* eventHandlerParameter; }; +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + MmsSampledValueControlBlock MmsSampledValueControlBlock_create() { @@ -63,7 +66,6 @@ MmsSampledValueControlBlock_create() return self; } - void MmsSampledValueControlBlock_destroy(MmsSampledValueControlBlock self) { @@ -105,7 +107,8 @@ MmsSampledValueControlBlock_enable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: enable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_ENABLE, self->eventHandlerParameter); } static void @@ -117,7 +120,8 @@ MmsSampledValueControlBlock_disable(MmsSampledValueControlBlock self) if (DEBUG_IED_SERVER) printf("IED_SERVER: disable SVCB %s\n", self->svcb->name); - self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); + if (self->eventHandler) + self->eventHandler(self->svcb, IEC61850_SVCB_EVENT_DISABLE, self->eventHandlerParameter); } static bool @@ -150,7 +154,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; else return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; @@ -160,7 +164,28 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch if (mmsSVCB == NULL) return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - if (mmsSVCB->reservedByClient != NULL) { + /* check if write access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + } + } + + if (mmsSVCB->reservedByClient) { if (mmsSVCB->reservedByClient != connection) return DATA_ACCESS_ERROR_TEMPORARILY_UNAVAILABLE; } @@ -209,7 +234,7 @@ LIBIEC61850_SV_writeAccessSVControlBlock(MmsMapping* self, MmsDomain* domain, ch } MmsValue* -LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -233,13 +258,35 @@ LIBIEC61850_SV_readAccessSampledValueControlBlock(MmsMapping* self, MmsDomain* d char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; MmsSampledValueControlBlock mmsSVCB = lookupSVCB(self, domain, lnName, objectName); - if (mmsSVCB != NULL) { - if (varName != NULL) { + if (mmsSVCB) { + + /* check if read access to SVCB is allowed on this connection */ + if (self->controlBlockAccessHandler) + { + ACSIClass acsiClass; + + if (mmsSVCB->svcb->isUnicast) + acsiClass = ACSI_CLASS_USVCB; + else + acsiClass = ACSI_CLASS_MSVCB; + + LogicalNode* ln = mmsSVCB->logicalNode; + + LogicalDevice* ld = (LogicalDevice*)ln->parent; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, mmsSVCB->svcb->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + return &objectAccessDenied; + } + } + + if (varName) { value = MmsValue_getSubElement(mmsSVCB->mmsValue, mmsSVCB->mmsType, varName); } else { From fd19abb0ab8382af66e2185c17069cba43f5d846 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 16 Aug 2023 15:51:20 +0100 Subject: [PATCH 09/14] - IED server: RBAC - replace specific access control handlers by generic handler - IedServer_ControlBlockAccessHandler (LIB61850-420) --- .../server_example_access_control.c | 49 +++++----- .../server_example_logging.c | 65 ++++++++++--- src/iec61850/inc/iec61850_server.h | 93 +++++-------------- .../inc_private/mms_mapping_internal.h | 9 -- src/iec61850/server/impl/ied_server.c | 21 ----- src/iec61850/server/mms_mapping/logging.c | 28 +----- src/iec61850/server/mms_mapping/mms_mapping.c | 28 ++++-- src/iec61850/server/mms_mapping/reporting.c | 23 +---- 8 files changed, 119 insertions(+), 197 deletions(-) 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 7dca5dc3..6b13ca7b 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -110,43 +110,41 @@ 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 */ static bool -rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation) +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) { - printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); + printf("%s %s access %s/%s.%s.%s\n", ACSIClassToStr(acsiClass), accessType == IEC61850_CB_ACCESS_TYPE_WRITE ? "write" : "read", ld->name, ln->name, objectName, subObjectName); - if (operation == RCB_EVENT_GET_PARAMETER) { - return true; + /* allow only read access to LCBs */ + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } - else { - /* change to false to disallow write access to control block */ - return true; + + /* allow only read access to BRCBs */ + if (acsiClass == ACSI_CLASS_BRCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } -} - -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; - } + /* to all other control blocks allow read and write access */ + return true; } static void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) { - printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); - if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); printf(" param: %s\n", parameterName); printf(" result: %i\n", serviceError); } if (event == RCB_EVENT_ENABLE) { + printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event); char* rptId = ReportControlBlock_getRptID(rcb); printf(" rptID: %s\n", rptId); char* dataSet = ReportControlBlock_getDataSet(rcb); @@ -286,15 +284,12 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); - /* Install handler to perform access control on RCB */ - IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL); - - /* Install handler to perform access control on LCB */ - IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); - /* Install handler to log RCB events */ IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); + /* Install handler to control access to control blocks (RCBs, LCBs, GoCBs, SVCBs, SGCBs)*/ + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); + /* By default access to variables with FC=DC and FC=CF is not allowed. * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used * by iec61850_client_example1. diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index ee388be8..e086d52a 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -115,26 +115,62 @@ 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) +static const char* +ACSIClassToStr(ACSIClass acsiClass) { - 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; + switch (acsiClass) + { + case ACSI_CLASS_BRCB: + return "BRCB"; + case ACSI_CLASS_URCB: + return "URCB"; + case ACSI_CLASS_GoCB: + return "GoCB"; + case ACSI_CLASS_SGCB: + return "SGCB"; + case ACSI_CLASS_LCB: + return "LCB"; + case ACSI_CLASS_GsCB: + return "GsCB"; + case ACSI_CLASS_LOG: + return "log"; + case ACSI_CLASS_DATA_SET: + return "dataset"; + case ACSI_CLASS_DATA_OBJECT: + return "data-object"; + case ACSI_CLASS_MSVCB: + return "MSVCB"; + case ACSI_CLASS_USVCB: + return "USVCB"; + default: + return "unknown"; } - else { - return false; +} + +bool +controlBlockAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType) +{ + printf("Access to %s %s/%s.%s\n", ACSIClassToStr(acsiClass), ld->name, ln ? ln->name : "-", objectName); + + if (acsiClass == ACSI_CLASS_LCB) { + if (accessType == IEC61850_CB_ACCESS_TYPE_READ) + return true; + else + return false; } + + return true; } static bool -logAccessHandler(void* parameter, const char* logName, ClientConnection connection) +listObjectsAccessHandler(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, FunctionalConstraint fc) { - printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection)); + if (subObjectName) + printf("list objects access[2] to %s/%s.%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, subObjectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); + else + printf("list objects access[2] to %s/%s.%s [acsi-class: %s(%i)] [FC=%s]\n", ld->name, ln ? ln->name : "-", objectName, ACSIClassToStr(acsiClass), acsiClass, FunctionalConstraint_toString(fc)); - return false; + return true; } int @@ -169,9 +205,7 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); - IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); - - IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL); + IedServer_setControlBlockAccessHandler(iedServer, controlBlockAccessHandler, NULL); LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); @@ -204,6 +238,7 @@ main(int argc, char** argv) LogStorage_getEntries(statusLog, 0, Hal_getTimeInMs(), entryCallback, (LogEntryDataCallback) entryDataCallback, NULL); #endif + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ IedServer_start(iedServer, tcpPort); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index ad88b369..b14bb6cb 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1892,77 +1892,6 @@ 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; - -/** - * \brief Callback that is called in case of LCB access to give the user the opportunity to block or allow the operation - * - * - * \param parameter user provided parameter - * \param lcb affected log control block - * \param connection client connection that is involved - * \param operation one of the following operation event types: LCB_EVENT_GET_PARAMETER, LCB_EVENT_SET_PARAMETER - */ -typedef bool -(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation); - -/** - * \brief Set a handler to control read and write access to log control blocks (LCBs) - * - * \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_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter); - -/** - * \brief Callback that is called when the client is trying to read log data - * - * \param parameter user provided parameter - * \param logRef object reference of the log - * \param connection client connection that is involved - * - * \return true to allow read log data, false to deny - */ -typedef bool -(*IedServer_LogAccessHandler) (void* parameter, const char* logRef, ClientConnection connection); - -/** - * \brief Set a handler control access to a log (read log data) - * - * \param handler the callback handler to be used - * \param parameter a user provided parameter that is passed to the handler. - */ -LIB61850_API void -IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter); - typedef enum { DATASET_CREATE, DATASET_DELETE, @@ -2040,9 +1969,31 @@ typedef enum { IEC61850_CB_ACCESS_TYPE_WRITE } IedServer_ControlBlockAccessType; +/** + * \brief Callback that is called when a client is invoking a read or write service to a control block or log + * + * This callback can be used to control the read and write access to control blocks and logs (SGCB, LCBs, URCBs, BRCBs, GoCBs, SVCBs, logs) + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * \param acsiClass the ACSI class of the object + * \param ld the logical device of the object + * \param ln the logical node of the object + * \param objectName the name of the object (e.g. data object name, data set name, log name, RCB name, ...) + * \param subObjectName the name of a sub element of an object or NULL + * \param accessType access type (read=IEC61850_CB_ACCESS_TYPE_READ or write=IEC61850_CB_ACCESS_TYPE_WRITE) + * + * \return true to include the object in the service response, otherwise false + */ typedef bool (*IedServer_ControlBlockAccessHandler)(void* parameter, ClientConnection connection, ACSIClass acsiClass, LogicalDevice* ld, LogicalNode* ln, const char* objectName, const char* subObjectName, IedServer_ControlBlockAccessType accessType); +/** + * \brief Set a handler to control read and write access to control blocks and logs + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ LIB61850_API void IedServer_setControlBlockAccessHandler(IedServer self, IedServer_ControlBlockAccessHandler handler, void* parameter); diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 4a27db2c..1830b87c 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -338,15 +338,6 @@ struct sMmsMapping { IedServer_RCBEventHandler rcbEventHandler; void* rcbEventHandlerParameter; - 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 0ad910eb..8a5c4baa 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -696,27 +696,6 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, self->mmsMapping->rcbEventHandlerParameter = parameter; } -void -IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter) -{ - self->mmsMapping->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 d6428ee1..7fcb78f1 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -523,18 +523,6 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma } else { - //TODO RBAC2 remove!? - 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 (self->controlBlockAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -544,7 +532,7 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); if (ln) { - if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } } @@ -773,18 +761,6 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, { bool allowAccess = true; - //TODO RBAC2 remove?! - if (self->lcbAccessHandler) - { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - - if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) { - allowAccess = false; - - value = &objectAccessDenied; - } - } - if (self->controlBlockAccessHandler) { ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); @@ -794,7 +770,7 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, LogicalNode* ln = LogicalDevice_getLogicalNode(ld, lnName); if (ln) { - if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LCB, ld, ln, logControl->logControlBlock->name, varName, IEC61850_CB_ACCESS_TYPE_READ) == false) { allowAccess = false; value = &objectAccessDenied; diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index ff17dac9..1d4d86c1 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3980,17 +3980,29 @@ mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, M MmsMapping* self = (MmsMapping*)parameter; - if (self->logAccessHandler) { - char logReference[130]; - logReference[0] = 0; + if (self->controlBlockAccessHandler) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - StringUtils_appendString(logReference, 130, MmsDomain_getName(domain)); - StringUtils_appendString(logReference, 130, "/"); - StringUtils_appendString(logReference, 130, logName); + LogicalDevice* ld = IedModel_getDevice(self->model, domain->domainName); - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + LogicalNode* ln = NULL; + + char str[65]; + + StringUtils_copyStringMax(str, 65, logName); + + char* name = str; + + char* separator = strchr(str, '$'); + + if (separator) { + name = separator + 1; + *separator = 0; + + ln = LogicalDevice_getLogicalNode(ld, str); + } - allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection); + allowAccess = self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, ACSI_CLASS_LOG, ld, ln, name, NULL, IEC61850_CB_ACCESS_TYPE_READ); } return allowAccess; diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 2ad84048..bc7edfe8 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1777,7 +1777,7 @@ ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerCon ClientConnection clientConnection = NULL; - if (mmsMapping->rcbAccessHandler || mmsMapping->rcbEventHandler) { + if (mmsMapping->controlBlockAccessHandler || mmsMapping->rcbEventHandler) { clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); } @@ -1794,15 +1794,7 @@ ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerCon LogicalDevice* ld = (LogicalDevice*)ln->parent; - if (mmsMapping->controlBlockAccessHandler(mmsMapping->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->name, elementName, IEC61850_CB_ACCESS_TYPE_READ) == false) { - accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - accessAllowed = false; - } - } - - //TODO RBAC2 remove? - if (mmsMapping->rcbAccessHandler) { - if (mmsMapping->rcbAccessHandler(mmsMapping->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER) == false) { + if (mmsMapping->controlBlockAccessHandler(mmsMapping->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->rcb->name, elementName, IEC61850_CB_ACCESS_TYPE_READ) == false) { accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; accessAllowed = false; } @@ -1910,15 +1902,6 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - //TODO RBAC2 remove? - if (self->rcbAccessHandler) { - if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) { - retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; - - goto exit_function_only_tracking; - } - } - /* check if write access to RCB is allowed on this connection */ if (self->controlBlockAccessHandler) { @@ -1933,7 +1916,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme LogicalDevice* ld = (LogicalDevice*)ln->parent; - if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->name, elementName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { + if (self->controlBlockAccessHandler(self->controlBlockAccessHandlerParameter, clientConnection, acsiClass, ld, ln, rc->rcb->name, elementName, IEC61850_CB_ACCESS_TYPE_WRITE) == false) { retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto exit_function_only_tracking; From 4cd4629a35af5469334780127706631ff6fec893 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 23 Aug 2023 17:55:08 +0100 Subject: [PATCH 10/14] - IED server: list objects access handler uses now '.' delimiter for the subObjectName (LIB61850-417) --- src/common/inc/string_utilities.h | 13 +++++++++++ src/common/string_utilities.c | 23 ++++++++++++++++++- src/iec61850/server/mms_mapping/mms_mapping.c | 4 ++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/common/inc/string_utilities.h b/src/common/inc/string_utilities.h index b6b238ff..404aec3e 100644 --- a/src/common/inc/string_utilities.h +++ b/src/common/inc/string_utilities.h @@ -40,6 +40,19 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1); LIB61850_INTERNAL char* StringUtils_copyStringToBuffer(const char* string, char* buffer); +/** + * \brief Copy string to buffer and replace characters + * + * NOTE: str should be a 0 terminated string. The terminating 0 is also copied. + * + * \param str the source string to copy + * \param buffer the destination buffer + * \param oldChar the character that has to be replaced while copying + * \param newChar the replacement character + */ +LIB61850_INTERNAL char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar); + LIB61850_INTERNAL char* StringUtils_copySubString(char* startPos, char* endPos); diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c index abf9a04c..2f659e6f 100644 --- a/src/common/string_utilities.c +++ b/src/common/string_utilities.c @@ -1,7 +1,7 @@ /* * string_utilities.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -65,6 +65,27 @@ StringUtils_copyStringToBuffer(const char* string, char* buffer) return buffer; } +char* +StringUtils_copyStringToBufferAndReplace(const char* str, char* buffer, char oldChar, char newChar) +{ + int i = 0; + + while (true) + { + if (str[i] == oldChar) + buffer[i] = newChar; + else + buffer[i] = str[i]; + + if (str[i] == 0) + break; + + i++; + } + + return buffer; +} + char* StringUtils_createStringFromBuffer(const uint8_t* buf, int size) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 1d4d86c1..27471a58 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3497,7 +3497,7 @@ mmsListObjectsAccessHandler(void* parameter, MmsGetNameListType listType, MmsDom StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); - subObjectName = StringUtils_copyStringToBuffer(doEnd + 2, subObjectBuf); + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); } } } @@ -3564,7 +3564,7 @@ mmsListObjectsAccessHandler(void* parameter, MmsGetNameListType listType, MmsDom StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); - subObjectName = StringUtils_copyStringToBuffer(doEnd + 2, subObjectBuf); + subObjectName = StringUtils_copyStringToBufferAndReplace(doEnd + 2, subObjectBuf, '$', '.'); } if (fc == IEC61850_FC_SP) { From 04bceea39828ad6cb310cbde587b4e4e344a8c3d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 1 Sep 2023 17:03:04 +0100 Subject: [PATCH 11/14] - SV receiver: fixed memory access problem during cleanup when using threads --- src/sampled_values/sv_subscriber.c | 33 +++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/sampled_values/sv_subscriber.c b/src/sampled_values/sv_subscriber.c index 99f5d34f..ba5d8d6e 100644 --- a/src/sampled_values/sv_subscriber.c +++ b/src/sampled_values/sv_subscriber.c @@ -57,6 +57,7 @@ struct sSVReceiver { #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore subscriberListLock; + Thread thread; #endif }; @@ -99,6 +100,7 @@ SVReceiver_create(void) #if (CONFIG_MMS_THREADLESS_STACK == 0) self->subscriberListLock = Semaphore_create(1); + self->thread = NULL; #endif } @@ -193,15 +195,19 @@ SVReceiver_start(SVReceiver self) if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV receiver started for interface %s\n", self->interfaceId); - Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + + self->thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, false); - if (thread) { - Thread_start(thread); + if (self->thread) { + Thread_start(self->thread); } else { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Failed to start thread\n"); } + +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ } else { if (DEBUG_SV_SUBSCRIBER) @@ -220,16 +226,24 @@ void SVReceiver_stop(SVReceiver self) { if (self->running) { - SVReceiver_stopThreadless(self); + self->running = false; - while (self->stopped == false) - Thread_sleep(1); +#if (CONFIG_MMS_THREADLESS_STACK == 0) + if (self->thread) { + Thread_destroy(self->thread); + self->thread = NULL; + } +#endif /* (CONFIG_MMS_THREADLESS_STACK == 0) */ + + SVReceiver_stopThreadless(self); } } void SVReceiver_destroy(SVReceiver self) { + SVReceiver_stop(self); + LinkedList_destroyDeep(self->subscriberList, (LinkedListValueDeleteFunction) SVSubscriber_destroy); @@ -237,7 +251,12 @@ SVReceiver_destroy(SVReceiver self) GLOBAL_FREEMEM(self->interfaceId); #if (CONFIG_MMS_THREADLESS_STACK == 0) - Semaphore_destroy(self->subscriberListLock); + if (self->thread) { + Thread_destroy(self->thread); + self->thread = NULL; + } + + Semaphore_destroy(self->subscriberListLock); #endif GLOBAL_FREEMEM(self->buffer); From e57dcc7214f54ca6bd61b10ee160d4574399a671 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 13 Oct 2023 15:19:28 +0100 Subject: [PATCH 12/14] - PAL: fixed wrong order of function arguments for fread and fwrite functions --- hal/filesystem/linux/file_provider_linux.c | 4 ++-- hal/filesystem/win32/file_provider_win32.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hal/filesystem/linux/file_provider_linux.c b/hal/filesystem/linux/file_provider_linux.c index 7b501f00..d37a7db1 100644 --- a/hal/filesystem/linux/file_provider_linux.c +++ b/hal/filesystem/linux/file_provider_linux.c @@ -53,13 +53,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void diff --git a/hal/filesystem/win32/file_provider_win32.c b/hal/filesystem/win32/file_provider_win32.c index 1a08e8d1..b51a1a45 100644 --- a/hal/filesystem/win32/file_provider_win32.c +++ b/hal/filesystem/win32/file_provider_win32.c @@ -62,13 +62,13 @@ FileSystem_openFile(char* fileName, bool readWrite) int FileSystem_readFile(FileHandle handle, uint8_t* buffer, int maxSize) { - return fread(buffer, maxSize, 1, (FILE*) handle); + return fread(buffer, 1, maxSize, (FILE*) handle); } int FileSystem_writeFile(FileHandle handle, uint8_t* buffer, int size) { - return fwrite(buffer, size, 1, (FILE*) handle); + return fwrite(buffer, 1, size, (FILE*) handle); } void From b695192e565901046bfd231fad4cda2a09291e96 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 4 Nov 2023 07:40:17 +0000 Subject: [PATCH 13/14] - IEC 61850/MMS Client: Handle MMS service error object-constraint-conflict and added IED_ERROR_OBJECT_CONSTRAINT_CONFLICT --- src/iec61850/client/ied_connection.c | 5 ++++- src/iec61850/common/iec61850_common.c | 1 - src/iec61850/inc/iec61850_client.h | 3 +++ src/mms/iso_mms/client/mms_client_connection.c | 12 +++++++++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index bc8e9ff6..d64fc595 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1,7 +1,7 @@ /* * ied_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -108,6 +108,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError) case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE: return IED_ERROR_TEMPORARILY_UNAVAILABLE; + case MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT: + return IED_ERROR_OBJECT_CONSTRAINT_CONFLICT; + default: return IED_ERROR_UNKNOWN; } diff --git a/src/iec61850/common/iec61850_common.c b/src/iec61850/common/iec61850_common.c index fa9a13c0..2ba1d3f7 100644 --- a/src/iec61850/common/iec61850_common.c +++ b/src/iec61850/common/iec61850_common.c @@ -768,7 +768,6 @@ MmsMapping_ObjectReferenceToVariableAccessSpec(char* objectReference) return accessSpec; } - static int getNumberOfDigits(int value) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 1326c1e7..5e53efe2 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -162,6 +162,9 @@ typedef enum { /** Received an invalid response message from the server */ IED_ERROR_MALFORMED_MESSAGE = 34, + /** Service was not executed because required resource is still in use */ + IED_ERROR_OBJECT_CONSTRAINT_CONFLICT = 35, + /** Service not implemented */ IED_ERROR_SERVICE_NOT_IMPLEMENTED = 98, diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index a9763c08..6f743b0b 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -436,7 +436,17 @@ convertServiceErrorToMmsError(MmsServiceError serviceError) break; case 4: /* class: service */ - mmsError = MMS_ERROR_SERVICE_OTHER; + + switch (serviceError.errorCode) + { + case 5: + mmsError = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + + default: + mmsError = MMS_ERROR_SERVICE_OTHER; + break; + } break; case 5: /* class: service-preempt */ From 9817d41d1a191f9d5232f2048e5e8a2e35507a0f Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 4 Nov 2023 07:43:30 +0000 Subject: [PATCH 14/14] - fixed - IEC 61580 server: dataset is not released when RCB.Datset is set to empty string by client (LIB61850-425) --- src/iec61850/server/mms_mapping/reporting.c | 24 ++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index bc7edfe8..493d5e48 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -724,9 +724,23 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, success = true; dataSetValue = NULL; - if (rc->buffered) { - rc->isBuffering = false; - purgeBuf(rc); + if (rc->dataSet) { + if (rc->buffered) { + rc->isBuffering = false; + purgeBuf(rc); + } + + /* delete pending events */ + deleteDataSetValuesShadowBuffer(rc); + + if (isUsedDataSetDynamic) { + if (rc->dataSet) { + MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); + } + } + + /* release used data set */ + rc->dataSet = NULL; } } else @@ -739,7 +753,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, /* check if old and new data sets are the same */ if (rc->dataSet && dataSetValue) { - const char* dataSetLdName = rc->dataSet->logicalDeviceName; const char* dataSetName = rc->dataSet->name; const char* newDataSetName = MmsValue_toString(dataSetValue); @@ -790,7 +803,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (dataSetValue) { + if (dataSetValue) + { const char* dataSetName = MmsValue_toString(dataSetValue); DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName);