Skip to content

Commit

Permalink
[AccessControl] Add Access Control checks to event management (#15376)
Browse files Browse the repository at this point in the history
* [ACL] Add ACL checks to event management

* Address comments

* Update to ToT

* Change ACL to Access Control

* Do not run TestReadRoundtripWithEventStatusIBInEventReport on ESP32 etc.

* Address comments

* remove mBypassAccessControl
  • Loading branch information
erjiaqing authored Mar 2, 2022
1 parent c3f31bb commit 1ccc2af
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 85 deletions.
6 changes: 3 additions & 3 deletions src/app/EventLoggingTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#pragma once

#include <access/SubjectDescriptor.h>
#include <app/ClusterInfo.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPCore.h>
Expand Down Expand Up @@ -152,8 +153,7 @@ class EventOptions
struct EventLoadOutContext
{
EventLoadOutContext(TLV::TLVWriter & aWriter, PriorityLevel aPriority, EventNumber aStartingEventNumber) :
mWriter(aWriter), mPriority(aPriority), mStartingEventNumber(aStartingEventNumber), mCurrentEventNumber(0), mFirst(true),
mFabricIndex(0)
mWriter(aWriter), mPriority(aPriority), mStartingEventNumber(aStartingEventNumber), mCurrentEventNumber(0), mFirst(true)
{}

TLV::TLVWriter & mWriter;
Expand All @@ -165,7 +165,7 @@ struct EventLoadOutContext
size_t mEventCount = 0;
ClusterInfo * mpInterestedEventPaths = nullptr;
bool mFirst = true;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
Access::SubjectDescriptor mSubjectDescriptor;
};
} // namespace app
} // namespace chip
144 changes: 103 additions & 41 deletions src/app/EventManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
* limitations under the License.
*/

#include <access/AccessControl.h>
#include <access/RequestPath.h>
#include <access/SubjectDescriptor.h>
#include <app/EventManagement.h>
#include <app/InteractionModelEngine.h>
#include <app/RequiredPrivilege.h>
#include <inttypes.h>
#include <lib/core/CHIPEventLoggingConfig.h>
#include <lib/core/CHIPTLVUtilities.hpp>
Expand Down Expand Up @@ -77,29 +81,6 @@ struct CopyAndAdjustDeltaTimeContext
EventLoadOutContext * mpContext = nullptr;
};

/**
* @brief
* Internal structure for traversing events.
*/
struct EventEnvelopeContext
{
EventEnvelopeContext() {}

int mFieldsToRead = 0;
/* PriorityLevel and DeltaTime are there if that is not first event when putting events in report*/
#if CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS & CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME
Timestamp mCurrentTime = Timestamp::System(System::Clock::kZero);
#else
Timestamp mCurrentTime = Timestamp::Epoch(System::Clock::kZero);
#endif
PriorityLevel mPriority = PriorityLevel::First;
ClusterId mClusterId = 0;
EndpointId mEndpointId = 0;
EventId mEventId = 0;
EventNumber mEventNumber = 0;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
};

void EventManagement::InitializeCounter(Platform::PersistedStorage::Key * apCounterKey, uint32_t aCounterEpoch,
PersistedCounter * apPersistedCounter)
{
Expand Down Expand Up @@ -590,46 +571,102 @@ CHIP_ERROR EventManagement::CopyEvent(const TLVReader & aReader, TLVWriter & aWr
return CHIP_NO_ERROR;
}

static bool IsInterestedEventPaths(EventLoadOutContext * eventLoadOutContext, const EventEnvelopeContext & event)
CHIP_ERROR EventManagement::WriteEventStatusIB(TLVWriter & aWriter, const ConcreteEventPath & aEvent, StatusIB aStatus)
{
TLVType containerType;
ReturnErrorOnFailure(aWriter.StartContainer(AnonymousTag(), kTLVType_Structure, containerType));

EventStatusIB::Builder builder;
builder.Init(&aWriter, to_underlying(EventReportIB::Tag::kEventStatus));

ReturnErrorOnFailure(builder.CreatePath()
.Endpoint(aEvent.mEndpointId)
.Cluster(aEvent.mClusterId)
.Event(aEvent.mEventId)
.EndOfEventPathIB()
.GetError());

ReturnErrorOnFailure(builder.CreateErrorStatus().EncodeStatusIB(aStatus).GetError());

ReturnErrorOnFailure(builder.EndOfEventStatusIB().GetError());

ReturnErrorOnFailure(aWriter.EndContainer(containerType));
ReturnErrorOnFailure(aWriter.Finalize());
return CHIP_NO_ERROR;
}

CHIP_ERROR EventManagement::CheckEventContext(EventLoadOutContext * eventLoadOutContext,
const EventManagement::EventEnvelopeContext & event)
{
if (eventLoadOutContext->mCurrentEventNumber < eventLoadOutContext->mStartingEventNumber)
{
return false;
return CHIP_ERROR_UNEXPECTED_EVENT;
}

if (event.mFabricIndex != kUndefinedFabricIndex && eventLoadOutContext->mFabricIndex != event.mFabricIndex)
if (event.mFabricIndex != kUndefinedFabricIndex && eventLoadOutContext->mSubjectDescriptor.fabricIndex != event.mFabricIndex)
{
return false;
return CHIP_ERROR_UNEXPECTED_EVENT;
}

ConcreteEventPath path(event.mEndpointId, event.mClusterId, event.mEventId);
CHIP_ERROR ret = CHIP_ERROR_UNEXPECTED_EVENT;

bool eventReadViaConcretePath = false;

for (auto * interestedPath = eventLoadOutContext->mpInterestedEventPaths; interestedPath != nullptr;
interestedPath = interestedPath->mpNext)
{
if (interestedPath->IsEventPathSupersetOf(path))
{
return true;
ret = CHIP_NO_ERROR;
if (!interestedPath->HasEventWildcard())
{
eventReadViaConcretePath = true;
break;
}
}
}

ReturnErrorOnFailure(ret);

Access::RequestPath requestPath{ .cluster = event.mClusterId, .endpoint = event.mEndpointId };
Access::Privilege requestPrivilege = RequiredPrivilege::ForReadEvent(path);
CHIP_ERROR accessControlError =
Access::GetAccessControl().Check(eventLoadOutContext->mSubjectDescriptor, requestPath, requestPrivilege);

if (accessControlError != CHIP_NO_ERROR)
{
ReturnErrorCodeIf(accessControlError != CHIP_ERROR_ACCESS_DENIED, accessControlError);
if (eventReadViaConcretePath)
{
ret = CHIP_ERROR_ACCESS_DENIED;
}
else
{
ret = CHIP_ERROR_UNEXPECTED_EVENT;
}
}
return false;

return ret;
}

CHIP_ERROR EventManagement::EventIterator(const TLVReader & aReader, size_t aDepth, EventLoadOutContext * apEventLoadOutContext)
CHIP_ERROR EventManagement::EventIterator(const TLVReader & aReader, size_t aDepth, EventLoadOutContext * apEventLoadOutContext,
EventEnvelopeContext * event)
{
CHIP_ERROR err = CHIP_NO_ERROR;
TLVReader innerReader;
TLVType tlvType;
TLVType tlvType1;
EventEnvelopeContext event;

innerReader.Init(aReader);
VerifyOrDie(event != nullptr);
ReturnErrorOnFailure(innerReader.EnterContainer(tlvType));
ReturnErrorOnFailure(innerReader.Next());

ReturnErrorOnFailure(innerReader.EnterContainer(tlvType1));
err = TLV::Utilities::Iterate(innerReader, FetchEventParameters, &event, false /*recurse*/);
err = TLV::Utilities::Iterate(innerReader, FetchEventParameters, event, false /*recurse*/);

if (event.mFieldsToRead != kRequiredEventField)
if (event->mFieldsToRead != kRequiredEventField)
{
return CHIP_ERROR_INVALID_ARGUMENT;
}
Expand All @@ -640,20 +677,27 @@ CHIP_ERROR EventManagement::EventIterator(const TLVReader & aReader, size_t aDep
}
ReturnErrorOnFailure(err);

apEventLoadOutContext->mCurrentTime = event.mCurrentTime;
apEventLoadOutContext->mCurrentEventNumber = event.mEventNumber;
if (IsInterestedEventPaths(apEventLoadOutContext, event))
apEventLoadOutContext->mCurrentTime = event->mCurrentTime;
apEventLoadOutContext->mCurrentEventNumber = event->mEventNumber;

err = CheckEventContext(apEventLoadOutContext, *event);
if (err == CHIP_NO_ERROR)
{
err = CHIP_EVENT_ID_FOUND;
}
else if (err == CHIP_ERROR_UNEXPECTED_EVENT)
{
return CHIP_EVENT_ID_FOUND;
err = CHIP_NO_ERROR;
}

return CHIP_NO_ERROR;
return err;
}

CHIP_ERROR EventManagement::CopyEventsSince(const TLVReader & aReader, size_t aDepth, void * apContext)
{
EventLoadOutContext * const loadOutContext = static_cast<EventLoadOutContext *>(apContext);
CHIP_ERROR err = EventIterator(aReader, aDepth, loadOutContext);
EventEnvelopeContext event;
CHIP_ERROR err = EventIterator(aReader, aDepth, loadOutContext, &event);
if (err == CHIP_EVENT_ID_FOUND)
{
// checkpoint the writer
Expand All @@ -675,11 +719,29 @@ CHIP_ERROR EventManagement::CopyEventsSince(const TLVReader & aReader, size_t aD
loadOutContext->mFirst = false;
loadOutContext->mEventCount++;
}
else if (err == CHIP_ERROR_ACCESS_DENIED)
{
// checkpoint the writer
TLV::TLVWriter checkpoint = loadOutContext->mWriter;

err = WriteEventStatusIB(loadOutContext->mWriter, ConcreteEventPath(event.mEndpointId, event.mClusterId, event.mEventId),
StatusIB(Protocols::InteractionModel::Status::UnsupportedAccess));

if (err != CHIP_NO_ERROR)
{
loadOutContext->mWriter = checkpoint;
return err;
}

loadOutContext->mPreviousTime.mValue = loadOutContext->mCurrentTime.mValue;
loadOutContext->mFirst = false;
loadOutContext->mEventCount++;
}
return err;
}

CHIP_ERROR EventManagement::FetchEventsSince(TLVWriter & aWriter, ClusterInfo * apClusterInfolist, EventNumber & aEventMin,
size_t & aEventCount, FabricIndex aFabricIndex)
size_t & aEventCount, const Access::SubjectDescriptor & aSubjectDescriptor)
{
// TODO: Add particular set of event Paths in FetchEventsSince so that we can filter the interested paths
CHIP_ERROR err = CHIP_NO_ERROR;
Expand All @@ -692,7 +754,7 @@ CHIP_ERROR EventManagement::FetchEventsSince(TLVWriter & aWriter, ClusterInfo *
ScopedLock lock(sInstance);
#endif // !CHIP_SYSTEM_CONFIG_NO_LOCKING

context.mFabricIndex = aFabricIndex;
context.mSubjectDescriptor = aSubjectDescriptor;
context.mpInterestedEventPaths = apClusterInfolist;
err = GetEventReader(reader, PriorityLevel::Critical, &bufWrapper);
SuccessOrExit(err);
Expand Down
48 changes: 46 additions & 2 deletions src/app/EventManagement.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@

#include "EventLoggingDelegate.h"
#include "EventLoggingTypes.h"
#include <access/SubjectDescriptor.h>
#include <app/ClusterInfo.h>
#include <app/MessageDef/EventDataIB.h>
#include <app/MessageDef/StatusIB.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPCircularTLVBuffer.h>
#include <lib/support/PersistedCounter.h>
Expand Down Expand Up @@ -339,7 +341,7 @@ class EventManagement
*
*/
CHIP_ERROR FetchEventsSince(chip::TLV::TLVWriter & aWriter, ClusterInfo * apClusterInfolist, EventNumber & aEventMin,
size_t & aEventCount, FabricIndex aFabricIndex);
size_t & aEventCount, const Access::SubjectDescriptor & aSubjectDescriptor);

/**
* @brief
Expand All @@ -361,6 +363,29 @@ class EventManagement
void SetScheduledEventInfo(EventNumber & aEventNumber, uint32_t & aInitialWrittenEventBytes);

private:
/**
* @brief
* Internal structure for traversing events.
*/
struct EventEnvelopeContext
{
EventEnvelopeContext() {}

int mFieldsToRead = 0;
/* PriorityLevel and DeltaTime are there if that is not first event when putting events in report*/
#if CHIP_CONFIG_EVENT_LOGGING_UTC_TIMESTAMPS & CHIP_SYSTEM_CONFIG_PLATFORM_PROVIDES_TIME
Timestamp mCurrentTime = Timestamp::System(System::Clock::kZero);
#else
Timestamp mCurrentTime = Timestamp::Epoch(System::Clock::kZero);
#endif
PriorityLevel mPriority = PriorityLevel::First;
ClusterId mClusterId = 0;
EndpointId mEndpointId = 0;
EventId mEventId = 0;
EventNumber mEventNumber = 0;
FabricIndex mFabricIndex = kUndefinedFabricIndex;
};

void VendEventNumber();
CHIP_ERROR CalculateEventSize(EventLoggingDelegate * apDelegate, const EventOptions * apOptions, uint32_t & requiredSize);
/**
Expand Down Expand Up @@ -423,7 +448,8 @@ class EventManagement
* The function is used to scan through the event log to find events matching the spec in the supplied context.
* Particularly, it would check against mStartingEventNumber, and skip fetched event.
*/
static CHIP_ERROR EventIterator(const TLV::TLVReader & aReader, size_t aDepth, EventLoadOutContext * apEventLoadOutContext);
static CHIP_ERROR EventIterator(const TLV::TLVReader & aReader, size_t aDepth, EventLoadOutContext * apEventLoadOutContext,
EventEnvelopeContext * event);

/**
* @brief Internal iterator function used to fetch event into EventEnvelopeContext, then EventIterator would filter event
Expand All @@ -449,11 +475,29 @@ class EventManagement
return CHIP_ERROR_NO_MEMORY;
};

/**
* @brief Check whether the event instance represented by the EventEnvelopeContext should be included in the report.
*
* @retval CHIP_ERROR_UNEXPECTED_EVENT This path should be excluded in the generated event report.
* @retval CHIP_EVENT_ID_FOUND This path should be included in the generated event report.
* @retval CHIP_ERROR_ACCESS_DENIED This path should be included in the generated event report, but the client does not have
* . enough privilege to access it.
*
* TODO: Consider using CHIP_NO_ERROR, CHIP_ERROR_SKIP_EVENT, CHIP_ERROR_ACCESS_DENINED or some enum to represent the checking
* result.
*/
static CHIP_ERROR CheckEventContext(EventLoadOutContext * eventLoadOutContext, const EventEnvelopeContext & event);

/**
* @brief copy event from circular buffer to target buffer for report
*/
static CHIP_ERROR CopyEvent(const TLV::TLVReader & aReader, TLV::TLVWriter & aWriter, EventLoadOutContext * apContext);

/**
* @brief construct EventStatusIB to target buffer for report
*/
static CHIP_ERROR WriteEventStatusIB(TLV::TLVWriter & aWriter, const ConcreteEventPath & aEvent, StatusIB aStatus);

/**
* @brief
* A function to get the circular buffer for particular priority
Expand Down
8 changes: 8 additions & 0 deletions src/app/MessageDef/EventPathIB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ CHIP_ERROR EventPathIB::Parser::GetEvent(EventId * const apEvent) const
return GetUnsignedInteger(to_underlying(Tag::kEvent), apEvent);
}

CHIP_ERROR EventPathIB::Parser::GetEventPath(ConcreteEventPath * const apPath) const
{
VerifyOrReturnError(GetEndpoint(&(apPath->mEndpointId)) == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_EVENT_PATH);
VerifyOrReturnError(GetCluster(&(apPath->mClusterId)) == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_EVENT_PATH);
VerifyOrReturnError(GetEvent(&(apPath->mEventId)) == CHIP_NO_ERROR, CHIP_ERROR_IM_MALFORMED_EVENT_PATH);
return CHIP_NO_ERROR;
}

CHIP_ERROR EventPathIB::Parser::GetIsUrgent(bool * const apIsUrgent) const
{
return GetSimpleValue(to_underlying(Tag::kIsUrgent), TLV::kTLVType_Boolean, apIsUrgent);
Expand Down
10 changes: 10 additions & 0 deletions src/app/MessageDef/EventPathIB.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ class Parser : public ListParser
* #CHIP_END_OF_TLV if there is no such element
*/
CHIP_ERROR GetIsUrgent(bool * const apIsUrgent) const;

/**
* @brief Fill the fields in apPath from the parser, the path in the parser should be a concrete path.
*
* @param [in] apEvent A pointer to apEvent
*
* @return #CHIP_NO_ERROR on success
* #CHIP_ERROR_IM_MALFORMED_EVENT_PATH if the path from the reader is not a valid concrere event path.
*/
CHIP_ERROR GetEventPath(ConcreteEventPath * const apPath) const;
};

class Builder : public ListBuilder
Expand Down
Loading

0 comments on commit 1ccc2af

Please sign in to comment.