Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion storm-core/src/native/worker-launcher/impl/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ int main(int argc, char **argv) {
oci_launch_cmd* olc = parse_oci_launch_cmd(command_file);
if (olc == NULL) {
exit_code = INVALID_CONFIG_FILE;
} else if (strcmp(olc->username, user_name) != 0) {
} else if (!oci_launch_cmd_matches_user(olc, user_name)) {
// The launch command file's username must match the user passed to
// the worker-launcher on the command line.
fprintf(ERRORFILE, "ERROR: OCI command file username %s does not match %s\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ bool is_valid_mount_source(const char* source) {
return allowed;
}

bool oci_launch_cmd_matches_user(const oci_launch_cmd* olc, const char* user_name) {
return olc != NULL && olc->username != NULL && user_name != NULL
&& strcmp(olc->username, user_name) == 0;
}

static bool is_valid_mount(const cJSON* mount) {
if (!cJSON_IsObject(mount)) {
fputs("ERROR: OCI config mount entry is not an object\n", ERRORFILE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,11 @@ bool is_mount_source_under(const char* source, const char* allowed);
*/
bool is_valid_mount_source(const char* source);

/**
* Return true if the launch command's username is present and equals
* user_name. Used to check that a launch command file's username matches the
* user the worker-launcher was invoked for.
*/
bool oci_launch_cmd_matches_user(const oci_launch_cmd* olc, const char* user_name);

#endif /* OCI_OCI_LAUNCH_CMD_H */
78 changes: 78 additions & 0 deletions storm-core/src/native/worker-launcher/test/test-worker-launcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,80 @@ void test_get_values_degenerate() {
printf("get_values degenerate-value handling OK\n");
}

// oci_launch_cmd_matches_user backs the check in main.c that a launch command
// file's username matches the user the worker-launcher was invoked for.
void test_oci_launch_cmd_matches_user() {
oci_launch_cmd olc;
memset(&olc, 0, sizeof(olc));
olc.username = "alice";
EXPECT(oci_launch_cmd_matches_user(&olc, "alice"), "matching username rejected");
EXPECT(!oci_launch_cmd_matches_user(&olc, "bob"), "mismatched username accepted");
olc.username = NULL;
EXPECT(!oci_launch_cmd_matches_user(&olc, "alice"), "command with no username accepted");
EXPECT(!oci_launch_cmd_matches_user(NULL, "alice"), "null command accepted");
}

// Write a launch command file that is valid except for the bind-mount source,
// which is set to mount_source.
static void write_olc_file(const char* path, const char* mount_source) {
FILE* f = fopen(path, "w");
EXPECT(f != NULL, "could not write launch command file");
fprintf(f,
"{\n"
" \"username\": \"olcuser\",\n"
" \"containerId\": \"85afb30b-286e-4d32-ab7a-9d5aad89bb88\",\n"
" \"pidFile\": \"" TEST_ROOT "/olc/pid\",\n"
" \"containerScriptPath\": \"" TEST_ROOT "/olc/script.sh\",\n"
" \"reapLayerKeepCount\": 0,\n"
" \"layers\": [ { \"mediaType\": \"application/vnd.squashfs\", \"path\": \"/layer\" } ],\n"
" \"ociRuntimeConfig\": {\n"
" \"linux\": { \"cgroupsPath\": \"/storm\" },\n"
" \"process\": { \"args\": [\"/bin/true\"], \"cwd\": \"/\", \"env\": [\"A=B\"] },\n"
" \"mounts\": [ { \"type\": \"bind\", \"source\": \"%s\", \"destination\": \"/dst\", \"options\": [\"rbind\", \"rprivate\"] } ]\n"
" }\n"
"}\n", mount_source);
fclose(f);
}

// parse_oci_launch_cmd runs the bind-mount allow-list check (is_valid_mount ->
// is_valid_mount_source) as part of validation, so a launch command whose mount
// source is outside the configured directories must fail to parse. The source
// is resolved with realpath, so build a real tree; run in a child so the
// temporary config does not leak into later tests.
void test_oci_parse_launch_cmd_mounts() {
const char* base = TEST_ROOT "/olc";
const char* allowed = TEST_ROOT "/olc/allowed";
const char* good_src = TEST_ROOT "/olc/allowed/mount.conf";
const char* bad_src = TEST_ROOT "/olc/outside.conf";
EXPECT(mkdir(base, 0755) == 0 || errno == EEXIST, "could not create olc base");
EXPECT(mkdir(allowed, 0755) == 0 || errno == EEXIST, "could not create allowed dir");
FILE* g = fopen(good_src, "w"); EXPECT(g != NULL, "could not create mount.conf"); fclose(g);
FILE* b = fopen(bad_src, "w"); EXPECT(b != NULL, "could not create outside.conf"); fclose(b);

const char* cfg = TEST_ROOT "/olc/wl.cfg";
FILE* c = fopen(cfg, "w");
EXPECT(c != NULL, "could not write wl.cfg");
fprintf(c, "min.user.id=%d\n", getuid());
fprintf(c, "worker.launcher.oci.allowed.mount.source.dirs=%s\n", allowed);
fclose(c);
read_config(cfg);

const char* good_cmd = TEST_ROOT "/olc/good.json";
const char* bad_cmd = TEST_ROOT "/olc/bad.json";
write_olc_file(good_cmd, good_src);
write_olc_file(bad_cmd, bad_src);

oci_launch_cmd* olc = parse_oci_launch_cmd(good_cmd);
EXPECT(olc != NULL, "launch command with an allowed mount source rejected");
free_oci_launch_cmd(olc);

// the key case: the mount hookup must reject a source outside the allowed dirs
olc = parse_oci_launch_cmd(bad_cmd);
EXPECT(olc == NULL, "launch command with a mount source outside the allowed dirs accepted");

printf("parse_oci_launch_cmd mount-source enforcement OK\n");
}

int main(int argc, char **argv) {
LOGFILE = stdout;
ERRORFILE = stderr;
Expand Down Expand Up @@ -496,7 +570,11 @@ int main(int argc, char **argv) {
printf("\nTesting mount path helpers\n");
test_mount_path_helpers();

printf("\nTesting oci_launch_cmd_matches_user\n");
test_oci_launch_cmd_matches_user();

run_test_in_child("test_mount_source_allowed_dirs", test_mount_source_allowed_dirs);
run_test_in_child("test_oci_parse_launch_cmd_mounts", test_oci_parse_launch_cmd_mounts);

run_test_in_child("test_signal_container", test_signal_container);
run_test_in_child("test_signal_container_group", test_signal_container_group);
Expand Down
Loading