Secure ReadProperty decoding and BACnetActionCommand (#702)

* Refactored and secured BACnetActionCommand codec into bacaction.c module for command object and added to bacapp module encode/decode with define for enabling and pseudo application tag for internal use.

* Simplified bacapp_data_len() and moved into bacdcode module as bacnet_enclosed_data_len() function.

* Secured ReadProperty-REQUEST and -ACK decoding.

* Removed deprecated Keylist_Key() functions from usage.

* Removed pseudo application datatypes from bacapp_data_decode() which only uses primitive application tag encoded values.

* Defined INT_MAX when it is not already defined by compiler or libc.

* Deprecated bacapp_decode_application_data_len() and bacapp_decode_context_data_len() as they are no longer used in any code in the library.

* Added BACnetScale to bacapp module. Improved complex property value decoding. Refactored bacapp_decode_known_property() function.

* Refactored and improved the bacapp_snprintf() function for printing EPICS.

* Fixed Lighting Output WriteProperty to handle known property decoding.
This commit is contained in:
Steve Karg
2024-07-25 17:12:08 -05:00
committed by GitHub
parent 923eaf2313
commit 4326128e72
191 changed files with 3856 additions and 2099 deletions
+139 -114
View File
@@ -120,73 +120,88 @@ int rp_encode_apdu(
/** Decode the service request only
*
* @param apdu Pointer to the buffer for encoding.
* @param apdu_len Count of valid bytes in the buffer.
* @param apdu_size Count of valid bytes in the buffer.
* @param rpdata Pointer to the property data to be encoded.
*
* @return Bytes decoded or zero on error.
* @return number of bytes decoded, or #BACNET_STATUS_REJECT
*/
int rp_decode_service_request(
uint8_t *apdu, unsigned apdu_len, BACNET_READ_PROPERTY_DATA *rpdata)
uint8_t *apdu, unsigned apdu_size, BACNET_READ_PROPERTY_DATA *data)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int len = 0;
int apdu_len = 0;
uint32_t instance = 0;
BACNET_OBJECT_TYPE type = OBJECT_NONE; /* for decoding */
uint32_t property = 0; /* for decoding */
BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */
/* check for value pointers */
if (rpdata) {
/* Must have at least 2 tags, an object id and a property identifier
* of at least 1 byte in length to have any chance of parsing */
if (apdu_len < 7) {
rpdata->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
return BACNET_STATUS_REJECT;
if (!apdu) {
if (data) {
data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
}
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[len++], 0)) {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len += decode_object_id(&apdu[len], &type, &rpdata->object_instance);
rpdata->object_type = type;
/* Tag 1: Property ID */
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
if (len < apdu_len) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if ((tag_number == 2) && (len < apdu_len)) {
len += decode_unsigned(
&apdu[len], len_value_type, &unsigned_value);
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rpdata->error_code = ERROR_CODE_REJECT_INVALID_TAG;
return BACNET_STATUS_REJECT;
return BACNET_STATUS_REJECT;
}
/* object-identifier [0] BACnetObjectIdentifier */
len = bacnet_object_id_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &type, &instance);
if (len > 0) {
if (instance > BACNET_MAX_INSTANCE) {
if (data) {
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
} else {
rpdata->array_index = BACNET_ARRAY_ALL;
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (data) {
data->object_type = type;
data->object_instance = instance;
}
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
/* property-identifier [1] BACnetPropertyIdentifier */
len = bacnet_enumerated_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &property);
if (len > 0) {
apdu_len += len;
if (data) {
data->object_property = (BACNET_PROPERTY_ID)property;
}
} else {
if (data) {
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
}
return BACNET_STATUS_REJECT;
}
/* property-array-index [2] Unsigned OPTIONAL */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value);
if (len > 0) {
apdu_len += len;
if (data) {
data->array_index = unsigned_value;
}
} else {
/* wrong tag - skip apdu_len increment and go to next field */
if (data) {
data->array_index = BACNET_ARRAY_ALL;
}
}
if (len < apdu_len) {
if (apdu_len < apdu_size) {
/* If something left over now, we have an invalid request */
if (rpdata) {
rpdata->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS;
if (data) {
data->error_code = ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS;
}
return BACNET_STATUS_REJECT;
}
return (int)len;
return apdu_len;
}
/**
* @brief Encode APDU for ReadProperty-ACK
*
@@ -345,7 +360,7 @@ int rp_ack_encode_apdu_object_property_end(uint8_t *apdu)
/** Encode the acknowledge.
*
* @param apdu Pointer to the buffer for encoding.
* @param apdu Pointer to the buffer for encoding, or NULL for length
* @param invoke_id Invoke Id
* @param rpdata Pointer to the property data to be encoded.
*
@@ -389,85 +404,95 @@ int rp_ack_encode_apdu(
* the application_data field points into the apdu buffer (is not allocated).
*
* @param apdu [in] The apdu portion of the ACK reply.
* @param apdu_len [in] The total length of the apdu.
* @param apdu_size [in] The total length of the apdu.
* @param rpdata [out] The structure holding the partially decoded result.
* @return Number of decoded bytes (could be less than apdu_len),
* or -1 on decoding error.
*/
int rp_ack_decode_service_request(uint8_t *apdu,
int apdu_len, /* total length of the apdu */
BACNET_READ_PROPERTY_DATA *rpdata)
int apdu_size,
BACNET_READ_PROPERTY_DATA *data)
{
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int tag_len = 0; /* length of tag decode */
int len = 0; /* total length of decodes */
BACNET_OBJECT_TYPE object_type = OBJECT_NONE; /* object type */
int apdu_len = 0; /* return value */
int len = 0;
uint32_t instance = 0;
BACNET_OBJECT_TYPE type = OBJECT_NONE; /* for decoding */
uint32_t property = 0; /* for decoding */
BACNET_UNSIGNED_INTEGER unsigned_value = 0; /* for decoding */
int data_len = 0;
/* Check basics. */
if (apdu && (apdu_len >= 8 /*minimum*/)) {
/* Tag 0: Object ID */
if (!decode_is_context_tag(&apdu[0], 0)) {
return -1;
if (!apdu) {
return -BACNET_STATUS_ERROR;
}
/* object-identifier [0] BACnetObjectIdentifier */
len = bacnet_object_id_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &type, &instance);
if (len > 0) {
if (instance > BACNET_MAX_INSTANCE) {
return BACNET_STATUS_ERROR;
}
len = 1;
len += decode_object_id(
&apdu[len], &object_type, &rpdata->object_instance);
rpdata->object_type = object_type;
/* Tag 1: Property ID */
if (len >= apdu_len) {
return -1;
}
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number != 1) {
return -1;
}
if (len >= apdu_len) {
return -1;
}
len += decode_enumerated(&apdu[len], len_value_type, &property);
rpdata->object_property = (BACNET_PROPERTY_ID)property;
/* Tag 2: Optional Array Index */
if (len >= apdu_len) {
return -1;
}
tag_len = decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value_type);
if (tag_number == 2) {
len += tag_len;
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
rpdata->array_index = (BACNET_ARRAY_INDEX)unsigned_value;
} else {
rpdata->array_index = BACNET_ARRAY_ALL;
}
/* Tag 3: opening context tag */
if (len >= apdu_len) {
return -1;
}
if (decode_is_opening_tag_number(&apdu[len], 3)) {
/* a tag number of 3 is not extended so only one octet */
len++;
/* don't decode the application tag number or its data here */
rpdata->application_data = &apdu[len];
/* Just to ensure we do not create a wrapped over value here. */
if (len < apdu_len) {
rpdata->application_data_len =
apdu_len - len - 1 /*closing tag */;
} else {
rpdata->application_data_len = 0;
}
/* len includes the data and the closing tag */
len = apdu_len;
} else {
return -1;
apdu_len += len;
if (data) {
data->object_type = type;
data->object_instance = instance;
}
} else {
return -1;
return BACNET_STATUS_ERROR;
}
/* property-identifier [1] BACnetPropertyIdentifier */
len = bacnet_enumerated_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 1, &property);
if (len > 0) {
apdu_len += len;
if (data) {
data->object_property = (BACNET_PROPERTY_ID)property;
}
} else {
return BACNET_STATUS_ERROR;
}
/* property-array-index [2] Unsigned OPTIONAL */
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 2, &unsigned_value);
if (len > 0) {
apdu_len += len;
if (data) {
data->array_index = unsigned_value;
}
} else {
/* wrong tag - skip apdu_len increment and go to next field */
if (data) {
data->array_index = BACNET_ARRAY_ALL;
}
}
/* property-value [3] ABSTRACT-SYNTAX.&Type */
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 3, &len)) {
return BACNET_STATUS_ERROR;
}
/* determine the length of the data blob */
data_len = bacnet_enclosed_data_length(&apdu[apdu_len],
apdu_size - apdu_len);
if (data_len == BACNET_STATUS_ERROR) {
return BACNET_STATUS_ERROR;
}
/* count the opening tag number length */
apdu_len += len;
if (data_len > MAX_APDU) {
/* not enough size in application_data to store the data chunk */
return BACNET_STATUS_ERROR;
} else if (data) {
/* don't decode the application tag number or its data here */
data->application_data = &apdu[apdu_len];
data->application_data_len = data_len;
}
apdu_len += data_len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, 3, &len)) {
return BACNET_STATUS_ERROR;
}
/* count the closing tag number length */
apdu_len += len;
return len;
return apdu_len;
}
#endif