#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
+#include <sys/capability.h>
#include <sys/prctl.h>
#include <unistd.h>
// By default, commands are being killed after 30 seconds
#define DEFAULT_TIMEOUT SEC_TO_USEC(30)
+#define MAX_CAPS 8
+
typedef struct td_command_output {
// Pipes
int pipes[2];
// Timeout
uint64_t timeout;
+ // Capabilities
+ cap_value_t caps[MAX_CAPS];
+ unsigned int num_caps;
+
// pidfd
int pidfd;
self->callbacks.on_success_data = data;
}
+// Capabilities
+static int td_command_require_cap(td_command* self, const cap_value_t cap) {
+ // Check if we have space
+ if (self->num_caps >= MAX_CAPS)
+ return -ENOSPC;
+
+ // Stop the capability
+ self->caps[self->num_caps++] = cap;
+
+ return 0;
+}
+
+int td_command_require_caps(td_command* self, const cap_value_t* caps) {
+ int r;
+
+ // Check input
+ if (!caps)
+ return -EINVAL;
+
+ // Apply all caps
+ for (const cap_value_t* cap = caps; *cap; cap++) {
+ r = td_command_require_cap(self, *cap);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int td_command_write(td_command* self,
int fd, unsigned int events, td_command_output* output) {
ssize_t bytes_read = 0;
return 0;
}
+static int td_command_drop_caps(td_command* self) {
+ cap_t caps = NULL;
+ int r;
+
+ // Don't do anything if no caps have been required
+ if (!self->num_caps)
+ return 0;
+
+ // Create a new set of capabilities
+ caps = cap_init();
+ if (!caps) {
+ ERROR(self->ctx, "Failed to create a new set of capabilities: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Set permitted caps
+ r = cap_set_flag(caps, CAP_PERMITTED, self->num_caps, self->caps, CAP_SET);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to set permitted caps: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Set effective caps
+ r = cap_set_flag(caps, CAP_EFFECTIVE, self->num_caps, self->caps, CAP_SET);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to set effective caps: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Set inheritable caps
+ r = cap_set_flag(caps, CAP_INHERITABLE, self->num_caps, self->caps, CAP_SET);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to set inheritable caps: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Set the caps
+ r = cap_set_proc(caps);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to set caps: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ERROR:
+ if (caps)
+ cap_free(caps);
+
+ return r;
+}
+
+static int td_command_set_caps(td_command* self) {
+ int r;
+
+ // Drop all capabilities
+ r = td_command_drop_caps(self);
+ if (r < 0)
+ return r;
+
+ // Clear ambient caps
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to clear ambient caps: %m\n");
+ return -errno;
+ }
+
+ // Now, re-raise ambient caps,
+ // so that the executed command will have the correct capabilities
+ for (unsigned int i = 0; i < self->num_caps; i++) {
+ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, self->caps[i], 0, 0);
+ if (r < 0) {
+ ERROR(self->ctx, "Failed to raise ambient cap %d: %m\n", self->caps[i]);
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
static int td_command_parent(td_command* self) {
int fd = -EBADF;
int r;
td_command_close_pipe(self->stdout.pipes);
td_command_close_pipe(self->stderr.pipes);
- // Execute the command
+ // Set capabilities
+ r = td_command_set_caps(self);
+ ERROR(self->ctx, "CAPS %d\n", r);
+ if (r < 0)
+ return r;
// Execute the command
r = execvp(argv[0], (char**)argv);
#ifndef TELEMETRY_COMMAND_H
#define TELEMETRY_COMMAND_H
+#include <sys/capability.h>
+
typedef struct td_command td_command;
#include "ctx.h"
void td_command_on_success(td_command* self,
td_command_success_callback callback, void* data);
+// Capabilities
+int td_command_require_caps(td_command* self, const cap_value_t* caps);
+
int td_command_execute(td_command* self, const char** argv);
#endif /* TELEMETRY_COMMAND_H */
return td_command_create(command, self->ctx, self->daemon);
}
-int td_source_run_command(td_source* self, const char** argv,
+int td_source_run_command(td_source* self, const cap_value_t* caps, const char** argv,
td_command_success_callback callback, void* data) {
td_command* command = NULL;
int r;
if (r < 0)
goto ERROR;
+ // Set capabilities
+ if (caps) {
+ r = td_command_require_caps(command, caps);
+ if (r < 0)
+ goto ERROR;
+ }
+
// Set the callback
td_command_on_success(command, callback, data);
#ifndef TELEMETRY_SOURCE_H
#define TELEMETRY_SOURCE_H
+#include <sys/capability.h>
+
#include <libudev.h>
#ifdef HAVE_LIBNL3
int td_source_create_metrics(td_source* self, td_metrics** metrics, const char* object);
int td_source_create_command(td_source* self, td_command** command);
-int td_source_run_command(td_source* self, const char** argv,
+int td_source_run_command(td_source* self, const cap_value_t* caps, const char** argv,
td_command_success_callback callback, void* data);
int td_source_parse_metrics(td_source* self, const char* object,
"hostapd_cli", "all_sta", NULL,
};
- return td_source_run_command(source, argv, hostapd_on_success, source);
+ return td_source_run_command(source, NULL, argv, hostapd_on_success, source);
}
const td_source_impl hostapd_source = {
#include "../time.h"
#include "legacy-gateway-latency4.h"
+static const cap_value_t caps[] = {
+ CAP_NET_ADMIN,
+ CAP_NET_RAW,
+ 0,
+};
+
static int fetch_default_gateway(td_ctx* ctx, td_source* source,
char* address, size_t length, unsigned int* type) {
struct nl_cache* routes = NULL;
NULL,
};
- return td_source_run_command(source, argv, legacy_gateway_latency_on_success, source);
+ return td_source_run_command(source, caps, argv, legacy_gateway_latency_on_success, source);
}
static int do_ping(td_ctx* ctx, td_source* source, const char* address) {
NULL,
};
- return td_source_run_command(source, argv, legacy_gateway_latency_on_success, source);
+ return td_source_run_command(source, caps, argv, legacy_gateway_latency_on_success, source);
}
static int legacy_gateway_latency4_heartbeat(td_ctx* ctx, td_source* source) {
"suricatasc", "--command=dump-counters", NULL,
};
- return td_source_run_command(source, argv, suricata_on_success, source);
+ return td_source_run_command(source, NULL, argv, suricata_on_success, source);
}
const td_source_impl suricata_source = {
"unbound-control", "stats_noreset", NULL,
};
- return td_source_run_command(source, argv, unbound_on_success, source);
+ return td_source_run_command(source, NULL, argv, unbound_on_success, source);
}
const td_source_impl unbound_source = {