Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
50b655b
daemon/unit: split out socket handling in u_service
quitschbo Nov 20, 2025
766aafe
daemon/cmld: Introduce cmld_init_stage_unit_notify()
quitschbo Nov 20, 2025
05a5344
daemon/scd: use cmld_init_stage_unit_notify()
quitschbo Nov 20, 2025
001f4ae
daemon/cmld: make cmld_init_stage_containers() static
quitschbo Nov 20, 2025
9da285b
daemon/unit: Introduce u_time submodule
quitschbo Nov 20, 2025
bd433ce
daemon/control: include status of units in protobuf responses
quitschbo Nov 20, 2025
6831c5a
control/control: control list -s to include system services
quitschbo Nov 20, 2025
07a76cd
control/control: fixed formatting of retrieve_logs in print_usage()
quitschbo Nov 21, 2025
3cdba25
daemon/tss: start tpm2d as unit with auto reconnect
quitschbo Nov 20, 2025
63bc81d
daemon/mount: Introduce mount_idmapped()
quitschbo Nov 20, 2025
10fa9b6
daemon/u_idmapped: Introduce u_idmapped module
quitschbo Nov 20, 2025
b494330
daemon/u_user: do not chown and chmod for idmapped mounts
quitschbo Nov 20, 2025
c7f1a37
daemon/u_perm: added header guards
quitschbo Nov 21, 2025
d77d6e6
daemon/u_user: added header guards
quitschbo Nov 21, 2025
c3a68b8
daemon/c_service: remove unused container.proto import
quitschbo Nov 22, 2025
3742836
daemon/container Introduce container_scd_connect handler
quitschbo Nov 26, 2025
626a0c3
daemon/c_smartcard: Implement the container_scd_connect handler
quitschbo Nov 26, 2025
8f3af2c
daemon/scd: notify containers about (re-)connection of the scd
quitschbo Nov 26, 2025
430b100
daemon: removed deprecated c_shiftid submodule
quitschbo Nov 24, 2025
4275594
daemon/u_net: set proper fs type to "sysfs" in u_net_start_child()
quitschbo Nov 27, 2025
548df00
common/dir: Provide dir_chown_folder() helper for recursively chown
quitschbo Nov 28, 2025
e8cc17d
daemon/cmld: ensure data directory in CML is root owned
quitschbo Nov 28, 2025
f7f7fdd
daemon/u_user: Make use of common dir_chown_folder()
quitschbo Nov 28, 2025
348d72f
daemon/unit: run compartment_new() late in unit_new()
quitschbo Jan 8, 2026
06b2af8
daemon/u_idmapped: ensure unit data directory in CML is root owned
quitschbo Jan 8, 2026
f95f603
daemon/unit: Allow setting sock_name and data_path to NULL
quitschbo Nov 28, 2025
05925c0
control/control: fix memory leek in receive loop of 'control run'
quitschbo Jan 13, 2026
fcd689e
daemon/audit: do not use temporary files for hashing records
quitschbo Jan 13, 2026
1d63aec
daemon/u_usb: added hot-plugging module for units
quitschbo Jan 19, 2026
71a5d91
daemon/scd: start unit of scd in private netns
quitschbo Jan 8, 2026
0c89f75
daemon/c_fifo: fixed race on container start and fifo creation in c0
quitschbo Jan 13, 2026
6fb41f8
daemon/unit: Fixed memory leak in unit_new() while searching the binary
quitschbo Jan 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions common/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,71 @@ dir_copy_folder(const char *source, const char *target,
dir_copy_params_free(params);
return ret;
}

struct chown_data {
uid_t uid;
gid_t gid;
};

static int
dir_chown_contents_cb(const char *path, const char *file, void *data)
{
struct stat s;
int ret = 0;
struct chown_data *cb_data = data;
ASSERT(cb_data);

char *file_to_chown = mem_printf("%s/%s", path, file);
if (lstat(file_to_chown, &s) == -1) {
mem_free0(file_to_chown);
return -1;
}

uid_t uid = cb_data->uid;
gid_t gid = cb_data->gid;

if (file_is_dir(file_to_chown)) {
TRACE("Path %s is dir", file_to_chown);
if (dir_foreach(file_to_chown, &dir_chown_contents_cb, cb_data) < 0) {
ERROR_ERRNO("Could not chown all dir contents in '%s' to (%d:%d)",
file_to_chown, uid, gid);
ret--;
}
if (chown(file_to_chown, uid, gid) < 0) {
Comment thread
MPeisl marked this conversation as resolved.
ERROR_ERRNO("Could not chown dir '%s' to (%d:%d)", file_to_chown, uid, gid);
ret--;
}
} else {
if (lchown(file_to_chown, uid, gid) < 0) {
ERROR_ERRNO("Could not chown file '%s' to (%d:%d)", file_to_chown, uid,
gid);
ret--;
}
}
TRACE("Chown file '%s' to (%d:%d)", file_to_chown, uid, gid);

// chown .
if (chown(path, uid, gid) < 0) {
ERROR_ERRNO("Could not chown dir '%s' to (%d:%d)", path, uid, gid);
ret--;
}
mem_free0(file_to_chown);
return ret;
}

int
dir_chown_folder(const char *path, uid_t uid, gid_t gid)
{
struct stat s;
IF_TRUE_RETVAL(stat(path, &s), -1);

int ret = 0;

struct chown_data cb_data = { .uid = uid, .gid = gid };
if (dir_foreach(path, &dir_chown_contents_cb, &cb_data) < 0) {
ERROR("Could not chown %s to uid:gid (%d:%d)", path, uid, gid);
ret--;
}

return ret;
Comment thread
MPeisl marked this conversation as resolved.
}
11 changes: 11 additions & 0 deletions common/dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,15 @@ int
dir_copy_folder(const char *source, const char *target,
bool (*filter)(const char *file, void *data), void *filter_data);

/**
* Chown all files and subdirectories in a directory recursively.
*
* @param path The path of the directory.
* @param uid uid to be set in the directory.
* @param gid gid to be set in the directory.
* @returns 0 on success, -1 on error.
*/
int
dir_chown_folder(const char *path, uid_t uid, gid_t gid);

#endif /* DIR_H */
52 changes: 44 additions & 8 deletions control/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ print_usage(const char *cmd)
printf("Usage: %s [-s <socket file>] <command> [<command args>]\n", cmd);
printf("\n");
printf("commands:\n");
printf(" list\n"
" Lists all containers.\n\n");
printf(" list [-s | --system]\n"
" Lists all containers. If 'system' option is set, include system services (units)\n\n");
printf(" list_guestos\n"
" Lists all installed guestos configs.\n\n");
printf(" reload\n"
Expand Down Expand Up @@ -133,8 +133,9 @@ print_usage(const char *cmd)
printf(" run <container-uuid> <command> [<arg_1> ... <arg_n>]\n"
" Runs the specified command with the given arguments inside the specified container.\n\n");
printf(" retrieve_logs [<path_to_logstore_dir>] [--remove]\n"
" Retrieves logs from the directory defined in LOGFILE_DIR and stores them in the given directory.\n"
"If the 'remove' option was given, all log files are removed upon successful retrieval. The currrently used log file is not removed.\n\n");
" Retrieves logs from the directory defined in LOGFILE_DIR and stores them in the\n"
" given directory. If the 'remove' option was given, all log files are removed upon\n"
" successful retrieval. The currrently used log file is not removed.\n\n");
printf("\n");
exit(-1);
}
Expand Down Expand Up @@ -196,11 +197,15 @@ get_container_usb_pin_entry(uuid_t *uuid, int sock)
}

static uuid_t *
get_container_uuid_new(const char *identifier, int sock)
get_container_uuid_new(const char *identifier, int sock, bool system)
{
uuid_t *valid_uuid = NULL;
ControllerToDaemon msg = CONTROLLER_TO_DAEMON__INIT;
msg.command = CONTROLLER_TO_DAEMON__COMMAND__GET_CONTAINER_STATUS;
if (system) {
msg.has_system_services = true;
msg.system_services = true;
}
send_message(sock, &msg);

DaemonToController *resp = recv_message(sock);
Expand Down Expand Up @@ -248,6 +253,8 @@ static const struct option assign_iface_options[] = { { "iface", required_argume

static const struct option log_options[] = { { "remove", no_argument, 0, 'r' }, { 0, 0, 0, 0 } };

static const struct option list_options[] = { { "system", no_argument, 0, 's' }, { 0, 0, 0, 0 } };

static char *
get_password_new(const char *prompt)
{
Expand Down Expand Up @@ -316,6 +323,24 @@ main(int argc, char *argv[])
*/
if (!strcasecmp(command, "list")) {
msg.command = CONTROLLER_TO_DAEMON__COMMAND__GET_CONTAINER_STATUS;
// parse specific options for list command
optind--;
char **list_argv = &argv[optind];
int list_argc = argc - optind;
optind = 0; // reset optind to scan command-specific options
for (int c, option_index = 0;
-1 !=
(c = getopt_long(list_argc, list_argv, "s", list_options, &option_index));) {
switch (c) {
case 's':
msg.has_system_services = true;
msg.system_services = true;
break;
default:
print_usage(argv[0]);
ASSERT(false); // never reached
}
}
goto send_message;
}
if (!strcasecmp(command, "list_guestos")) {
Expand Down Expand Up @@ -550,7 +575,17 @@ main(int argc, char *argv[])
print_usage(argv[0]);

sock = sock_connect(socket_file);
uuid = get_container_uuid_new(argv[optind], sock);

if (!strcasecmp(command, "state")) {
uuid = get_container_uuid_new(argv[optind], sock, true);
msg.command = CONTROLLER_TO_DAEMON__COMMAND__GET_CONTAINER_STATUS;
msg.n_container_uuids = 1;
msg.container_uuids = mem_new(char *, 1);
msg.container_uuids[0] = mem_strdup(uuid_string(uuid));
goto send_message;
}

uuid = get_container_uuid_new(argv[optind], sock, false);

ContainerStartParams container_start_params = CONTAINER_START_PARAMS__INIT;
if (!strcasecmp(command, "remove")) {
Expand Down Expand Up @@ -637,8 +672,6 @@ main(int argc, char *argv[])
msg.command = CONTROLLER_TO_DAEMON__COMMAND__CONTAINER_ALLOWAUDIO;
} else if (!strcasecmp(command, "deny_audio")) {
msg.command = CONTROLLER_TO_DAEMON__COMMAND__CONTAINER_DENYAUDIO;
} else if (!strcasecmp(command, "state")) {
msg.command = CONTROLLER_TO_DAEMON__COMMAND__GET_CONTAINER_STATUS;
} else if (!strcasecmp(command, "config")) {
msg.command = CONTROLLER_TO_DAEMON__COMMAND__GET_CONTAINER_CONFIG;
} else if (!strcasecmp(command, "update_config")) {
Expand Down Expand Up @@ -877,13 +910,16 @@ main(int argc, char *argv[])
}
fflush(stdout);
}
protobuf_free_message((ProtobufCMessage *)resp);
} else if (resp->code == DAEMON_TO_CONTROLLER__CODE__EXEC_END) {
TRACE("[CLIENT] Got notification of command termination. Exiting...");
kill(pid, SIGTERM);
waitpid(pid, NULL, 0);
protobuf_free_message((ProtobufCMessage *)resp);
goto exit;
} else {
ERROR("Detected unexpected message from cmld. Exiting");
protobuf_free_message((ProtobufCMessage *)resp);
goto exit;
}
}
Expand Down
16 changes: 6 additions & 10 deletions daemon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ AGGRESSIVE_WARNINGS ?= y
SANITIZERS ?= n
WCAST_ALIGN ?= y
OCI ?= n
IDMAPPED ?= y
CGROUP_V2 ?= y
CGROUP_SOCKOPT ?= n
SYSTEMD ?= n
Expand Down Expand Up @@ -126,7 +125,11 @@ SRC_FILES := main.c \

SRC_UMODULES += \
u_user.c \
u_net.c
u_idmapped.c \
u_service.c \
u_net.c \
u_time.c \
u_usb.c

ifeq ($(UNIT_MODULE_PERM),y)
SRC_UMODULES += \
Expand All @@ -139,15 +142,8 @@ endif
SRC_CMODULES := \
c_hotplug.c \
c_smartcard.c \
c_user.c

ifeq ($(IDMAPPED),y)
SRC_CMODULES += \
c_user.c \
c_idmapped.c
else
SRC_CMODULES += \
c_shiftid.c
endif

ifeq ($(SYSTEMD),y)
SRC_CMODULES += \
Expand Down
51 changes: 7 additions & 44 deletions daemon/audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,46 +159,26 @@ audit_remaining_storage(const char *uuid)
}

static void
audit_send_record_cb(const char *hash_string, const char *hash_file,
audit_send_record_cb(const char *hash_string, const unsigned char *hash_buf, size_t hash_buf_len,
UNUSED crypto_hashalgo_t hash_algo, void *data)
{
uint8_t *buf = NULL;
const container_t *c = (const container_t *)data;
ASSERT(c);

// callback is triggert twice for cleanup
IF_NULL_RETURN_TRACE(hash_string);

if (!hash_file) {
ERROR("audit_send_record_cb: hash_file was empty");
return;
}

if (!data) {
ERROR("audit_send_record_cb: No container given");
return;
}

uint32_t buf_len = file_size(hash_file);
buf = mem_alloc0(buf_len);

int read = file_read(hash_file, (char *)buf, buf_len);

if (read < 0 || buf_len != (unsigned int)read) {
ERROR("Processing SCD response: read %u bytes, expected %u", read, buf_len);
goto out;
}

TRACE("Got hash from SCD for file %s: %s", hash_file, hash_string);

if (unlink(hash_file)) {
ERROR_ERRNO("Failed to unlink %s", hash_file);
}
TRACE("Got hash from SCD for record: %s", hash_string);

char *old_acked = mem_strdup(container_audit_get_last_ack(c));
container_audit_set_last_ack(c, hash_string);

if (0 > container_audit_record_send(c, buf, buf_len)) {
if (0 > container_audit_record_send(c, hash_buf, hash_buf_len)) {
ERROR("Failed to send audit record to container");
// rollback
container_audit_set_last_ack(c, old_acked);
Expand All @@ -212,7 +192,6 @@ audit_send_record_cb(const char *hash_string, const char *hash_file,

out:
container_audit_set_processing_ack(c, false);
mem_free0(buf);
}

static AuditRecord *
Expand Down Expand Up @@ -408,7 +387,7 @@ audit_write_file(const uuid_t *uuid, const AuditRecord *msg)
int audit_file_lock = -1;
int fd = -1;

if (!file_is_dir(AUDIT_LOGDIR) && dir_mkdir_p(AUDIT_LOGDIR, 0600)) {
if (!file_is_dir(AUDIT_LOGDIR) && dir_mkdir_p(AUDIT_LOGDIR, 0700)) {
Comment thread
MPeisl marked this conversation as resolved.
ERROR("Failed to create logdir");
return -1;
}
Expand Down Expand Up @@ -515,17 +494,6 @@ audit_do_send_record(const container_t *c)
int ret = -1;
int fd = -1;

char tmpfile[strlen(AUDIT_LOGDIR) + 14];
if (0 >= sprintf(tmpfile, "%s/%s", AUDIT_LOGDIR, "audit_XXXXXX")) {
ERROR_ERRNO("Failed to prepare temporary filename");
return -1;
}

if (-1 == (fd = mkstemp(tmpfile))) {
ERROR_ERRNO("Failed to generate temporary filename");
return -1;
}

CmldToServiceMessage *message_proto = mem_new0(CmldToServiceMessage, 1);
cmld_to_service_message__init(message_proto);
message_proto->code = CMLD_TO_SERVICE_MESSAGE__CODE__AUDIT_RECORD;
Expand All @@ -543,18 +511,13 @@ audit_do_send_record(const container_t *c)
goto out;
}

if (-1 == fd_write(fd, (char *)packed, packed_len)) {
ERROR("Failed to write packed message to file.");
goto out;
}

TRACE("Requesting scd to hash serialized protobuf message at %s", tmpfile);
TRACE("Requesting scd to hash serialized protobuf message");

// this state is needed s.t. additional ACKs from container arriving while scd is hashing the file
// this state is needed s.t. additional ACKs from container arriving while scd is hashing a record
// do not lead to multiple transmissions of the same record
container_audit_set_processing_ack(c, true);

if (crypto_hash_file(tmpfile, AUDIT_HASH_ALGO, audit_send_record_cb, (void *)c)) {
if (crypto_hash_buf(packed, packed_len, AUDIT_HASH_ALGO, audit_send_record_cb, (void *)c)) {
container_audit_set_processing_ack(c, false);
str_t *dump = str_hexdump_new((unsigned char *)packed, (int)packed_len);
ERROR("Failed to request hashing of record to be sent with length %u: %s.",
Expand Down
31 changes: 30 additions & 1 deletion daemon/c_fifo.c
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,35 @@ c_fifo_start_post_clone(void *fifop)
return ret;
}

static int
c_fifo_start_pre_exec_child(void *fifop)
{
c_fifo_t *fifo = fifop;
ASSERT(fifo);

int ret = -COMPARTMENT_ERROR_FIFO;
container_t *c0 = cmld_containers_get_c0();

if (fifo->container != c0)
return 0;

/*
* avoid race where container is already started and the corresponding
* fifo dir in c0 is not ready yet.
*/
DEBUG("Creating dir for FIFOs in c0");

if (dir_mkdir_p(FIFO_PATH, 0755) < 0 && errno != EEXIST) {
ERROR_ERRNO("Could not create fifo parent dir at %s", FIFO_PATH);
ret = -COMPARTMENT_ERROR_FIFO;
} else {
DEBUG("Created dir for FIFOs at path %s", FIFO_PATH);
ret = 0;
}

return ret;
}

static int
c_fifo_stop(void *fifop)
{
Expand Down Expand Up @@ -438,7 +467,7 @@ static compartment_module_t c_fifo_module = {
.start_post_exec = NULL,
.start_child = NULL,
.start_pre_exec_child_early = NULL,
.start_pre_exec_child = NULL,
.start_pre_exec_child = c_fifo_start_pre_exec_child,
.stop = c_fifo_stop,
.cleanup = c_fifo_cleanup,
.join_ns = NULL,
Expand Down
Loading