Skip to content

Commit

Permalink
Add ancestry annotator
Browse files Browse the repository at this point in the history
  • Loading branch information
kubalaguna committed Dec 9, 2024
1 parent 737525b commit 9de4798
Show file tree
Hide file tree
Showing 17 changed files with 310 additions and 12 deletions.
1 change: 1 addition & 0 deletions Source/common/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//:helper.bzl", "santa_unit_test")

Expand Down
10 changes: 5 additions & 5 deletions Source/common/santa.proto
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ message FileDescriptor {
// Process information
message ProcessInfo {
// Process ID of the process
optional ProcessID id = 1;
optional santa.pb.v1.ProcessID id = 1;

// Process ID of the parent process
optional ProcessID parent_id = 2;
optional santa.pb.v1.ProcessID parent_id = 2;

// Process ID of the process responsible for this one
optional ProcessID responsible_id = 3;
optional santa.pb.v1.ProcessID responsible_id = 3;

// Original parent ID, remains stable in the event a process is reparented
optional int32 original_parent_pid = 4;
Expand Down Expand Up @@ -181,10 +181,10 @@ message ProcessInfo {
// Light variant of ProcessInfo message to help minimize on-disk/on-wire sizes
message ProcessInfoLight {
// Process ID of the process
optional ProcessID id = 1;
optional santa.pb.v1.ProcessID id = 1;

// Process ID of the parent process
optional ProcessID parent_id = 2;
optional santa.pb.v1.ProcessID parent_id = 2;

// Original parent ID, remains stable in the event a process is reparented
optional int32 original_parent_pid = 3;
Expand Down
2 changes: 1 addition & 1 deletion Source/santad/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ objc_library(
"//Source/common:Unit",
"//Source/santad/ProcessTree:process_tree",
"//Source/santad/ProcessTree/annotations:originator",
"//Source/santad/ProcessTree/annotations:ancestry",
"@MOLXPCConnection",
],
)
Expand Down Expand Up @@ -840,7 +841,6 @@ macos_bundle(
],
entitlements = select({
"//:adhoc_build": "com.google.santa.daemon.systemextension-adhoc.entitlements",
# Non-adhoc builds get their entitlements from the provisioning profile.
"//conditions:default": None,
}),
infoplists = ["Info.plist"],
Expand Down
1 change: 1 addition & 0 deletions Source/santad/Logs/EndpointSecurity/Writers/FSSpool/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//:helper.bzl", "santa_unit_test")

Expand Down
1 change: 1 addition & 0 deletions Source/santad/ProcessTree/BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("//:helper.bzl", "santa_unit_test")

Expand Down
24 changes: 24 additions & 0 deletions Source/santad/ProcessTree/annotations/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ cc_library(
],
)

cc_library(
name = "ancestry",
srcs = ["ancestry.cc"],
hdrs = ["ancestry.h"],
deps = [
":annotator",
"//Source/santad/ProcessTree:process",
"//Source/santad/ProcessTree:process_tree",
"//Source/santad/ProcessTree:process_tree_cc_proto",
"@com_google_absl//absl/container:flat_hash_map",
],
)

santa_unit_test(
name = "originator_test",
srcs = ["originator_test.mm"],
Expand All @@ -35,3 +48,14 @@ santa_unit_test(
"//Source/santad/ProcessTree:process_tree_test_helpers",
],
)

santa_unit_test(
name = "ancestry_test",
srcs = ["ancestry_test.mm"],
deps = [
":ancestry",
"//Source/santad/ProcessTree:process",
"//Source/santad/ProcessTree:process_tree_cc_proto",
"//Source/santad/ProcessTree:process_tree_test_helpers",
],
)
74 changes: 74 additions & 0 deletions Source/santad/ProcessTree/annotations/ancestry.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/// Copyright 2023 Google LLC
///
/// 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
///
/// https://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.
#include "Source/santad/ProcessTree/annotations/ancestry.h"

#include "Source/santad/ProcessTree/process.h"
#include "Source/santad/ProcessTree/process_tree.h"
#include "Source/santad/ProcessTree/process_tree.pb.h"
#include "absl/container/flat_hash_map.h"

namespace ptpb = ::santa::pb::v1::process_tree;

namespace santa::santad::process_tree {

::santa::pb::v1::process_tree::Annotations::Ancestry
AncestryAnnotator::getAncestry() const {
::santa::pb::v1::process_tree::Annotations::Ancestry ancestry;
ancestry.CopyFrom(ancestry_);
return ancestry;
}

void AncestryAnnotator::AddProcessToAncestry(
ptpb::Annotations::Ancestry &ancestry, const Process &process) {
ptpb::AncestryProcessID *ancestor = ancestry.add_ancestor();
ancestor->set_pid(process.pid_.pid);
ancestor->set_secondary_id(process.creation_timestamp);
}

void AncestryAnnotator::AnnotateFork(ProcessTree &tree, const Process &parent,
const Process &child) {
ptpb::Annotations::Ancestry ancestry;

// If parent process has ancestry annotation, copy and add parent.
if (auto parent_annotation = tree.GetAnnotation<AncestryAnnotator>(parent)) {
ancestry.CopyFrom((*parent_annotation)->getAncestry());
AddProcessToAncestry(ancestry, parent);
// Otherwise, get all ancestors of the child and add them.
} else {
std::vector<ptpb::AncestryProcessID> ancestors = tree.GetAncestors(child);
// Add ancestors starting from the root process
for (auto it = ancestors.rbegin(); it != ancestors.rend(); it++) {
ptpb::AncestryProcessID *ancestor = ancestry.add_ancestor();
ancestor->set_pid(it->pid());
ancestor->set_secondary_id(it->secondary_id());
}
}
tree.AnnotateProcess(child, std::make_shared<AncestryAnnotator>(ancestry));
}

void AncestryAnnotator::AnnotateExec(ProcessTree &tree,
const Process &orig_process,
const Process &new_process) {
// Do not annotate process on exec
return;
}

std::optional<ptpb::Annotations> AncestryAnnotator::Proto() const {
auto annotation = ptpb::Annotations();
auto *ancestry_ptr = annotation.mutable_ancestry();
ancestry_ptr->CopyFrom(AncestryAnnotator::getAncestry());
return annotation;
}

} // namespace santa::santad::process_tree
50 changes: 50 additions & 0 deletions Source/santad/ProcessTree/annotations/ancestry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// Copyright 2023 Google LLC
///
/// 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
///
/// https://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.
#ifndef SANTA__SANTAD_PROCESSTREE_ANNOTATIONS_ANCESTRY_H
#define SANTA__SANTAD_PROCESSTREE_ANNOTATIONS_ANCESTRY_H

#include <optional>

#include "Source/santad/ProcessTree/annotations/annotator.h"
#include "Source/santad/ProcessTree/process.h"
#include "Source/santad/ProcessTree/process_tree.pb.h"

namespace santa::santad::process_tree {

class AncestryAnnotator : public Annotator {
public:
// clang-format off
AncestryAnnotator() {}
explicit AncestryAnnotator(
::santa::pb::v1::process_tree::Annotations::Ancestry ancestry)
: ancestry_(ancestry) {};
// clang-format on
void AnnotateFork(ProcessTree &tree, const Process &parent,
const Process &child) override;
void AnnotateExec(ProcessTree &tree, const Process &orig_process,
const Process &new_process) override;
std::optional<::santa::pb::v1::process_tree::Annotations> Proto()
const override;
::santa::pb::v1::process_tree::Annotations::Ancestry getAncestry() const;

private:
void AddProcessToAncestry(
::santa::pb::v1::process_tree::Annotations::Ancestry &ancestry,
const Process &process);
::santa::pb::v1::process_tree::Annotations::Ancestry ancestry_;
};

} // namespace santa::santad::process_tree

#endif
98 changes: 98 additions & 0 deletions Source/santad/ProcessTree/annotations/ancestry_test.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/// Copyright 2023 Google LLC
///
/// 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
///
/// https://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.

#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>

#include "Source/santad/ProcessTree/annotations/ancestry.h"
#include "Source/santad/ProcessTree/process.h"
#include "Source/santad/ProcessTree/process_tree.pb.h"
#include "Source/santad/ProcessTree/process_tree_test_helpers.h"

using namespace santa::santad::process_tree;
namespace ptpb = ::santa::pb::v1::process_tree;

@interface AncestryAnnotatorTest : XCTestCase
@property std::shared_ptr<ProcessTreeTestPeer> tree;
@property std::shared_ptr<const Process> initProc;
@end

@implementation AncestryAnnotatorTest

- (void)setUp {
std::vector<std::unique_ptr<Annotator>> annotators;
annotators.emplace_back(std::make_unique<AncestryAnnotator>());
self.tree = std::make_shared<ProcessTreeTestPeer>(std::move(annotators));
self.initProc = self.tree->InsertInit();
}

- (void)testSingleFork_childHasAncestryAnnotation {
uint64_t event_id = 1;
// PID 1.1: fork() -> PID 2.2
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
self.tree->HandleFork(event_id++, *self.initProc, child_pid);

auto child = *self.tree->Get(child_pid);
auto annotation_opt = self.tree->GetAnnotation<AncestryAnnotator>(*child);
XCTAssertTrue(annotation_opt.has_value());
auto proto_opt = (*annotation_opt)->Proto();

XCTAssertTrue(proto_opt.has_value());
XCTAssertEqual(proto_opt->ancestry().ancestor_size(), 1);
XCTAssertEqual(proto_opt->ancestry().ancestor().Get(0).pid(), 1);
XCTAssertEqual(proto_opt->ancestry().ancestor().Get(0).pidversion(), 1);
}

- (void)testDoubleFork_grandchildHasAncestryAnnotation {
uint64_t event_id = 1;
// PID 1.1: fork() -> PID 2.2 fork() -> PID 3.3
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
const struct Pid grandchild_pid = {.pid = 3, .pidversion = 3};

self.tree->HandleFork(event_id++, *self.initProc, child_pid);
auto child = *self.tree->Get(child_pid);
self.tree->HandleFork(event_id++, *child, grandchild_pid);

auto grandchild = *self.tree->Get(grandchild_pid);
auto annotation_opt = self.tree->GetAnnotation<AncestryAnnotator>(*grandchild);
XCTAssertTrue(annotation_opt.has_value());
auto grandchild_proto_opt = (*annotation_opt)->Proto();
XCTAssertTrue(grandchild_proto_opt.has_value());
auto grandchild_proto = *grandchild_proto_opt;
XCTAssertEqual(grandchild_proto.ancestry().ancestor_size(), 2);
XCTAssertEqual(grandchild_proto.ancestry().ancestor().Get(0).pid(), 1);
XCTAssertEqual(grandchild_proto.ancestry().ancestor().Get(0).pidversion(), 1);
XCTAssertEqual(grandchild_proto.ancestry().ancestor().Get(1).pid(), 2);
XCTAssertEqual(grandchild_proto.ancestry().ancestor().Get(1).pidversion(), 2);
}

- (void)testExec_noEffectOnAncestryAnnotation() {
uint64_t event_id = 1;

// PID 1.1: fork() -> PID 2.2
const struct Pid child_pid = {.pid = 2, .pidversion = 2};
self.tree->HandleFork(event_id++, *self.initProc, child_pid);
auto child = *self.tree->Get(child_pid);
auto annotation_opt = self.tree->GetAnnotation<AncestryAnnotator>(*child);
XCTAssertTrue(annotation_opt.has_value());
auto proto_opt = (*annotation_opt)->Proto();

// PID 2.2: exec("/usr/bin/login") -> PID 2.3
const struct Pid login_exec_pid = {.pid = 2, .pidversion = 3};
const struct Program login_prog = {.executable = "/usr/bin/login", .arguments = {}};
auto login = *self.tree->Get(login_pid);
self.tree->HandleExec(event_id++, *login, login_exec_pid, login_prog, cred);
}

@end
5 changes: 4 additions & 1 deletion Source/santad/ProcessTree/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ class Process {
public:
explicit Process(const Pid pid, const Cred cred,
std::shared_ptr<const Program> program,
std::shared_ptr<const Process> parent)
std::shared_ptr<const Process> parent,
const uint64_t creation_timestamp)
: pid_(pid),
effective_cred_(cred),
program_(program),
creation_timestamp(creation_timestamp),
annotations_(),
parent_(parent),
refcnt_(0),
Expand All @@ -92,6 +94,7 @@ class Process {
const struct Pid pid_;
const struct Cred effective_cred_;
const std::shared_ptr<const Program> program_;
const uint64_t creation_timestamp;

private:
// This is not API.
Expand Down
Loading

0 comments on commit 9de4798

Please sign in to comment.