Skip to content

Commit

Permalink
Subscribe to authentication event types and handle enrichment (northp…
Browse files Browse the repository at this point in the history
…olesec#107)

This PR starts adding in support for new authentication event types.
Protobuf/string serialization will come in future PRs.

The main goal is to define the new enriched types and handle them in the
enricher.
  • Loading branch information
mlw authored Nov 11, 2024
1 parent 1f41f0c commit 13e4fa2
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 163 deletions.
2 changes: 2 additions & 0 deletions Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ objc_library(
hdrs = ["EventProviders/EndpointSecurity/EnrichedTypes.h"],
deps = [
":EndpointSecurityMessage",
"//Source/common:Platform",
"//Source/santad/ProcessTree:process_tree_cc_proto",
],
)
Expand Down Expand Up @@ -1223,6 +1224,7 @@ santa_unit_test(
":EndpointSecurityMessage",
":Metrics",
":MockEndpointSecurityAPI",
"//Source/common:Platform",
"//Source/common:SNTCommonEnums",
"//Source/common:SNTMetricSet",
"//Source/common:TestUtils",
Expand Down
177 changes: 162 additions & 15 deletions Source/santad/EventProviders/EndpointSecurity/EnrichedTypes.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
/// Copyright 2022 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

/// This file groups all of the enriched message types - that is the
/// objects that are constructed to hold all enriched event data prior
Expand All @@ -25,6 +26,7 @@
#include <string>
#include <variant>

#include "Source/common/Platform.h"
#include "Source/santad/EventProviders/EndpointSecurity/Message.h"
#include "Source/santad/ProcessTree/process_tree.pb.h"

Expand Down Expand Up @@ -159,8 +161,10 @@ class EnrichedEventType {
return enrichment_time_;
}

private:
protected:
Message es_msg_;

private:
EnrichedProcess instigator_;
struct timespec enrichment_time_;
};
Expand Down Expand Up @@ -435,14 +439,157 @@ class EnrichedLoginLogout : public EnrichedEventType {
EnrichedLoginLogout(EnrichedLoginLogout &&) = default;
};

using EnrichedType = std::variant<
EnrichedClose, EnrichedExchange, EnrichedExec, EnrichedExit, EnrichedFork,
EnrichedLink, EnrichedRename, EnrichedUnlink, EnrichedCSInvalidated,
EnrichedLoginWindowSessionLogin, EnrichedLoginWindowSessionLogout,
EnrichedLoginWindowSessionLock, EnrichedLoginWindowSessionUnlock,
EnrichedScreenSharingAttach, EnrichedScreenSharingDetach,
EnrichedOpenSSHLogin, EnrichedOpenSSHLogout, EnrichedLoginLogin,
EnrichedLoginLogout>;
// Base class for authentication event types that contain instigator
// information. Note that beginning in macOS 15 instigator information
// is optional. If complete process info is missing, the audit token
// of the instigator is still made available.
class EnrichedAuthenticationWithInstigator : public EnrichedEventType {
public:
EnrichedAuthenticationWithInstigator(
Message &&es_msg, EnrichedProcess instigator,
std::optional<EnrichedProcess> enriched_auth_instigator)
: EnrichedEventType(std::move(es_msg), std::move(instigator)),
enriched_auth_instigator_(std::move(enriched_auth_instigator)) {}

virtual ~EnrichedAuthenticationWithInstigator() = default;

EnrichedAuthenticationWithInstigator(
EnrichedAuthenticationWithInstigator &&) = default;

virtual const es_process_t *AuthInstigator() const = 0;
virtual std::optional<audit_token_t> AuthInstigatorToken() const = 0;

const std::optional<EnrichedProcess> &EnrichedAuthInstigator() const {
return enriched_auth_instigator_;
}

private:
std::optional<EnrichedProcess> enriched_auth_instigator_;
};

class EnrichedAuthenticationOD : public EnrichedAuthenticationWithInstigator {
public:
using EnrichedAuthenticationWithInstigator::
EnrichedAuthenticationWithInstigator;

EnrichedAuthenticationOD(EnrichedAuthenticationOD &&) = default;

const es_process_t *AuthInstigator() const override {
#if HAVE_MACOS_13
return es_msg_->event.authentication->data.od->instigator;
#else
return nullptr;
#endif
}

std::optional<audit_token_t> AuthInstigatorToken() const override {
#if HAVE_MACOS_15
return es_msg_->version >= 8
? std::make_optional<audit_token_t>(
es_msg_->event.authentication->data.od->instigator_token)
: std::nullopt;
#else
return std::nullopt;
#endif
}
};

class EnrichedAuthenticationTouchID
: public EnrichedAuthenticationWithInstigator {
public:
EnrichedAuthenticationTouchID(
Message &&es_msg, EnrichedProcess instigator,
std::optional<EnrichedProcess> auth_instigator,
std::optional<std::shared_ptr<std::string>> username)
: EnrichedAuthenticationWithInstigator(std::move(es_msg),
std::move(instigator),
std::move(auth_instigator)),
username_(std::move(username)) {}

EnrichedAuthenticationTouchID(EnrichedAuthenticationTouchID &&) = default;

const es_process_t *AuthInstigator() const override {
#if HAVE_MACOS_13
return es_msg_->event.authentication->data.touchid->instigator;
#else
return nullptr;
#endif
}

std::optional<audit_token_t> AuthInstigatorToken() const override {
#if HAVE_MACOS_15
return es_msg_->version >= 8 ? std::make_optional<audit_token_t>(
es_msg_->event.authentication->data
.touchid->instigator_token)
: std::nullopt;
#else
return std::nullopt;
#endif
}

const std::optional<std::shared_ptr<std::string>> &Username() const {
return username_;
}

private:
std::optional<std::shared_ptr<std::string>> username_;
};

class EnrichedAuthenticationToken
: public EnrichedAuthenticationWithInstigator {
public:
using EnrichedAuthenticationWithInstigator::
EnrichedAuthenticationWithInstigator;

EnrichedAuthenticationToken(EnrichedAuthenticationToken &&) = default;

const es_process_t *AuthInstigator() const override {
#if HAVE_MACOS_13
return es_msg_->event.authentication->data.token->instigator;
#else
return nullptr;
#endif
}

std::optional<audit_token_t> AuthInstigatorToken() const override {
#if HAVE_MACOS_15
return es_msg_->version >= 8 ? std::make_optional<audit_token_t>(
es_msg_->event.authentication->data
.token->instigator_token)
: std::nullopt;
#else
return std::nullopt;
#endif
}
};

class EnrichedAuthenticationAutoUnlock : public EnrichedEventType {
public:
EnrichedAuthenticationAutoUnlock(Message &&es_msg, EnrichedProcess instigator,
std::optional<uid_t> uid)
: EnrichedEventType(std::move(es_msg), std::move(instigator)),
uid_(std::move(uid)) {}

EnrichedAuthenticationAutoUnlock(EnrichedAuthenticationAutoUnlock &&) =
default;

inline std::optional<uid_t> UID() const { return uid_; }

private:
std::optional<uid_t> uid_;
};

using EnrichedType =
std::variant<EnrichedClose, EnrichedExchange, EnrichedExec, EnrichedExit,
EnrichedFork, EnrichedLink, EnrichedRename, EnrichedUnlink,
EnrichedCSInvalidated, EnrichedLoginWindowSessionLogin,
EnrichedLoginWindowSessionLogout,
EnrichedLoginWindowSessionLock,
EnrichedLoginWindowSessionUnlock, EnrichedScreenSharingAttach,
EnrichedScreenSharingDetach, EnrichedOpenSSHLogin,
EnrichedOpenSSHLogout, EnrichedLoginLogin, EnrichedLoginLogout,
EnrichedAuthenticationOD, EnrichedAuthenticationTouchID,
EnrichedAuthenticationToken, EnrichedAuthenticationAutoUnlock>;

class EnrichedMessage {
public:
Expand Down
3 changes: 3 additions & 0 deletions Source/santad/EventProviders/EndpointSecurity/Enricher.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class Enricher {
virtual EnrichedProcess Enrich(
const es_process_t &es_proc,
EnrichOptions options = EnrichOptions::kDefault);
virtual std::optional<EnrichedProcess> Enrich(
const es_process_t *es_proc,
EnrichOptions options = EnrichOptions::kDefault);
virtual EnrichedFile Enrich(const es_file_t &es_file,
EnrichOptions options = EnrichOptions::kDefault);

Expand Down
48 changes: 42 additions & 6 deletions Source/santad/EventProviders/EndpointSecurity/Enricher.mm
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
/// Copyright 2022 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#include "Source/santad/EventProviders/EndpointSecurity/Enricher.h"

Expand Down Expand Up @@ -86,6 +87,36 @@
return std::make_unique<EnrichedMessage>(
EnrichedCSInvalidated(std::move(es_msg), Enrich(*es_msg->process)));
#if HAVE_MACOS_13
case ES_EVENT_TYPE_NOTIFY_AUTHENTICATION:
switch (es_msg->event.authentication->type) {
case ES_AUTHENTICATION_TYPE_OD:
return std::make_unique<EnrichedMessage>(
EnrichedAuthenticationOD(std::move(es_msg), Enrich(*es_msg->process),
Enrich(es_msg->event.authentication->data.od->instigator)));
case ES_AUTHENTICATION_TYPE_TOUCHID:
return std::make_unique<EnrichedMessage>(EnrichedAuthenticationTouchID(
std::move(es_msg), Enrich(*es_msg->process),
Enrich(es_msg->event.authentication->data.touchid->instigator),
es_msg->event.authentication->data.touchid->has_uid
? UsernameForUID(es_msg->event.authentication->data.touchid->uid.uid)
: std::nullopt));
case ES_AUTHENTICATION_TYPE_TOKEN:
return std::make_unique<EnrichedMessage>(EnrichedAuthenticationToken(
std::move(es_msg), Enrich(*es_msg->process),
Enrich(es_msg->event.authentication->data.token->instigator)));
case ES_AUTHENTICATION_TYPE_AUTO_UNLOCK:
return std::make_unique<EnrichedMessage>(EnrichedAuthenticationAutoUnlock(
std::move(es_msg), Enrich(*es_msg->process),
UIDForUsername(
StringTokenToStringView(es_msg->event.authentication->data.auto_unlock->username))));

// Note: There is a case here where future OS versions could add new authentication types
// that did not exist in the SDK used to build the current running version of Santa. Return
// nullptr here to signal to the caller that event processing should not continue. Santa
// would have to be updated and rebuilt with the new SDK in order to properly handle the new
// types.
default: return nullptr;
}
case ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN:
return std::make_unique<EnrichedMessage>(EnrichedLoginWindowSessionLogin(
std::move(es_msg), Enrich(*es_msg->process),
Expand Down Expand Up @@ -128,6 +159,11 @@
}
}

std::optional<EnrichedProcess> Enricher::Enrich(const es_process_t *es_proc,
EnrichOptions options) {
return es_proc ? std::make_optional<EnrichedProcess>(Enrich(*es_proc, options)) : std::nullopt;
}

EnrichedProcess Enricher::Enrich(const es_process_t &es_proc, EnrichOptions options) {
return EnrichedProcess(UsernameForUID(audit_token_to_euid(es_proc.audit_token), options),
UsernameForGID(audit_token_to_egid(es_proc.audit_token), options),
Expand Down
19 changes: 13 additions & 6 deletions Source/santad/EventProviders/SNTEndpointSecurityRecorder.mm
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
/// Copyright 2022 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#import "Source/santad/EventProviders/SNTEndpointSecurityRecorder.h"
#include <os/base.h>
Expand Down Expand Up @@ -178,6 +179,11 @@ - (void)handleMessage:(Message &&)esMsg
// data as close to the source event as possible.
std::unique_ptr<EnrichedMessage> enrichedMessage = _enricher->Enrich(std::move(esMsg));

if (!enrichedMessage) {
recordEventMetrics(EventDisposition::kDropped);
return;
}

// Asynchronously log the message
[self processEnrichedMessage:std::move(enrichedMessage)
handler:^(std::unique_ptr<EnrichedMessage> msg) {
Expand All @@ -203,6 +209,7 @@ - (void)enable {
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
events.insert({
ES_EVENT_TYPE_NOTIFY_AUTHENTICATION,
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGIN,
ES_EVENT_TYPE_NOTIFY_LOGIN_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN,
Expand Down
21 changes: 14 additions & 7 deletions Source/santad/EventProviders/SNTEndpointSecurityRecorderTest.mm
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
/// Copyright 2022 Google Inc. All rights reserved.
/// Copyright 2024 North Pole Security, Inc.
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

#include <EndpointSecurity/ESTypes.h>
#import <OCMock/OCMock.h>
Expand Down Expand Up @@ -97,6 +98,7 @@ - (void)testEnable {
#if HAVE_MACOS_13
if (@available(macOS 13.0, *)) {
expectedEventSubs.insert({
ES_EVENT_TYPE_NOTIFY_AUTHENTICATION,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGIN,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOGOUT,
ES_EVENT_TYPE_NOTIFY_LW_SESSION_LOCK,
Expand Down Expand Up @@ -148,7 +150,12 @@ - (void)handleMessageShouldLog:(BOOL)shouldLog
mockESApi->SetExpectationsESNewClient();
mockESApi->SetExpectationsRetainReleaseMessage();

std::unique_ptr<EnrichedMessage> enrichedMsg = std::unique_ptr<EnrichedMessage>(nullptr);
// Create a fake enriched message. It isn't used other than to inject a valid
// returned object from a mocked Enrich method. The purpose is to ensure the
// check in the recorder that a message is enriched always succeeds.
es_message_t fakeEnrichedMsg = MakeESMessage(ES_EVENT_TYPE_NOTIFY_EXIT, NULL);
std::unique_ptr<EnrichedMessage> enrichedMsg = std::make_unique<EnrichedMessage>(EnrichedMessage(
santa::EnrichedExit(Message(mockESApi, &fakeEnrichedMsg), santa::EnrichedProcess())));

auto mockEnricher = std::make_shared<MockEnricher>();

Expand Down
Loading

0 comments on commit 13e4fa2

Please sign in to comment.