Bugfix/secure apdu handler unconfirmed (#645)
* Secured APDU handler by avoiding read ahead.
This commit is contained in:
+164
-153
@@ -554,10 +554,12 @@ static bool apdu_unconfirmed_dcc_disabled(uint8_t service_choice)
|
|||||||
* @param apdu [in] The apdu portion of the request, to be processed.
|
* @param apdu [in] The apdu portion of the request, to be processed.
|
||||||
* @param apdu_len [in] The total (remaining) length of the apdu.
|
* @param apdu_len [in] The total (remaining) length of the apdu.
|
||||||
*/
|
*/
|
||||||
void apdu_handler(BACNET_ADDRESS *src,
|
void apdu_handler(
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
uint8_t *apdu, /* APDU data */
|
uint8_t *apdu, /* APDU data */
|
||||||
uint16_t apdu_len)
|
uint16_t apdu_len)
|
||||||
{
|
{
|
||||||
|
BACNET_PDU_TYPE pdu_type;
|
||||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
uint8_t service_choice = 0;
|
uint8_t service_choice = 0;
|
||||||
uint8_t *service_request = NULL;
|
uint8_t *service_request = NULL;
|
||||||
@@ -572,157 +574,166 @@ void apdu_handler(BACNET_ADDRESS *src,
|
|||||||
bool server = false;
|
bool server = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (apdu) {
|
if (!apdu) {
|
||||||
/* PDU Type */
|
return;
|
||||||
switch (apdu[0] & 0xF0) {
|
}
|
||||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
if (apdu_len == 0) {
|
||||||
len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len,
|
return;
|
||||||
&service_data, &service_choice, &service_request,
|
}
|
||||||
&service_request_len);
|
pdu_type = apdu[0] & 0xF0;
|
||||||
if (len == 0) {
|
switch (pdu_type) {
|
||||||
/* service data unable to be decoded - simply drop */
|
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||||
break;
|
len = apdu_decode_confirmed_service_request(
|
||||||
}
|
apdu, apdu_len, &service_data, &service_choice,
|
||||||
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
&service_request, &service_request_len);
|
||||||
/* When network communications are completely disabled,
|
if (len == 0) {
|
||||||
only DeviceCommunicationControl and ReinitializeDevice
|
/* service data unable to be decoded - simply drop */
|
||||||
APDUs shall be processed and no messages shall be
|
break;
|
||||||
initiated. */
|
}
|
||||||
break;
|
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
||||||
}
|
/* When network communications are completely disabled,
|
||||||
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
only DeviceCommunicationControl and ReinitializeDevice
|
||||||
(Confirmed_Function[service_choice])) {
|
APDUs shall be processed and no messages shall be
|
||||||
Confirmed_Function[service_choice](service_request,
|
initiated. */
|
||||||
service_request_len, src, &service_data);
|
break;
|
||||||
} else if (Unrecognized_Service_Handler) {
|
}
|
||||||
Unrecognized_Service_Handler(service_request,
|
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
||||||
service_request_len, src, &service_data);
|
(Confirmed_Function[service_choice])) {
|
||||||
}
|
Confirmed_Function[service_choice](
|
||||||
break;
|
service_request, service_request_len, src, &service_data);
|
||||||
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
} else if (Unrecognized_Service_Handler) {
|
||||||
if (apdu_len < 2) {
|
Unrecognized_Service_Handler(
|
||||||
break;
|
service_request, service_request_len, src, &service_data);
|
||||||
}
|
}
|
||||||
service_choice = apdu[1];
|
break;
|
||||||
service_request = &apdu[2];
|
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
||||||
service_request_len = apdu_len - 2;
|
if (apdu_len < 2) {
|
||||||
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
|
break;
|
||||||
/* When network communications are disabled,
|
}
|
||||||
only DeviceCommunicationControl and
|
service_choice = apdu[1];
|
||||||
ReinitializeDevice APDUs shall be processed and no
|
/* prepare the service request buffer and length */
|
||||||
messages shall be initiated. If communications have
|
service_request_len = apdu_len - 2;
|
||||||
been initiation disabled, then WhoIs may be
|
service_request = &apdu[2];
|
||||||
processed. */
|
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
|
||||||
break;
|
/* When network communications are disabled,
|
||||||
}
|
only DeviceCommunicationControl and
|
||||||
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
|
ReinitializeDevice APDUs shall be processed and no
|
||||||
if (Unconfirmed_Function[service_choice]) {
|
messages shall be initiated. If communications have
|
||||||
Unconfirmed_Function[service_choice](
|
been initiation disabled, then WhoIs may be
|
||||||
service_request, service_request_len, src);
|
processed. */
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
break;
|
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
|
||||||
#if !BACNET_SVC_SERVER
|
if (Unconfirmed_Function[service_choice]) {
|
||||||
case PDU_TYPE_SIMPLE_ACK:
|
Unconfirmed_Function[service_choice](
|
||||||
if (apdu_len < 3) {
|
service_request, service_request_len, src);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
invoke_id = apdu[1];
|
break;
|
||||||
service_choice = apdu[2];
|
#if !BACNET_SVC_SERVER
|
||||||
if (apdu_confirmed_simple_ack_service(service_choice)) {
|
case PDU_TYPE_SIMPLE_ACK:
|
||||||
if (Confirmed_ACK_Function[service_choice].simple !=
|
if (apdu_len < 3) {
|
||||||
NULL) {
|
break;
|
||||||
Confirmed_ACK_Function[service_choice].simple(
|
}
|
||||||
src, invoke_id);
|
invoke_id = apdu[1];
|
||||||
}
|
service_choice = apdu[2];
|
||||||
tsm_free_invoke_id(invoke_id);
|
if (apdu_confirmed_simple_ack_service(service_choice)) {
|
||||||
}
|
if (Confirmed_ACK_Function[service_choice].simple != NULL) {
|
||||||
break;
|
Confirmed_ACK_Function[service_choice].simple(
|
||||||
case PDU_TYPE_COMPLEX_ACK:
|
src, invoke_id);
|
||||||
if (apdu_len < 3) {
|
}
|
||||||
break;
|
tsm_free_invoke_id(invoke_id);
|
||||||
}
|
}
|
||||||
service_ack_data.segmented_message =
|
break;
|
||||||
(apdu[0] & BIT(3)) ? true : false;
|
case PDU_TYPE_COMPLEX_ACK:
|
||||||
service_ack_data.more_follows =
|
if (apdu_len < 3) {
|
||||||
(apdu[0] & BIT(2)) ? true : false;
|
break;
|
||||||
invoke_id = service_ack_data.invoke_id = apdu[1];
|
}
|
||||||
len = 2;
|
service_ack_data.segmented_message =
|
||||||
if (service_ack_data.segmented_message) {
|
(apdu[0] & BIT(3)) ? true : false;
|
||||||
service_ack_data.sequence_number = apdu[len++];
|
service_ack_data.more_follows = (apdu[0] & BIT(2)) ? true : false;
|
||||||
service_ack_data.proposed_window_number = apdu[len++];
|
invoke_id = service_ack_data.invoke_id = apdu[1];
|
||||||
}
|
len = 2;
|
||||||
service_choice = apdu[len++];
|
if (service_ack_data.segmented_message) {
|
||||||
service_request = &apdu[len];
|
if (apdu_len < 5) {
|
||||||
service_request_len = apdu_len - (uint16_t)len;
|
break;
|
||||||
if (!apdu_confirmed_simple_ack_service(service_choice)) {
|
}
|
||||||
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
service_ack_data.sequence_number = apdu[len++];
|
||||||
if (Confirmed_ACK_Function[service_choice]
|
service_ack_data.proposed_window_number = apdu[len++];
|
||||||
.complex != NULL) {
|
}
|
||||||
Confirmed_ACK_Function[service_choice].complex(
|
service_choice = apdu[len++];
|
||||||
service_request, service_request_len, src,
|
/* prepare the service request buffer and length */
|
||||||
&service_ack_data);
|
service_request_len = apdu_len - (uint16_t)len;
|
||||||
}
|
service_request = &apdu[len];
|
||||||
}
|
if (!apdu_confirmed_simple_ack_service(service_choice)) {
|
||||||
tsm_free_invoke_id(invoke_id);
|
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||||
}
|
if (Confirmed_ACK_Function[service_choice].complex !=
|
||||||
break;
|
NULL) {
|
||||||
case PDU_TYPE_SEGMENT_ACK:
|
Confirmed_ACK_Function[service_choice].complex(
|
||||||
/* FIXME: what about a denial of service attack here?
|
service_request, service_request_len, src,
|
||||||
we could check src to see if that matched the tsm */
|
&service_ack_data);
|
||||||
tsm_free_invoke_id(invoke_id);
|
}
|
||||||
break;
|
}
|
||||||
case PDU_TYPE_ERROR:
|
tsm_free_invoke_id(invoke_id);
|
||||||
if (apdu_len < 3) {
|
}
|
||||||
break;
|
break;
|
||||||
}
|
case PDU_TYPE_SEGMENT_ACK:
|
||||||
invoke_id = apdu[1];
|
/* FIXME: what about a denial of service attack here?
|
||||||
service_choice = apdu[2];
|
we could check src to see if that matched the tsm */
|
||||||
if (apdu_complex_error(service_choice)) {
|
tsm_free_invoke_id(invoke_id);
|
||||||
if (Error_Function[service_choice].complex) {
|
break;
|
||||||
Error_Function[service_choice].complex(src,
|
case PDU_TYPE_ERROR:
|
||||||
invoke_id, service_choice, &apdu[3],
|
if (apdu_len < 3) {
|
||||||
apdu_len - 3);
|
break;
|
||||||
}
|
}
|
||||||
} else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
invoke_id = apdu[1];
|
||||||
len = bacerror_decode_error_class_and_code(
|
service_choice = apdu[2];
|
||||||
&apdu[3], apdu_len - 3, &error_class, &error_code);
|
/* prepare the service request buffer and length */
|
||||||
if ((len != 0) &&
|
service_request_len = apdu_len - 3;
|
||||||
(Error_Function[service_choice].error)) {
|
service_request = &apdu[3];
|
||||||
Error_Function[service_choice].error(src, invoke_id,
|
if (apdu_complex_error(service_choice)) {
|
||||||
(BACNET_ERROR_CLASS)error_class,
|
if (Error_Function[service_choice].complex) {
|
||||||
(BACNET_ERROR_CODE)error_code);
|
Error_Function[service_choice].complex(
|
||||||
}
|
src, invoke_id, service_choice, service_request,
|
||||||
}
|
service_request_len);
|
||||||
tsm_free_invoke_id(invoke_id);
|
}
|
||||||
break;
|
} else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||||
case PDU_TYPE_REJECT:
|
len = bacerror_decode_error_class_and_code(
|
||||||
if (apdu_len < 3) {
|
service_request, service_request_len, &error_class,
|
||||||
break;
|
&error_code);
|
||||||
}
|
if ((len != 0) && (Error_Function[service_choice].error)) {
|
||||||
invoke_id = apdu[1];
|
Error_Function[service_choice].error(
|
||||||
reason = apdu[2];
|
src, invoke_id, (BACNET_ERROR_CLASS)error_class,
|
||||||
if (Reject_Function) {
|
(BACNET_ERROR_CODE)error_code);
|
||||||
Reject_Function(src, invoke_id, reason);
|
}
|
||||||
}
|
}
|
||||||
tsm_free_invoke_id(invoke_id);
|
tsm_free_invoke_id(invoke_id);
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_ABORT:
|
case PDU_TYPE_REJECT:
|
||||||
if (apdu_len < 3) {
|
if (apdu_len < 3) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
server = apdu[0] & 0x01;
|
invoke_id = apdu[1];
|
||||||
invoke_id = apdu[1];
|
reason = apdu[2];
|
||||||
reason = apdu[2];
|
if (Reject_Function) {
|
||||||
if (Abort_Function) {
|
Reject_Function(src, invoke_id, reason);
|
||||||
Abort_Function(src, invoke_id, reason, server);
|
}
|
||||||
}
|
tsm_free_invoke_id(invoke_id);
|
||||||
tsm_free_invoke_id(invoke_id);
|
break;
|
||||||
break;
|
case PDU_TYPE_ABORT:
|
||||||
#endif
|
if (apdu_len < 3) {
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
}
|
server = apdu[0] & 0x01;
|
||||||
|
invoke_id = apdu[1];
|
||||||
|
reason = apdu[2];
|
||||||
|
if (Abort_Function) {
|
||||||
|
Abort_Function(src, invoke_id, reason, server);
|
||||||
|
}
|
||||||
|
tsm_free_invoke_id(invoke_id);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user