Hello,
My application monitors ES_EVENT_TYPE_NOTIFY_CLOSE. If a file is dragged to another location in Finder, the Endpoint Security reports the event ES_EVENT_TYPE_NOTIFY_CLOSE was performed by '/usr/libexec/xpcproxy'. So, xpcproxy is the process that performed ES_EVENT_TYPE_NOTIFY_CLOSE.
Looks like the dragged file is copied by some XPC service.
I have found the audit user id is equal to user who dragged a file.
Can audit user id be used to identify a user who triggers copy file action in this case? If no, are there any way to define such info?
Thank you in advance!
Endpoint Security
RSS for tagDevelop system extensions that enhance user security using Endpoint Security.
Posts under Endpoint Security tag
79 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
I'm developing a file access control system. In order to protect the file content copied out, I'm finding a way to deny user copy file content to other files.
I know there are data transmission between the copied application and pboard service by XPC. But I don't know how to interrupt the data transmission. Or I can do something to stop the copied data send to the Clipboard.
So is there any way to prevent the contents of a file being copied?
I added ES_EVENT_TYPE_AUTH_SIGNAL to the event list, and added logging:
os_log_debug(esfLogger, "antitampering signal %d from process %{public}s to process %{public}s", esm.signal, signing.UTF8String, targetSigning.UTF8String);
I get some logs, such as
2024-12-09 10:21:47.668034+0000 0xc2c562 Debug 0x0 29448 0 DopeMonitorService: [security.dope:anti-tamper] antitampering signal 0 from process com.apple.spindump to process com.apple.mds_stores
But when I do sudo kill -9 ${ourappprocess}, the proess dies with no log generated. (This is a different process than the one using ESF; the goal is, obviously, to keep our processes from being killed, but I'm only at the logging stage so far.)
sudo kill -INFO ${ourappprocess} works:
2024-12-09 10:21:38.410851+0000 0xc2c562 Debug 0x0 29448 0 Monitor: [debug:anti-tamper] antitampering signal 29 from process com.apple.csh to process Worker
So it is getting through to the monitoring process. But kill -9 ... isn't. Am I missing something obvious again?
I try to do unit test for an ES system extension's code using Unit Testing Bundle.
the code tries to get NSEndpointSecurityMachServiceName in info.plist by [bundle objectForInfoDictionaryKey:] but failed.
I have added NSEndpointSecurityMachService to info.plist of Test Bundle, and I did check info.plist file in bundle, NSEndpointSecurityMachServiceName was listed there.
Hello,
Let's say I have several opened user sessions in parallel.
Endpoint Security notify about executing a process (ES_EVENT_TYPE_NOTIFY_EXEC) and provide audit token.
The goal is to find relationship between logged-in users and new process.
Can I use audit user ID for this?
Thank you in advance.
Hi, i'm working on an endpoint security extension loader and implement several callbacks from delegate object
OSSystemExtensionRequestDelegate
the callback i'm interested in is :
public func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result);
public func requestNeedsUserApproval(_ request: OSSystemExtensionRequest);
I've noticed that if I manually approve the extension long time after it was activated, the extension process goes up, but the callback isn't being called. The requestNeedUserApproval callback always gets called.
I see in the unified logs that when the extension goes from
activated_waiting_for_user -> activated_enabling -> activated_enabled
Than the request callback doesn't get called.
But whenever the extension goes
activated_waiting_for_user -> activated_disabled
The request callback gets called. (this is counter intuitive since we expected the state activated_disabled may hint that the extension failed to be activated somehow)
Any Idea why my callback doesn't gets called if the extension gets approved long after it was activated ?
I have a process [command line cpp application] which i want to run always such as it should relaunch after a crash, after device startup etc.
I created a launchd Property List File with KeepAlive true and placed under /Library/LaunchDaemons.
Problem Statements:
I have a bash script to start and stop this process.
start using: launchctl bootstrap.
stop involve these two steps:
send SIGTERM signal and wait untill process stops after doing some cleanups
launchctl bootout [It doesn't sends SIGTERM]
during steps 1 - Process is getting stop, but also getting immediate relaunch by launchctl
during step 2 - it getting stop again.
is there a proper way so that we can disable KeepAlive temporarily so that process will not launch during step 1?
or suggest other ways to handle this?
I'm working on a system extension leveraging endpoint security entitlement. However, while in development, is there a way to continue working and testing locally without having the endpoint security entitlement approved or needing the extension signed.
I got these errors running a build:
Provisioning profile "Mac Team Provisioning Profile: "com.xxxxx.extension" doesn't include the com.apple.developer.endpoint-security.client entitlement.
Firstly, I want to keep my GUI app available for download on the Mac App Store, which means I need to provide the Endpoint Security functionality in some other executable, such as a daemon.
I'm going to use a User Agent for user-aware background processing.
Instead of the typical use of adding an Endpoint Security system extension to a macOS app, could I instead add the sysex to my User Agent bundle?
I like the benefits of coupling the sysex to the client that will interact with it, and this would also allow me to not have to worry about managing a daemon. I could simply provide the User Agent in a downloadable installer signed with my Developer ID certificate.
Trying to flesh out an idea for an application which would rely on Endpoint Security Framework and Network Extension Framework, where intend the application to:
Forward certain ESF events to a backend (on a separate server)
Forward certain Unified logs to a backend (on a separate server)
Forwarding various DNS queries and responses (on a separate server)
Retrieve configuration from the backend to set Network Extension Filters
Are there any limitations and/or reasons not to bundle all this functionality into a single system extension?
I know of other applications where system extension is very thin and main application (daemon) communicates over xpc with the system extension, would this be considered best practice?
We’re encountering issues with a system extension that subscribes to multiple events. Some users are experiencing performance problems when running our extension alongside other system extensions like Microsoft Defender and Crowdstrike, which seem to generate a high volume of events. However, on certain Macs with an identical setup, there are no performance issues, making it difficult to pinpoint the cause.
Has anyone found ways to improve compatibility with other system extensions? Currently, we’re ignoring and caching events from other extensions to avoid unnecessary processing.
The specific ES events contributing to the issue seem to be:
• ES_EVENT_TYPE_AUTH_EXEC
• ES_EVENT_TYPE_AUTH_OPEN
I realize this is a broad question, but the documentation for endpoint security extensions is quite limited. Any insights or suggestions would be greatly appreciated!
Hey folks,
I developed a DLP program based on Endpoint Security for the enterprise, and everything functioned normally. I also applied for the development permission of Endpoint Security before, which took 3 months. Now I want to distribute the software internally, so I tried to apply for a certificate for distribution permission. After waiting for 3 months, Apple told me that the permission was rejected.
This is the replay content:
Thank you for your interest in Endpoint Security. After carefulconsideration, we regret that we're unable to approve your request at this time. If you'd like to submit another request for this capability, please review andconfirm that your app details and justification meet the criteria before resubmittting.
Rejecting duplicate request.
Apple Developer Relations
I don't know what's wrong, what should I do to get distribution or developer id permissions.
I try to mix content filter and endpoint security in one system extension, but get error below when the program invoke es_new_client(returned ES_NEW_CLIENTRESULT_ERR_INTERNAL).
Failed to open services: 0xe00002e2: Caller was denied connecting to the ES subsystem, possibly due to a sandbox violation.
how to solve this error while keeping two functionalities in one system extension?
or I have to seperate them?
I think there's a slight discrepancy between what is being communicated in EndpointSecurity docs, and what is really happening.
For example, consider the description of this event:
https://developer.apple.com/documentation/endpointsecurity/es_event_type_t/es_event_type_notify_truncate?language=objc
"ES_EVENT_TYPE_NOTIFY_TRUNCATE: An identifier for a process that notifies endpoint security that it is truncating a file."
But, it seems that this event is fired up only when truncate(2) is called, not when process truncates a file (which can be done in lots of different ways). But the documentation doesn't even mention that it's only about the truncate(2) call, it's impossible to know.
Another example:
https://developer.apple.com/documentation/endpointsecurity/es_event_type_t/es_event_type_notify_copyfile?language=objc
"ES_EVENT_TYPE_NOTIFY_COPYFILE: An identifier for a process that notifies endpoint security that it is copying a file."
It seems that this event is only called when copyfile(3) syscall is called. But the docs doesn't mention that syscall at all. The wording suggests that the event should be emitted on every file copy operation, which is probably impossible to detect.
I mean, I get that you'd like the docs to be "easy to digest", but I think that such working confuses people. They expect one thing, then they get confusing behavior from ES, because it doesn't match their expectations, and after reaching out to Apple they get concise and clear answer -- but it would be easier for everyone (including Apple devs) when this answer would be included directly in the official docs for the framework.
The kernel sends SIGKILL to application if it handles ES_EVENT_TYPE_AUTH_OPEN and lldb is attached to this process.
App:
int main(int /*argc*/, char** /*argv*/)
{
es_client_t *pEpClient = nullptr;
es_new_client_result_t result = es_new_client(&pEpClient, ^(es_client_t *pClient, const es_message_t *pMessage)
{
switch (pMessage->event_type)
{
case ES_EVENT_TYPE_AUTH_OPEN:
{
uint32_t authorizedFlags = pMessage->event.open.fflag;
if ((authorizedFlags & FREAD) || (authorizedFlags & FWRITE))
{
std::filesystem::path filePath = std::string(pMessage->event.open.file->path.data, pMessage->event.open.file->path.length);
std::string fileName = filePath.filename();
if (fileName == "test.txt")
{
std::cout << "blocked fileName: " << filePath.filename() << std::endl;
authorizedFlags &= ~FWRITE;
authorizedFlags &= ~FREAD;
}
}
if (es_respond_flags_result(pClient, pMessage, authorizedFlags, false) != ES_RESPOND_RESULT_SUCCESS)
{
std::cout << "es_respond_flags_result() failed with error " << std::endl;
}
}
break;
default:
break;
}
});
if (result != ES_NEW_CLIENT_RESULT_SUCCESS)
{
std::cout << "es_new_client() failed." << std::endl;
return 1;
}
es_event_type_t eventsList[] =
{
ES_EVENT_TYPE_AUTH_OPEN
};
if (es_subscribe(pEpClient, eventsList, 1) == ES_RETURN_ERROR)
{
std::cout << "es_subscribe() failed." << std::endl;
}
// wait
int i = 0;
std::cin >> i;
if (es_delete_client(pEpClient) == ES_RETURN_ERROR)
{
std::cout << "es_delete_client() failed." << std::endl;
}
return 0;
}
(lldb) process attach --pid 61127
....
(lldb) c
Process 61127 resuming
Process 61127 exited with status = 9 (0x00000009) Terminated due to signal 9
System log:
Allowing set_exception_ports from [debugserver] on [ep_sample] for entitled process/debugger
Client did not respond in appropriate amount of time (client pid: 61127), sent SIGKILL
I'm seeing some odd behavior which may be a bug. I've broken it down to a least common denominator to reproduce it. But maybe I'm doing something wrong.
I am opening a file read-write. I'm then mapping the file read-only and private:
void* pointer = mmap(NULL, 17, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
I then unmap the memory and close the file. After the close, eslogger shows me this:
{"close":{"modified":false,[...],"was_mapped_writable":false}}
Which makes sense.
I then change the mmap statement to:
void* pointer = mmap(NULL, 17, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
I run the new code and and the close looks like:
{"close":{"modified":false, [....], "was_mapped_writable":true}}
Which also makes sense.
I then run the original again (ie, with MAP_PRIVATE vs. MAP_SHARED) and the close looks like:
{"close":{"modified":false,"was_mapped_writable":true,[...]}
Which doesn't appear to be correct.
Now if I just open and close the file (again, read-write) and don't mmap anything the close still shows:
{"close":{ [...], "was_mapped_writable":true,"modified":false}}
And the same is true if I open the file read-only.
It will remain that way until I delete the file. If I recreate the file and try again, everything is good until I map it MAP_SHARED.
I tried this with macOS 13.6.7 and macOS 15.0.1.
Good day. As part of a business unit separation, we are required to have our product with a different name, bundle IDs and certificates than our current configuration.
The product contains network extensions and requires Full Disk Access. We distribute this product to our customers who either support MDM or not.
I know from previous experience that a product can be transferred to a different account, which is something we could do only for some parts of our product (only a couple of Bundle IDs).
My question is what's the best way to do this. I can imagine that having a scripted scenario where the other business unit's product is removed from customers and ours is installed, in a different folder.
The main issue I can foresee is that because our architecture uses several network extensions that are installed as plugins (bad design I know), we would be asking the users for authorisation, again, to use those extensions, plus full disk access.
What options do I have?
In the man page for the eslogger tool, there is a reference to the jq tool.
Postprocess the output in a shell pipeline with jq:
% sudo eslogger exec | jq -r 'select(.process.executable.path ==
"/bin/zsh")|"(.process.audit_token.pid): (.process.executable.path) -(.event.exec.target.executable.path)"'
The problem is that the jq tool is not installed by default with macOS.
[Q] Isn't the idea that the man page should only reference tools that are part of the standard macOS distribution (or can be downloaded and installed by the OS when you try to run them, like with some developer tools)?
I've developed a Endpoint Security system extension, which will be installed in a container APP.
I use XPC to send message from container APP to the ES client, it works fine.
I have developed an Endpoint Security system extension that will be installed in a container app.
I utilize XPC to send messages from the container app to the ES client, and it functions properly. However, when I attempt to send messages from the ES client to the container app, it always displays an error: 'Couldn’t communicate with a helper application.'.
I have removed the sandbox capability of the container app and also employed the same app group for both the ES client and the container app. When an XPC client is connected, I use the following code in the ES client to establish two-way communication.
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(NXFileGuardXPCProtocol)];
NXFileGuardXPCService *xpcService = [NXFileGuardXPCService sharedInstance];
newConnection.exportedObject = xpcService;
// To APP container client (As remote interface)
newConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(NXFileGuardXPCClientProtocol)];
[newConnection activate];
self.containerAPPConnection = newConnection;
return YES;
}
But it always fails. How can I deal with this error?
Hi Team,
In previous macOS version, We were using this link to open system extension permission page programmatically for our swift app. x-apple.systempreferences:com.apple.preference.security?General
In macos 15 (Sequoia), this pane is moved to system settings-> general->login Items and extensions->end point security extensions which is a modal/popup.
Can you please share what should be link to open exact this popup for asking permissions.It appears when you click on i button against end point security extensions
Based on apple script I could find following link but it opens login item & extensions pane, I want the next popup as above screenshot.
"x-apple.systempreferences:com.apple.LoginItems-Settings.extension?extensionItems™