Skip to content

Commit

Permalink
Make AutoCommissioner::SetCommissioningParameters safer. (#24422)
Browse files Browse the repository at this point in the history
* Make AutoCommissioner::SetCommissioningParameters safer.

Right now, we copy all members of the incoming CommissioningParameters (which might include pointers to buffers that we don't own), then copy some of those external buffers into our own buffers.  Then we also hand-copy some scalar values we have already copied.

Changes in this PR:

* Stop copying scalar values that operator= already handled.
* Clear out all buffer references from our copy of CommissioningParameters
  before we start copying things into our buffers, so we don't end up with
  dangling pointers.
* Add the missing early return when an incoming country code value is too long
  (used to end up with a dangling pointer).

* Address review comment.
  • Loading branch information
bzbarsky-apple authored Jan 20, 2023
1 parent 6648ef2 commit 5706a21
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 29 deletions.
79 changes: 50 additions & 29 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,53 +45,83 @@ void AutoCommissioner::SetOperationalCredentialsDelegate(OperationalCredentialsD
mOperationalCredentialsDelegate = operationalCredentialsDelegate;
}

CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params)
// Returns true if maybeUnsafeSpan is pointing to a buffer that we're not sure
// will live for long enough. knownSafeSpan, if it has a value, points to a
// buffer that we _are_ sure will live for long enough.
template <typename SpanType>
static bool IsUnsafeSpan(const Optional<SpanType> & maybeUnsafeSpan, const Optional<SpanType> & knownSafeSpan)
{
mParams = params;
if (params.GetFailsafeTimerSeconds().HasValue())
if (!maybeUnsafeSpan.HasValue())
{
ChipLogProgress(Controller, "Setting failsafe timer from parameters");
mParams.SetFailsafeTimerSeconds(params.GetFailsafeTimerSeconds().Value());
return false;
}

if (params.GetCASEFailsafeTimerSeconds().HasValue())
if (!knownSafeSpan.HasValue())
{
ChipLogProgress(Controller, "Setting CASE failsafe timer from parameters");
mParams.SetCASEFailsafeTimerSeconds(params.GetCASEFailsafeTimerSeconds().Value());
return true;
}

if (params.GetAdminSubject().HasValue())
return maybeUnsafeSpan.Value().data() != knownSafeSpan.Value().data();
}

CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParameters & params)
{
// Make sure any members that point to buffers that we are not pointing to
// our own buffers are not going to dangle. We can skip this step if all
// the buffers pointers that we don't plan to re-point to our own buffers
// below are already pointing to the same things as our own buffer pointers
// (so that we know they have to be safe somehow).
//
// The checks are a bit painful, because Span does not have a usable
// operator==, and in any case, we want to compare for pointer equality, not
// data equality.
bool haveMaybeDanglingBufferPointers =
((params.GetNOCChainGenerationParameters().HasValue() &&
(!mParams.GetNOCChainGenerationParameters().HasValue() ||
params.GetNOCChainGenerationParameters().Value().nocsrElements.data() !=
mParams.GetNOCChainGenerationParameters().Value().nocsrElements.data() ||
params.GetNOCChainGenerationParameters().Value().signature.data() !=
mParams.GetNOCChainGenerationParameters().Value().signature.data())) ||
IsUnsafeSpan(params.GetRootCert(), mParams.GetRootCert()) || IsUnsafeSpan(params.GetNoc(), mParams.GetNoc()) ||
IsUnsafeSpan(params.GetIcac(), mParams.GetIcac()) || IsUnsafeSpan(params.GetIpk(), mParams.GetIpk()) ||
IsUnsafeSpan(params.GetAttestationElements(), mParams.GetAttestationElements()) ||
IsUnsafeSpan(params.GetAttestationSignature(), mParams.GetAttestationSignature()) ||
IsUnsafeSpan(params.GetPAI(), mParams.GetPAI()) || IsUnsafeSpan(params.GetDAC(), mParams.GetDAC()));

mParams = params;

if (haveMaybeDanglingBufferPointers)
{
ChipLogProgress(Controller, "Setting adminSubject from parameters");
mParams.SetAdminSubject(params.GetAdminSubject().Value());
mParams.ClearExternalBufferDependentValues();
}

// For members of params that point to some sort of buffer, we have to copy
// the data over into our own buffers.

if (params.GetThreadOperationalDataset().HasValue())
{
ByteSpan dataset = params.GetThreadOperationalDataset().Value();
if (dataset.size() > CommissioningParameters::kMaxThreadDatasetLen)
{
ChipLogError(Controller, "Thread operational data set is too large");
// Make sure our buffer pointers don't dangle.
mParams.ClearExternalBufferDependentValues();
return CHIP_ERROR_INVALID_ARGUMENT;
}
memcpy(mThreadOperationalDataset, dataset.data(), dataset.size());
ChipLogProgress(Controller, "Setting thread operational dataset from parameters");
mParams.SetThreadOperationalDataset(ByteSpan(mThreadOperationalDataset, dataset.size()));
}

if (params.GetAttemptThreadNetworkScan().HasValue())
{
ChipLogProgress(Controller, "Setting attempt thread scan from parameters");
mParams.SetAttemptThreadNetworkScan(params.GetAttemptThreadNetworkScan().Value());
}

if (params.GetWiFiCredentials().HasValue())
{
WiFiCredentials creds = params.GetWiFiCredentials().Value();
if (creds.ssid.size() > CommissioningParameters::kMaxSsidLen ||
creds.credentials.size() > CommissioningParameters::kMaxCredentialsLen)
{
ChipLogError(Controller, "Wifi credentials are too large");
// Make sure our buffer pointers don't dangle.
mParams.ClearExternalBufferDependentValues();
return CHIP_ERROR_INVALID_ARGUMENT;
}
memcpy(mSsid, creds.ssid.data(), creds.ssid.size());
Expand All @@ -101,12 +131,6 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
WiFiCredentials(ByteSpan(mSsid, creds.ssid.size()), ByteSpan(mCredentials, creds.credentials.size())));
}

if (params.GetAttemptWiFiNetworkScan().HasValue())
{
ChipLogProgress(Controller, "Setting attempt wifi scan from parameters");
mParams.SetAttemptWiFiNetworkScan(params.GetAttemptWiFiNetworkScan().Value());
}

if (params.GetCountryCode().HasValue())
{
auto code = params.GetCountryCode().Value();
Expand All @@ -118,6 +142,9 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
else
{
ChipLogError(Controller, "Country code is too large: %u", static_cast<unsigned>(code.size()));
// Make sure our buffer pointers don't dangle.
mParams.ClearExternalBufferDependentValues();
return CHIP_ERROR_INVALID_ARGUMENT;
}
}

Expand Down Expand Up @@ -148,12 +175,6 @@ CHIP_ERROR AutoCommissioner::SetCommissioningParameters(const CommissioningParam
}
mParams.SetCSRNonce(ByteSpan(mCSRNonce, sizeof(mCSRNonce)));

if (params.GetSkipCommissioningComplete().HasValue())
{
ChipLogProgress(Controller, "Setting PASE-only commissioning from parameters");
mParams.SetSkipCommissioningComplete(params.GetSkipCommissioningComplete().Value());
}

return CHIP_NO_ERROR;
}

Expand Down
20 changes: 20 additions & 0 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,26 @@ class CommissioningParameters
return *this;
}

// Clear all members that depend on some sort of external buffer. Can be
// used to make sure that we are not holding any dangling pointers.
void ClearExternalBufferDependentValues()
{
mCSRNonce.ClearValue();
mAttestationNonce.ClearValue();
mWiFiCreds.ClearValue();
mCountryCode.ClearValue();
mThreadOperationalDataset.ClearValue();
mNOCChainGenerationParameters.ClearValue();
mRootCert.ClearValue();
mNoc.ClearValue();
mIcac.ClearValue();
mIpk.ClearValue();
mAttestationElements.ClearValue();
mAttestationSignature.ClearValue();
mPAI.ClearValue();
mDAC.ClearValue();
}

private:
// Items that can be set by the commissioner
Optional<uint16_t> mFailsafeTimerSeconds;
Expand Down

0 comments on commit 5706a21

Please sign in to comment.