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_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 */
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
BACNET_PDU_TYPE pdu_type;
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
uint8_t service_choice = 0;
|
||||
uint8_t *service_request = NULL;
|
||||
@@ -572,157 +574,166 @@ void apdu_handler(BACNET_ADDRESS *src,
|
||||
bool server = false;
|
||||
#endif
|
||||
|
||||
if (apdu) {
|
||||
/* PDU Type */
|
||||
switch (apdu[0] & 0xF0) {
|
||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||
len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len,
|
||||
&service_data, &service_choice, &service_request,
|
||||
&service_request_len);
|
||||
if (len == 0) {
|
||||
/* service data unable to be decoded - simply drop */
|
||||
break;
|
||||
}
|
||||
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
||||
/* When network communications are completely disabled,
|
||||
only DeviceCommunicationControl and ReinitializeDevice
|
||||
APDUs shall be processed and no messages shall be
|
||||
initiated. */
|
||||
break;
|
||||
}
|
||||
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
||||
(Confirmed_Function[service_choice])) {
|
||||
Confirmed_Function[service_choice](service_request,
|
||||
service_request_len, src, &service_data);
|
||||
} else if (Unrecognized_Service_Handler) {
|
||||
Unrecognized_Service_Handler(service_request,
|
||||
service_request_len, src, &service_data);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
||||
if (apdu_len < 2) {
|
||||
break;
|
||||
}
|
||||
service_choice = apdu[1];
|
||||
service_request = &apdu[2];
|
||||
service_request_len = apdu_len - 2;
|
||||
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
|
||||
/* When network communications are disabled,
|
||||
only DeviceCommunicationControl and
|
||||
ReinitializeDevice APDUs shall be processed and no
|
||||
messages shall be initiated. If communications have
|
||||
been initiation disabled, then WhoIs may be
|
||||
processed. */
|
||||
break;
|
||||
}
|
||||
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
|
||||
if (Unconfirmed_Function[service_choice]) {
|
||||
Unconfirmed_Function[service_choice](
|
||||
service_request, service_request_len, src);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if !BACNET_SVC_SERVER
|
||||
case PDU_TYPE_SIMPLE_ACK:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
service_choice = apdu[2];
|
||||
if (apdu_confirmed_simple_ack_service(service_choice)) {
|
||||
if (Confirmed_ACK_Function[service_choice].simple !=
|
||||
NULL) {
|
||||
Confirmed_ACK_Function[service_choice].simple(
|
||||
src, invoke_id);
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_COMPLEX_ACK:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
service_ack_data.segmented_message =
|
||||
(apdu[0] & BIT(3)) ? true : false;
|
||||
service_ack_data.more_follows =
|
||||
(apdu[0] & BIT(2)) ? true : false;
|
||||
invoke_id = service_ack_data.invoke_id = apdu[1];
|
||||
len = 2;
|
||||
if (service_ack_data.segmented_message) {
|
||||
service_ack_data.sequence_number = apdu[len++];
|
||||
service_ack_data.proposed_window_number = apdu[len++];
|
||||
}
|
||||
service_choice = apdu[len++];
|
||||
service_request = &apdu[len];
|
||||
service_request_len = apdu_len - (uint16_t)len;
|
||||
if (!apdu_confirmed_simple_ack_service(service_choice)) {
|
||||
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||
if (Confirmed_ACK_Function[service_choice]
|
||||
.complex != NULL) {
|
||||
Confirmed_ACK_Function[service_choice].complex(
|
||||
service_request, service_request_len, src,
|
||||
&service_ack_data);
|
||||
}
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_SEGMENT_ACK:
|
||||
/* FIXME: what about a denial of service attack here?
|
||||
we could check src to see if that matched the tsm */
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_ERROR:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
service_choice = apdu[2];
|
||||
if (apdu_complex_error(service_choice)) {
|
||||
if (Error_Function[service_choice].complex) {
|
||||
Error_Function[service_choice].complex(src,
|
||||
invoke_id, service_choice, &apdu[3],
|
||||
apdu_len - 3);
|
||||
}
|
||||
} else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||
len = bacerror_decode_error_class_and_code(
|
||||
&apdu[3], apdu_len - 3, &error_class, &error_code);
|
||||
if ((len != 0) &&
|
||||
(Error_Function[service_choice].error)) {
|
||||
Error_Function[service_choice].error(src, invoke_id,
|
||||
(BACNET_ERROR_CLASS)error_class,
|
||||
(BACNET_ERROR_CODE)error_code);
|
||||
}
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_REJECT:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
reason = apdu[2];
|
||||
if (Reject_Function) {
|
||||
Reject_Function(src, invoke_id, reason);
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_ABORT:
|
||||
if (apdu_len < 3) {
|
||||
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;
|
||||
}
|
||||
if (!apdu) {
|
||||
return;
|
||||
}
|
||||
if (apdu_len == 0) {
|
||||
return;
|
||||
}
|
||||
pdu_type = apdu[0] & 0xF0;
|
||||
switch (pdu_type) {
|
||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||
len = apdu_decode_confirmed_service_request(
|
||||
apdu, apdu_len, &service_data, &service_choice,
|
||||
&service_request, &service_request_len);
|
||||
if (len == 0) {
|
||||
/* service data unable to be decoded - simply drop */
|
||||
break;
|
||||
}
|
||||
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
||||
/* When network communications are completely disabled,
|
||||
only DeviceCommunicationControl and ReinitializeDevice
|
||||
APDUs shall be processed and no messages shall be
|
||||
initiated. */
|
||||
break;
|
||||
}
|
||||
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
||||
(Confirmed_Function[service_choice])) {
|
||||
Confirmed_Function[service_choice](
|
||||
service_request, service_request_len, src, &service_data);
|
||||
} else if (Unrecognized_Service_Handler) {
|
||||
Unrecognized_Service_Handler(
|
||||
service_request, service_request_len, src, &service_data);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
||||
if (apdu_len < 2) {
|
||||
break;
|
||||
}
|
||||
service_choice = apdu[1];
|
||||
/* prepare the service request buffer and length */
|
||||
service_request_len = apdu_len - 2;
|
||||
service_request = &apdu[2];
|
||||
if (apdu_unconfirmed_dcc_disabled(service_choice)) {
|
||||
/* When network communications are disabled,
|
||||
only DeviceCommunicationControl and
|
||||
ReinitializeDevice APDUs shall be processed and no
|
||||
messages shall be initiated. If communications have
|
||||
been initiation disabled, then WhoIs may be
|
||||
processed. */
|
||||
break;
|
||||
}
|
||||
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
|
||||
if (Unconfirmed_Function[service_choice]) {
|
||||
Unconfirmed_Function[service_choice](
|
||||
service_request, service_request_len, src);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#if !BACNET_SVC_SERVER
|
||||
case PDU_TYPE_SIMPLE_ACK:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
service_choice = apdu[2];
|
||||
if (apdu_confirmed_simple_ack_service(service_choice)) {
|
||||
if (Confirmed_ACK_Function[service_choice].simple != NULL) {
|
||||
Confirmed_ACK_Function[service_choice].simple(
|
||||
src, invoke_id);
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_COMPLEX_ACK:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
service_ack_data.segmented_message =
|
||||
(apdu[0] & BIT(3)) ? true : false;
|
||||
service_ack_data.more_follows = (apdu[0] & BIT(2)) ? true : false;
|
||||
invoke_id = service_ack_data.invoke_id = apdu[1];
|
||||
len = 2;
|
||||
if (service_ack_data.segmented_message) {
|
||||
if (apdu_len < 5) {
|
||||
break;
|
||||
}
|
||||
service_ack_data.sequence_number = apdu[len++];
|
||||
service_ack_data.proposed_window_number = apdu[len++];
|
||||
}
|
||||
service_choice = apdu[len++];
|
||||
/* prepare the service request buffer and length */
|
||||
service_request_len = apdu_len - (uint16_t)len;
|
||||
service_request = &apdu[len];
|
||||
if (!apdu_confirmed_simple_ack_service(service_choice)) {
|
||||
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||
if (Confirmed_ACK_Function[service_choice].complex !=
|
||||
NULL) {
|
||||
Confirmed_ACK_Function[service_choice].complex(
|
||||
service_request, service_request_len, src,
|
||||
&service_ack_data);
|
||||
}
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
}
|
||||
break;
|
||||
case PDU_TYPE_SEGMENT_ACK:
|
||||
/* FIXME: what about a denial of service attack here?
|
||||
we could check src to see if that matched the tsm */
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_ERROR:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
service_choice = apdu[2];
|
||||
/* prepare the service request buffer and length */
|
||||
service_request_len = apdu_len - 3;
|
||||
service_request = &apdu[3];
|
||||
if (apdu_complex_error(service_choice)) {
|
||||
if (Error_Function[service_choice].complex) {
|
||||
Error_Function[service_choice].complex(
|
||||
src, invoke_id, service_choice, service_request,
|
||||
service_request_len);
|
||||
}
|
||||
} else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
|
||||
len = bacerror_decode_error_class_and_code(
|
||||
service_request, service_request_len, &error_class,
|
||||
&error_code);
|
||||
if ((len != 0) && (Error_Function[service_choice].error)) {
|
||||
Error_Function[service_choice].error(
|
||||
src, invoke_id, (BACNET_ERROR_CLASS)error_class,
|
||||
(BACNET_ERROR_CODE)error_code);
|
||||
}
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_REJECT:
|
||||
if (apdu_len < 3) {
|
||||
break;
|
||||
}
|
||||
invoke_id = apdu[1];
|
||||
reason = apdu[2];
|
||||
if (Reject_Function) {
|
||||
Reject_Function(src, invoke_id, reason);
|
||||
}
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
break;
|
||||
case PDU_TYPE_ABORT:
|
||||
if (apdu_len < 3) {
|
||||
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