diff --git a/osquery/events/CMakeLists.txt b/osquery/events/CMakeLists.txt index 980b4fcb1233..22fc486040f8 100644 --- a/osquery/events/CMakeLists.txt +++ b/osquery/events/CMakeLists.txt @@ -47,6 +47,7 @@ function(generateOsqueryEvents) darwin/es_utils.cpp darwin/endpointsecurity.cpp darwin/endpointsecurity_fim.cpp + darwin/endpointsecurity_socketevents.cpp darwin/event_taps.cpp darwin/fsevents.cpp darwin/iokit.cpp diff --git a/osquery/events/darwin/endpointsecurity.h b/osquery/events/darwin/endpointsecurity.h index 44796fc6ecc1..f323280739fc 100644 --- a/osquery/events/darwin/endpointsecurity.h +++ b/osquery/events/darwin/endpointsecurity.h @@ -103,6 +103,25 @@ struct EndpointSecurityFileEventContext : EndpointSecurityEventContext { using EndpointSecurityFileEventContextRef = std::shared_ptr; +struct EndpointSecuritySocketSubscriptionContext : public SubscriptionContext { + std::vector es_socket_event_subscriptions_; + std::vector row_list; +}; + +using EndpointSecuritySocketSubscriptionContextRef = + std::shared_ptr; + +struct EndpointSecuritySocketEventContext + : public EndpointSecurityEventContext { + std::string socket_path; + int domain; + int protocol; + int mode; + int type; +}; +using EndpointSecuritySocketEventContextRef = + std::shared_ptr; + class EndpointSecurityPublisher : public EventPublisher { @@ -200,6 +219,45 @@ class EndpointSecurityFileEventPublisher // clang-format on }; +class EndpointSecuritySocketEventPublisher + : public EventPublisher { + DECLARE_PUBLISHER("endpointsecurity_socketevents"); + + public: + explicit EndpointSecuritySocketEventPublisher( + const std::string& name = "EndpointSecuritySocketEventPublisher") + : EventPublisher() { + runnable_name_ = name; + } + + Status setUp() override API_AVAILABLE(macos(10.15)); + + void configure() override API_AVAILABLE(macos(10.15)); + + void tearDown() override API_AVAILABLE(macos(10.15)); + + Status run() override API_AVAILABLE(macos(10.15)) { + return Status::success(); + } + + bool shouldFire(const EndpointSecuritySocketSubscriptionContextRef& sc, + const EndpointSecuritySocketEventContextRef& ec) + const override API_AVAILABLE(macos(10.15)); + + virtual ~EndpointSecuritySocketEventPublisher() API_AVAILABLE(macos(10.15)) { + tearDown(); + } + + public: + static void handleMessage(const es_message_t* message) + API_AVAILABLE(macos(10.15)); + + private: + es_client_s* es_socket_client_{nullptr}; + bool es_socket_client_success_{false}; +}; + class ESProcessEventSubscriber : public EventSubscriber { public: @@ -225,4 +283,17 @@ class ESProcessFileEventSubscriber const EndpointSecurityFileSubscriptionContextRef& sc) API_AVAILABLE(macos(10.15)); }; + +class ESProcessSocketEventSubscriber + : public EventSubscriber { + public: + ESProcessSocketEventSubscriber() { + setName("es_process_socket_events"); + } + + Status init() override API_AVAILABLE(macos(10.15)); + Status Callback(const EndpointSecuritySocketEventContextRef& ec, + const EndpointSecuritySocketSubscriptionContextRef& sc) + API_AVAILABLE(macos(10.15)); +}; } // namespace osquery diff --git a/osquery/events/darwin/endpointsecurity_socketevents.cpp b/osquery/events/darwin/endpointsecurity_socketevents.cpp new file mode 100644 index 000000000000..03e2c033e09c --- /dev/null +++ b/osquery/events/darwin/endpointsecurity_socketevents.cpp @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2014-present, The osquery authors + * + * This source code is licensed as defined by the LICENSE file found in the + * root directory of this source tree. + * + * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only) + */ + +#include + +#include +#include +#include +#include +#include + +namespace osquery { + +DECLARE_bool(disable_endpointsecurity); +DECLARE_bool(disable_endpointsecurity_socketevents); + +REGISTER(EndpointSecuritySocketEventPublisher, + "event_publisher", + "endpointsecurity_socketevents") + +Status EndpointSecuritySocketEventPublisher::setUp() { + if (__builtin_available(macos 10.15, *)) { + if (FLAGS_disable_endpointsecurity_socketevents) { + return Status::failure( + 1, "EndpointSecurity Socket Events is disabled via configuration"); + } + + auto handler = ^(es_client_t* client, const es_message_t* message) { + handleMessage(message); + }; + + auto result = es_new_client(&es_socket_client_, handler); + + if (result == ES_NEW_CLIENT_RESULT_SUCCESS) { + es_socket_client_success_ = true; + return Status::success(); + } else { + return Status::failure(1, getEsNewClientErrorMessage(result)); + } + } else { + return Status::failure( + 1, "EndpointSecurity is only available on macOS 10.15 and higher"); + } +} + +void EndpointSecuritySocketEventPublisher::configure() { + if (es_socket_client_ == nullptr) { + return; + } + + auto cache = es_clear_cache(es_socket_client_); + if (cache != ES_CLEAR_CACHE_RESULT_SUCCESS) { + VLOG(1) << "Couldn't clear cache for EndpointSecurity client"; + return; + } + + for (auto& sub : subscriptions_) { + auto sc = getSubscriptionContext(sub->context); + auto events = sc->es_socket_event_subscriptions_; + auto es_sub = es_subscribe(es_socket_client_, &events[0], events.size()); + if (es_sub != ES_RETURN_SUCCESS) { + VLOG(1) << "Couldn't subscribe to EndpointSecurity subsystem"; + } + } +} + +void EndpointSecuritySocketEventPublisher::tearDown() { + if (es_socket_client_ == nullptr) { + return; + } + es_unsubscribe_all(es_socket_client_); + + if (es_socket_client_success_) { + auto result = es_delete_client(es_socket_client_); + if (result != ES_RETURN_SUCCESS) { + VLOG(1) << "endpointsecurity: error tearing down es_client"; + } + es_socket_client_ = nullptr; + } +} + +void EndpointSecuritySocketEventPublisher::handleMessage( + const es_message_t* message) { + if (message == nullptr) { + return; + } + + if (message->action_type == ES_ACTION_TYPE_AUTH) { + return; + } + + auto ec = createEventContext(); + + ec->version = message->version; + if (ec->version >= 2) { + ec->seq_num = message->seq_num; + } + + if (ec->version >= 4) { + ec->global_seq_num = message->global_seq_num; + } + + getProcessProperties(message->process, ec); + + switch (message->event_type) { + case ES_EVENT_TYPE_NOTIFY_UIPC_BIND: { + ec->es_event = ES_EVENT_TYPE_NOTIFY_UIPC_BIND; + ec->event_type = "bind"; + + auto dir = getStringFromToken(&message->event.uipc_bind.dir->path); + auto filename = getStringFromToken(&message->event.uipc_bind.filename); + + ec->socket_path = dir + "/" + filename; + + } break; + case ES_EVENT_TYPE_NOTIFY_UIPC_CONNECT: { + ec->es_event = ES_EVENT_TYPE_NOTIFY_UIPC_BIND; + ec->event_type = "connect"; + + const auto& connect_event = message->event.uipc_connect; + ec->socket_path = getStringFromToken(&connect_event.file->path); + + ec->domain = connect_event.domain; + ec->protocol = connect_event.protocol; + ec->type = connect_event.type; + } break; + default: + break; + } + + EventFactory::fire(ec); +} + +bool EndpointSecuritySocketEventPublisher::shouldFire( + const EndpointSecuritySocketSubscriptionContextRef& sc, + const EndpointSecuritySocketEventContextRef& ec) const { + return true; +} + +} // namespace osquery diff --git a/osquery/events/darwin/es_utils.cpp b/osquery/events/darwin/es_utils.cpp index 4b1ebb6e0957..06c94921930a 100644 --- a/osquery/events/darwin/es_utils.cpp +++ b/osquery/events/darwin/es_utils.cpp @@ -17,15 +17,15 @@ namespace osquery { -FLAG(bool, - disable_endpointsecurity, - true, - "Disable receiving events from the EndpointSecurity subsystem"); +CLI_FLAG(bool, + disable_endpointsecurity, + true, + "Disable receiving events from the EndpointSecurity subsystem"); -FLAG(bool, - disable_endpointsecurity_fim, - true, - "Disable file events from the EndpointSecurity subsystem"); +CLI_FLAG(bool, + disable_endpointsecurity_fim, + true, + "Disable file events from the EndpointSecurity subsystem"); FLAG(string, es_fim_mute_path_literal, @@ -40,6 +40,11 @@ FLAG(string, // document performance issues FLAG(bool, es_fim_enable_open_events, false, "Enable open events"); +CLI_FLAG(bool, + disable_endpointsecurity_socketevents, + true, + "Disable socket events from the EndpointSecurity subsystem"); + std::string getEsNewClientErrorMessage(const es_new_client_result_t r) { switch (r) { case ES_NEW_CLIENT_RESULT_ERR_INTERNAL: diff --git a/osquery/tables/events/CMakeLists.txt b/osquery/tables/events/CMakeLists.txt index a2a644d602a2..037900499330 100644 --- a/osquery/tables/events/CMakeLists.txt +++ b/osquery/tables/events/CMakeLists.txt @@ -45,6 +45,7 @@ function(generateOsqueryTablesEventsEventstable) darwin/disk_events.cpp darwin/es_process_events.cpp darwin/es_process_file_events.cpp + darwin/es_process_socket_events.cpp darwin/file_events.cpp darwin/hardware_events.cpp darwin/socket_events.cpp diff --git a/osquery/tables/events/darwin/es_process_socket_events.cpp b/osquery/tables/events/darwin/es_process_socket_events.cpp new file mode 100644 index 000000000000..11e54cf1908b --- /dev/null +++ b/osquery/tables/events/darwin/es_process_socket_events.cpp @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2014-present, The osquery authors + * + * This source code is licensed as defined by the LICENSE file found in the + * root directory of this source tree. + * + * SPDX-License-Identifier: (Apache-2.0 OR GPL-2.0-only) + */ + +#include +#include +#include + +#include +#include +#include +#include + +namespace osquery { + +REGISTER(ESProcessSocketEventSubscriber, + "event_subscriber", + "es_process_socket_events"); + +Status ESProcessSocketEventSubscriber::init() { + if (__builtin_available(macos 10.15, *)) { + auto sc = createSubscriptionContext(); + + sc->es_socket_event_subscriptions_.push_back( + ES_EVENT_TYPE_NOTIFY_UIPC_BIND); + sc->es_socket_event_subscriptions_.push_back( + ES_EVENT_TYPE_NOTIFY_UIPC_CONNECT); + + subscribe(&ESProcessSocketEventSubscriber::Callback, sc); + + return Status::success(); + } else { + return Status::failure(1, "Only available on macOS 10.15 and higher"); + } +} + +Status ESProcessSocketEventSubscriber::Callback( + const EndpointSecuritySocketEventContextRef& ec, + const EndpointSecuritySocketSubscriptionContextRef& sc) { + Row r; + + r["version"] = INTEGER(ec->version); + r["seq_num"] = BIGINT(ec->seq_num); + r["global_seq_num"] = BIGINT(ec->global_seq_num); + + r["event_type"] = ec->event_type; + + r["pid"] = BIGINT(ec->pid); + r["parent"] = BIGINT(ec->parent); + + r["path"] = ec->path; + + r["family"] = INTEGER(ec->domain); + r["protocol"] = INTEGER(ec->protocol); + r["type"] = INTEGER(ec->type); + r["socket"] = SQL_TEXT(ec->socket_path); + + sc->row_list = {r}; + if (!sc->row_list.empty()) { + addBatch(sc->row_list); + } + + return Status::success(); +} +} // namespace osquery diff --git a/specs/CMakeLists.txt b/specs/CMakeLists.txt index f93c7c10bae2..0f4cb17c83e7 100644 --- a/specs/CMakeLists.txt +++ b/specs/CMakeLists.txt @@ -105,6 +105,7 @@ function(generateNativeTables) "darwin/disk_events.table:macos" "darwin/es_process_events.table:macos" "darwin/es_process_file_events.table:macos" + "darwin/es_process_socket_events.table:macos" "darwin/event_taps.table:macos" "darwin/fan_speed_sensors.table:macos" "darwin/gatekeeper.table:macos" diff --git a/specs/darwin/es_process_file_events.table b/specs/darwin/es_process_file_events.table index a022a8beeac8..4866101ae843 100644 --- a/specs/darwin/es_process_file_events.table +++ b/specs/darwin/es_process_file_events.table @@ -5,7 +5,7 @@ schema([ Column("seq_num", BIGINT, "Per event sequence number"), Column("global_seq_num", BIGINT, "Global sequence number"), Column("pid", BIGINT, "Process (or thread) ID"), - Column("parent", BIGINT, "Parent process ID"), + Column("parent", BIGINT, "Parent process ID"), Column("path", TEXT, "Path of executed file"), Column("filename", TEXT, "The source or target filename for the event"), Column("dest_filename", TEXT, "Destination filename for the event"), diff --git a/specs/darwin/es_process_socket_events.table b/specs/darwin/es_process_socket_events.table new file mode 100644 index 000000000000..e5a85dba59d4 --- /dev/null +++ b/specs/darwin/es_process_socket_events.table @@ -0,0 +1,19 @@ +table_name("es_process_socket_events") +description("Track Unix Domain socket binds and connects.") +schema([ + Column("version", INTEGER, "Version of EndpointSecurity event"), + Column("seq_num", BIGINT, "Per event sequence number"), + Column("global_seq_num", BIGINT, "Global sequence number"), + Column("pid", BIGINT, "Process (or thread) ID"), + Column("parent", BIGINT, "Parent process ID"), + Column("path", TEXT, "Path of the process causing the event"), + Column("family", INTEGER, "The Internet protocol family ID"), + Column("protocol", INTEGER, "The network protocol ID"), + Column("type", INTEGER, "The socket type, among SOCK_STREAM(1), SOCK_DGRAM(2), SOCK_RAW(3)"), + Column("socket", TEXT, "The local path (UNIX domain socket only)"), + Column("event_type", TEXT, "Type of EndpointSecurity event, among connect and bind"), + Column("time", BIGINT, "Time of execution in UNIX time", additional=True), + Column("eid", TEXT, "Event ID", hidden=True), +]) +attributes(event_subscriber=True) +implementation("events/darwin/es_process_socket_events@es_process_socket_events::genTable")