mirror of
https://github.com/espressif/openthread.git
synced 2026-09-07 01:30:11 +00:00
[dataset] add additional validation for dataset range and formatting (#13518)
This commit is contained in:
@@ -578,6 +578,12 @@ otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameSt
|
||||
*
|
||||
* This method also checks whether there are duplicated TLVs or the TLVs are not well-formed in the @p aDatasetTlvs.
|
||||
*
|
||||
* In addition to the TLV lengths, the values of the following TLVs are validated: Channel and Wake-up Channel (the
|
||||
* channel page must be supported and the channel within range), Channel Mask (well-formed entries), PAN ID (must not
|
||||
* be the broadcast PAN ID 0xffff), Extended PAN ID (must not be all-zeros or all-ones), Mesh-Local Prefix (must be a
|
||||
* locally assigned ULA prefix, i.e., `fd00::/8`), Network Name (1 to 16 bytes, valid UTF-8, no control characters),
|
||||
* and Security Policy.
|
||||
*
|
||||
* @param[in] aDatasetTlvs A pointer to dataset TLVs.
|
||||
* @param[in] aActive TRUE for Active Dataset, FALSE for Pending Dataset.
|
||||
*
|
||||
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (617)
|
||||
#define OPENTHREAD_API_VERSION (618)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
@@ -128,6 +128,10 @@ bool Dataset::IsTlvValid(const Tlv &aTlv)
|
||||
bool isValid = true;
|
||||
uint8_t minLength = 0;
|
||||
|
||||
// Validate the TLV format, i.e., that the value is long enough for
|
||||
// the TLV type. TLV types whose `IsValid()` does its own length
|
||||
// checking are not included here.
|
||||
|
||||
switch (aTlv.GetType())
|
||||
{
|
||||
case Tlv::kActiveTimestamp:
|
||||
@@ -139,41 +143,25 @@ bool Dataset::IsTlvValid(const Tlv &aTlv)
|
||||
case Tlv::kDelayTimer:
|
||||
minLength = sizeof(DelayTimerTlv::UintValueType);
|
||||
break;
|
||||
case Tlv::kPanId:
|
||||
minLength = sizeof(PanIdTlv::UintValueType);
|
||||
break;
|
||||
case Tlv::kExtendedPanId:
|
||||
minLength = sizeof(ExtendedPanIdTlv::ValueType);
|
||||
break;
|
||||
case Tlv::kPskc:
|
||||
minLength = sizeof(PskcTlv::ValueType);
|
||||
break;
|
||||
case Tlv::kNetworkKey:
|
||||
minLength = sizeof(NetworkKeyTlv::ValueType);
|
||||
break;
|
||||
case Tlv::kPanId:
|
||||
minLength = sizeof(PanIdTlv::UintValueType);
|
||||
break;
|
||||
case Tlv::kExtendedPanId:
|
||||
minLength = sizeof(ExtendedPanIdTlv::ValueType);
|
||||
break;
|
||||
case Tlv::kMeshLocalPrefix:
|
||||
minLength = sizeof(MeshLocalPrefixTlv::ValueType);
|
||||
break;
|
||||
case Tlv::kChannel:
|
||||
VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
|
||||
isValid = aTlv.ReadValueAs<ChannelTlv>().IsValid();
|
||||
break;
|
||||
case Tlv::kWakeupChannel:
|
||||
VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false);
|
||||
isValid = aTlv.ReadValueAs<WakeupChannelTlv>().IsValid();
|
||||
minLength = sizeof(ChannelTlvValue);
|
||||
break;
|
||||
case Tlv::kNetworkName:
|
||||
isValid = As<NetworkNameTlv>(aTlv).IsValid();
|
||||
break;
|
||||
|
||||
case Tlv::kSecurityPolicy:
|
||||
isValid = As<SecurityPolicyTlv>(aTlv).IsValid();
|
||||
break;
|
||||
|
||||
case Tlv::kChannelMask:
|
||||
isValid = As<ChannelMaskTlv>(aTlv).IsValid();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -181,6 +169,41 @@ bool Dataset::IsTlvValid(const Tlv &aTlv)
|
||||
if (minLength > 0)
|
||||
{
|
||||
isValid = (aTlv.GetLength() >= minLength);
|
||||
VerifyOrExit(isValid);
|
||||
}
|
||||
|
||||
// Validate the TLV value.
|
||||
|
||||
switch (aTlv.GetType())
|
||||
{
|
||||
case Tlv::kPanId:
|
||||
// The broadcast PAN ID does not identify a network.
|
||||
isValid = (aTlv.ReadValueAs<PanIdTlv>() != Mac::kPanIdBroadcast);
|
||||
break;
|
||||
case Tlv::kExtendedPanId:
|
||||
isValid = aTlv.ReadValueAs<ExtendedPanIdTlv>().IsValid();
|
||||
break;
|
||||
case Tlv::kMeshLocalPrefix:
|
||||
// The Mesh-Local Prefix is required to be a locally assigned ULA prefix.
|
||||
isValid = aTlv.ReadValueAs<MeshLocalPrefixTlv>().IsLocallyAssignedUla();
|
||||
break;
|
||||
case Tlv::kChannel:
|
||||
isValid = aTlv.ReadValueAs<ChannelTlv>().IsValid();
|
||||
break;
|
||||
case Tlv::kWakeupChannel:
|
||||
isValid = aTlv.ReadValueAs<WakeupChannelTlv>().IsValid();
|
||||
break;
|
||||
case Tlv::kNetworkName:
|
||||
isValid = As<NetworkNameTlv>(aTlv).IsValid();
|
||||
break;
|
||||
case Tlv::kSecurityPolicy:
|
||||
isValid = As<SecurityPolicyTlv>(aTlv).IsValid();
|
||||
break;
|
||||
case Tlv::kChannelMask:
|
||||
isValid = As<ChannelMaskTlv>(aTlv).IsValid();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
|
||||
@@ -49,5 +49,23 @@ ExtendedPanId::InfoString ExtendedPanId::ToString(void) const
|
||||
|
||||
Error ExtendedPanId::GenerateRandom(void) { return Random::Crypto::Fill(*this); }
|
||||
|
||||
bool ExtendedPanId::IsValid(void) const
|
||||
{
|
||||
// The all-zeros and all-ones Extended PAN IDs are disallowed by
|
||||
// the Thread specification. Determine both cases in a single pass
|
||||
// by OR-ing and AND-ing all bytes together.
|
||||
|
||||
uint8_t oredBytes = 0x00;
|
||||
uint8_t andedBytes = 0xff;
|
||||
|
||||
for (uint8_t byte : m8)
|
||||
{
|
||||
oredBytes |= byte;
|
||||
andedBytes &= byte;
|
||||
}
|
||||
|
||||
return (oredBytes != 0x00) && (andedBytes != 0xff);
|
||||
}
|
||||
|
||||
} // namespace MeshCoP
|
||||
} // namespace ot
|
||||
|
||||
@@ -75,6 +75,16 @@ public:
|
||||
*/
|
||||
Error GenerateRandom(void);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Extended PAN Identifier is valid.
|
||||
*
|
||||
* The all-zeros and all-ones values are reserved and disallowed by the Thread specification.
|
||||
*
|
||||
* @retval TRUE If the Extended PAN Identifier is valid.
|
||||
* @retval FALSE If the Extended PAN Identifier is not valid.
|
||||
*/
|
||||
bool IsValid(void) const;
|
||||
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
} // namespace MeshCoP
|
||||
|
||||
@@ -58,7 +58,24 @@ void NetworkNameTlv::SetNetworkName(const NameData &aNameData)
|
||||
SetLength(len);
|
||||
}
|
||||
|
||||
bool NetworkNameTlv::IsValid(void) const { return IsValidUtf8String(mNetworkName, GetLength()); }
|
||||
bool NetworkNameTlv::IsValid(void) const
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
#if OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
|
||||
if (GetLength() == 0)
|
||||
{
|
||||
ExitNow(isValid = true);
|
||||
}
|
||||
#endif
|
||||
|
||||
VerifyOrExit(IsValueInRange<uint8_t>(GetLength(), 1, NetworkName::kMaxSize));
|
||||
VerifyOrExit(IsValidUtf8String(mNetworkName, GetLength()));
|
||||
isValid = true;
|
||||
|
||||
exit:
|
||||
return isValid;
|
||||
}
|
||||
|
||||
Error SteeringDataTlv::CopyTo(SteeringData &aSteeringData) const
|
||||
{
|
||||
|
||||
@@ -93,6 +93,22 @@ public:
|
||||
*/
|
||||
Error InitFrom(const Prefix &aPrefix);
|
||||
|
||||
/**
|
||||
* Indicates whether or not the Network Prefix is a locally assigned Unique Local Address (ULA) prefix, i.e., a
|
||||
* `fd00::/8` prefix.
|
||||
*
|
||||
* RFC 4193 defines a ULA prefix as `fc00::/7` followed by the L bit, which is set to one for a locally assigned
|
||||
* prefix. Section 3.2 of RFC 4193 defines a Global ID generation process for locally assigned prefixes only, so
|
||||
* `fd00::/8` is the only form a conformant generator can produce. This is what `GenerateRandomUla()` produces.
|
||||
*
|
||||
* Note that this is intentionally stricter than `Prefix::IsUniqueLocal()`, which matches the entire `fc00::/7`
|
||||
* ULA range and is used to recognize prefixes advertised by other devices.
|
||||
*
|
||||
* @retval TRUE If the Network Prefix is a locally assigned ULA prefix.
|
||||
* @retval FALSE If the Network Prefix is not a locally assigned ULA prefix.
|
||||
*/
|
||||
bool IsLocallyAssignedUla(void) const { return m8[0] == 0xfd; }
|
||||
|
||||
} OT_TOOL_PACKED_END;
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
namespace ot {
|
||||
namespace MeshCoP {
|
||||
|
||||
template <size_t kSize> Error SetFromTlvsAndValidate(const uint8_t (&aTlvs)[kSize])
|
||||
{
|
||||
Dataset dataset;
|
||||
|
||||
// `SetFrom()` takes the length as `uint8_t`, so guard against an array
|
||||
// that would be silently narrowed when passed to it.
|
||||
static_assert(kSize <= Dataset::kMaxLength, "aTlvs is too long for a Dataset");
|
||||
|
||||
SuccessOrQuit(dataset.SetFrom(aTlvs, kSize));
|
||||
|
||||
return dataset.ValidateTlvs();
|
||||
}
|
||||
|
||||
void TestDataset(void)
|
||||
{
|
||||
static const uint8_t kTlvBytes[] = {
|
||||
@@ -57,6 +70,29 @@ void TestDataset(void)
|
||||
0x00, 0x03, 0x00, 0x00, 0x1a, 0x00, 0x03, 0x00, 0x00, 0x1a,
|
||||
};
|
||||
|
||||
// PAN ID TLV - the broadcast PAN ID is not allowed.
|
||||
static const uint8_t kInvalidPanId[] = {0x01, 0x02, 0xff, 0xff};
|
||||
static const uint8_t kValidPanId[] = {0x01, 0x02, 0xff, 0xfe};
|
||||
|
||||
// Extended PAN ID TLV - all-zeros and all-ones are disallowed.
|
||||
static const uint8_t kInvalidExtPanIdAllZeros[] = {0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t kInvalidExtPanIdAllOnes[] = {0x02, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
static const uint8_t kValidExtPanId[] = {0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
|
||||
|
||||
// Mesh-Local Prefix TLV - must be a locally assigned ULA prefix (`fd00::/8`).
|
||||
static const uint8_t kInvalidMeshLocalPrefix[] = {0x07, 0x08, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t kInvalidMeshLocalPrefixLBit[] = {0x07, 0x08, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const uint8_t kValidMeshLocalPrefix[] = {0x07, 0x08, 0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00};
|
||||
|
||||
// Network Name TLV - 1 to 16 bytes of valid UTF-8 without control characters.
|
||||
// A zero-length name is valid only when `ALLOW_EMPTY_NETWORK_NAME` is enabled.
|
||||
static const uint8_t kEmptyNetworkName[] = {0x03, 0x00};
|
||||
static const uint8_t kInvalidTooLongNetworkName[] = {0x03, 0x11, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'};
|
||||
static const uint8_t kInvalidUtf8NetworkName[] = {0x03, 0x03, 'a', 0x00, 'b'};
|
||||
static const uint8_t kValidMaxLenNetworkName[] = {0x03, 0x10, 'a', 'a', 'a', 'a', 'a', 'a', 'a',
|
||||
'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a'};
|
||||
|
||||
static const Tlv::Type kDatasetTlvTypes[] = {
|
||||
Tlv::kChannel, Tlv::kPanId, Tlv::kExtendedPanId, Tlv::kNetworkName, Tlv::kPskc,
|
||||
Tlv::kNetworkKey, Tlv::kMeshLocalPrefix, Tlv::kSecurityPolicy, Tlv::kActiveTimestamp,
|
||||
@@ -173,6 +209,28 @@ void TestDataset(void)
|
||||
SuccessOrQuit(dataset.SetFrom(kDuplicateChannels, sizeof(kDuplicateChannels) / 2));
|
||||
SuccessOrQuit(dataset.ValidateTlvs());
|
||||
|
||||
// Invalid TLV values
|
||||
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidPanId) == kErrorParse);
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidExtPanIdAllZeros) == kErrorParse);
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidExtPanIdAllOnes) == kErrorParse);
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidMeshLocalPrefix) == kErrorParse);
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidMeshLocalPrefixLBit) == kErrorParse);
|
||||
#if OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME
|
||||
SuccessOrQuit(SetFromTlvsAndValidate(kEmptyNetworkName));
|
||||
#else
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kEmptyNetworkName) == kErrorParse);
|
||||
#endif
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidTooLongNetworkName) == kErrorParse);
|
||||
VerifyOrQuit(SetFromTlvsAndValidate(kInvalidUtf8NetworkName) == kErrorParse);
|
||||
|
||||
// Valid variants of the same TLVs
|
||||
|
||||
SuccessOrQuit(SetFromTlvsAndValidate(kValidPanId));
|
||||
SuccessOrQuit(SetFromTlvsAndValidate(kValidExtPanId));
|
||||
SuccessOrQuit(SetFromTlvsAndValidate(kValidMeshLocalPrefix));
|
||||
SuccessOrQuit(SetFromTlvsAndValidate(kValidMaxLenNetworkName));
|
||||
|
||||
// Combining/Merging TLVs from two Datasets.
|
||||
|
||||
SuccessOrQuit(dataset.SetFrom(kTlvBytes, sizeof(kTlvBytes)));
|
||||
|
||||
Reference in New Issue
Block a user