Monitoring security policy events
Functions provided in the security policy event library (libsecpolev) allow you to monitor system trace events that relate to the successful or failed use of privileges covered by security policies. It allows you write a program that performs a similar function to the secpolmonitor utility.
For detailed information about the functions and structures that
libsecpolev provides, go to The libsecpolev API (libsecpolev.h).
Starting event monitoring and getting event information
To capture events, first call secpolev_init() to initialise libsecpolev and start event collection. Then, call secpolev_wait_event() in a loop to wait for and process each event. Each time the call is complete, secpolev_wait_event() returns the type of the next queued event.
- secpolev_get_process_name()
- secpolev_get_process_type()
- secpolev_get_ability_info()
- secpolev_get_path_info()
- secpolev_get_custom_perm_info()
Stopping event monitoring
To terminate event monitoring, call secpolev_shutdown().
This function takes one of the following shutdown types:
- If secpolev_shutdown() is called from the thread that also called secpolev_wait_event(), use SECPOLEV_SHUTDOWN_IMMEDIATE to shut down monitoring immediately and invalidate any strings returned from functions such as secpolev_get_process_type().
- If secpolev_shutdown() is called from a different thread than the one that called secpolev_wait_event(), call either SECPOLEV_SHUTDOWN_WAIT or SECPOLEV_SHUTDOWN_NOWAIT. The next call the monitoring thread makes to secpolev_wait_event() returns SECPOLEV_EVENT_END and if it is currently blocked in that function, it is unblocked.
Event buffering, buffer sizing, and dropped events
A monitoring program's handling of security events can affect the timing of the programs being monitored. To avoid this issue, events are buffered by the kernel and read by the monitoring program later. The kernel releases the buffers to the monitoring program when they are full or explicitly flushed by the monitoring program.
You can specify a flush interval when you call secpolev_init(). If the value is non-zero, libsecpolev automatically requests a flush if no events have been received before the specified time has elapsed. This interval limits how long the receipt of events can be delayed from when they were generated.
The monitoring program can also flush events at other times by calling secpolev_flush(), which makes all currently logged events immediately available.
A call to secpolev_init() specifies the number of trace buffers to use. Using multiple trace buffers helps prevent events from being lost when the system is generating them faster than the monitoring program can deal with them. Each trace buffer requires 16k of memory. Requesting too many buffers wastes memory, while having too few risks losing events.
To help you configure the number of buffers, libsecpolev includes a secpolev_get_dropped_count() function. This function returns the number of trace event fragments that have been lost because the buffers were full. After a call to secpolev_init(), the number returned by secpolev_get_dropped_count() can only increase. Usually you want to configure the number of buffers so that the dropped count is always zero. It is a good idea to provide at least a few extra buffers since the exact behavior can vary.
PROCMGR_AID_TRACE ability
A program needs the PROCMGR_AID_TRACE ability to use the libsecpolev API. Because tools such as secpolgenerate and secpolmonitor can't discover this requirement, you have to manually give it this ability in the security policies.
Restrictions
The libsecpolev API can only be used by one program at a time, and you can't use it at the same time as tracelogger or other programs that use system trace events.
During development, you can use both secpolgenerate and the libsecpolev API simultaneously (because the system trace events are forwarded to libsecpolev by secpolgenerate). This exception also permits the use of tracelogger, but the libsecpolev API doesn't report any events while tracelogger is running.
Sample program
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <inttypes.h>
#include <string.h>
#include <secpol/secpolev.h>
int
main(int argc, char **argv)
{
if ( secpolev_init(0, 4, 4000) == -1 ) {
printf("secpolev_init failed:%s\n", strerror(errno));
}
while ( true ) {
unsigned flags;
const char *proc_name;
secpolev_event_type_t type;
type = secpolev_wait_event(&flags);
proc_name = secpolev_get_process_name();
if ( proc_name == NULL ) {
// Ignore unnamed processes. These will arise from exec*()
// calls.
continue;
}
printf("%s %s (%s)",
((flags & SECPOL_EVENT_FLAG_DENIED) != 0u) ? "DENIED" : "OK",
proc_name, secpolev_get_process_type());
if ( type == SECPOLEV_EVENT_ABILITY_TEST ) {
unsigned id;
const char *able_name;
unsigned able_flags;
uint64_t start;
uint64_t end;
if ( secpolev_get_ability_info(&id, &able_name, &able_flags,
&start, &end) == 0 ) {
printf(" ability: %s %s",
((able_flags & SECPOLEV_ABILITY_ROOT) == 0u) ?
"non-root" : "root",
able_name);
if ( (able_flags & SECPOLEV_ABILITY_RANGED) != 0u ) {
printf(" (%" PRIu64 "-%" PRIu64 ")", start, end);
}
fputc((int)'\n', stdout);
}
} else if ( (type == SECPOLEV_EVENT_PATH_ATTACH) ||
(type == SECPOLEV_EVENT_PATH_LINK) ) {
const char *path;
secpolev_path_event_status_t status;
if ( secpolev_get_path_info(&path, &status) == 0 ) {
const char *status_str;
switch ( status ) {
case SECPOLEV_PATH_EVENT_OK:
status_str = "";
break;
case SECPOLEV_PATH_EVENT_CONFLICT:
status_str = "conflict";
break;
case SECPOLEV_PATH_EVENT_BAD_OP:
status_str = "bad_op";
break;
case SECPOLEV_PATH_EVENT_NO_RULE:
status_str = "no_rule";
break;
default:
status_str = "???";
break;
}
printf(" %s: %s%s\n",
(type == SECPOLEV_EVENT_PATH_ATTACH) ?
"attach" : "link", status_str, path);
}
} else if ( type == SECPOLEV_EVENT_CUSTOM_PERM ) {
const char *class_name;
const char *perm_name;
const char *obj_type;
if ( secpolev_get_custom_perm_info(&class_name, &perm_name,
&obj_type) == 0 ) {
printf(" custom: %s:%s for type %s\n", class_name, perm_name,
obj_type);
}
} else {
fputc((int)'\n', stdout);
}
}
}