Ignore ReinitializeDevice and DeviceCommunicationControl service password (#518)

* Fix device object ReinitializeDevice service handling examples of no-password in the device. Add unit testing of device object ReinitializeDevice service.  Add API for setting ReinitializeDevice device object password.

* Fix DeviceCommunicationControl service handling example of no-password in the device.

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-10-18 21:32:44 -05:00
committed by GitHub
parent e143066b29
commit 0b5474d36e
12 changed files with 576 additions and 173 deletions
+65 -21
View File
@@ -67,12 +67,11 @@ static struct my_object_functions {
read_property_function Object_Read_Property;
write_property_function Object_Write_Property;
rpm_property_lists_function Object_RPM_List;
} Object_Table[] = {
{ OBJECT_DEVICE, NULL, /* don't init - recursive! */
Device_Count, Device_Index_To_Instance,
Device_Valid_Object_Instance_Number,
Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists },
} Object_Table[] = { { OBJECT_DEVICE, NULL, /* don't init - recursive! */
Device_Count, Device_Index_To_Instance,
Device_Valid_Object_Instance_Number,
Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists },
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
Analog_Input_Object_Name, Analog_Input_Read_Property, NULL,
@@ -159,8 +158,7 @@ static int Read_Property_Common(
}
apdu = rpdata->application_data;
if (property_list_common(rpdata->object_property)) {
apdu_len = property_list_common_encode(rpdata,
Object_Instance_Number);
apdu_len = property_list_common_encode(rpdata, Object_Instance_Number);
} else if (rpdata->object_property == PROP_OBJECT_NAME) {
/* only array properties can have array options */
if (rpdata->array_index != BACNET_ARRAY_ALL) {
@@ -180,9 +178,8 @@ static int Read_Property_Common(
} else if (rpdata->object_property == PROP_PROPERTY_LIST) {
Device_Objects_Property_List(
rpdata->object_type, rpdata->object_instance, &property_list);
apdu_len = property_list_encode(rpdata,
property_list.Required.pList, property_list.Optional.pList,
property_list.Proprietary.pList);
apdu_len = property_list_encode(rpdata, property_list.Required.pList,
property_list.Optional.pList, property_list.Proprietary.pList);
#endif
} else if (pObject->Object_Read_Property) {
apdu_len = pObject->Object_Read_Property(rpdata);
@@ -332,12 +329,64 @@ bool Device_Set_Object_Name(BACNET_CHARACTER_STRING *object_name)
return status;
}
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -367,9 +416,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;
@@ -705,9 +751,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
case PROP_OBJECT_LIST:
count = Device_Object_List_Count();
apdu_len = bacnet_array_encode(rpdata->object_instance,
rpdata->array_index,
Device_Object_List_Element_Encode,
count, apdu, apdu_max);
rpdata->array_index, Device_Object_List_Element_Encode, count,
apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
@@ -745,8 +790,7 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
encode_application_unsigned(&apdu[0], dlmstp_max_master());
break;
case 9600:
apdu_len =
encode_application_unsigned(&apdu[0], rs485_baud_rate());
apdu_len = encode_application_unsigned(&apdu[0], rs485_baud_rate());
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
+62 -16
View File
@@ -67,12 +67,11 @@ static struct my_object_functions {
read_property_function Object_Read_Property;
write_property_function Object_Write_Property;
rpm_property_lists_function Object_RPM_List;
} Object_Table[] = {
{ OBJECT_DEVICE, NULL, /* don't init - recursive! */
Device_Count, Device_Index_To_Instance,
Device_Valid_Object_Instance_Number,
Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists },
} Object_Table[] = { { OBJECT_DEVICE, NULL, /* don't init - recursive! */
Device_Count, Device_Index_To_Instance,
Device_Valid_Object_Instance_Number,
Device_Object_Name, Device_Read_Property_Local,
Device_Write_Property_Local, Device_Property_Lists },
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
Analog_Input_Object_Name, Analog_Input_Read_Property, NULL,
@@ -161,8 +160,7 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
#if (BACNET_PROTOCOL_REVISION >= 14)
if ((int)rpdata->object_property == PROP_PROPERTY_LIST) {
Device_Objects_Property_List(rpdata->object_type,
rpdata->object_instance,
&property_list);
rpdata->object_instance, &property_list);
apdu_len = property_list_encode(rpdata,
property_list.Required.pList,
property_list.Optional.pList,
@@ -306,12 +304,64 @@ bool Device_Object_Name(
return status;
}
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -341,9 +391,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;
@@ -695,9 +742,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
case PROP_OBJECT_LIST:
count = Device_Object_List_Count();
apdu_len = bacnet_array_encode(rpdata->object_instance,
rpdata->array_index,
Device_Object_List_Element_Encode,
count, apdu, apdu_max);
rpdata->array_index, Device_Object_List_Element_Encode, count,
apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
+54 -6
View File
@@ -52,14 +52,65 @@ static uint32_t Object_Instance_Number = 12345;
static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
static uint8_t Database_Revision;
BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE;
static char Reinit_Password[16] = "filister";
static const char *Reinit_Password = "filister";
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/** Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -89,9 +140,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;
+54 -5
View File
@@ -271,12 +271,64 @@ uint32_t Device_Index_To_Instance(unsigned index)
return Object_Instance_Number;
}
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -306,9 +358,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;
+54 -5
View File
@@ -342,12 +342,64 @@ bool Device_Set_Object_Name(BACNET_CHARACTER_STRING *object_name)
return status;
}
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -379,9 +431,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;
+54 -5
View File
@@ -336,12 +336,64 @@ bool Device_Set_Object_Name(BACNET_CHARACTER_STRING *object_name)
return status;
}
/**
* @brief Sets the ReinitializeDevice password
*
* The password shall be a null terminated C string of up to
* 20 ASCII characters for those devices that require the password.
*
* For those devices that do not require a password, set to NULL or
* point to a zero length C string (null terminated).
*
* @param the ReinitializeDevice password; can be NULL or empty string
*/
bool Device_Reinitialize_Password_Set(const char *password)
{
Reinit_Password = password;
return true;
}
/**
* @brief Commands a Device re-initialization, to a given state.
* The request's password must match for the operation to succeed.
* This implementation provides a framework, but doesn't
* actually *DO* anything.
* @note You could use a mix of states and passwords to multiple outcomes.
* @note You probably want to restart *after* the simple ack has been sent
* from the return handler, so just set a local flag here.
* @ingroup ObjIntf
*
* @param rd_data [in,out] The information from the RD request.
* On failure, the error class and code will be set.
* @return True if succeeds (password is correct), else False.
*/
bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
bool password_success = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (Reinit_Password && strlen(Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(
&rd_data->password, Reinit_Password)) {
password_success = true;
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
} else {
password_success = true;
}
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -373,9 +425,6 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
break;
}
} else {
rd_data->error_class = ERROR_CLASS_SECURITY;
rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
}
return status;