Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AccessControl] Add Access Control checks to event management #15376

Merged
merged 8 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
}
}
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.
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
* @retval CHIP_EVENT_ID_FOUND This path should be included in the generated event report.
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
* @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