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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,35 @@ on:
- main

jobs:
clang-format:
runs-on: ubuntu-latest
container:
image: fedora:latest

# clang-format reads the repository's .clang-format and reflows to its
# ColumnLimit. --dry-run discards the output and --Werror makes clang-format
# exit non-zero if any tracked source file would be changed, so this job
# fails (rather than silently reformatting) whenever the tree drifts.
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Install clang-format
run: |
dnf -y update
dnf -y install clang-tools-extra findutils

# Enumerate sources with find rather than `git ls-files`: actions/checkout
# downloads the tree as a plain archive inside the container (git is not
# present at checkout time), so no .git directory exists for git to read.
# Running from the checkout root makes the repo's .clang-format apply.
- name: Verify formatting
run: find . -type f \( -name '*.c' -o -name '*.h' \) -exec clang-format --dry-run --Werror {} +

build-test:
runs-on: ubuntu-latest
# Compile/test will not run until formatting passes.
needs: clang-format
container:
image: fedora:latest

Expand Down
1 change: 0 additions & 1 deletion src/C/create-empty-client-keytab.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ static void free_buffers(char *keytab, char *keytab_dirname, char *keytab_filena
}
}


int main(void) {
char *keytab = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char));
char *keytab_dirname = calloc(FILE_PATH_MAX_LENGTH + 3, sizeof(char));
Expand Down
12 changes: 4 additions & 8 deletions test/test_client_keytab_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ static int compute_expected(char *buf, size_t n) {
if (snprintf(uid_str, sizeof(uid_str), "%u", (unsigned)getuid()) < 0) {
return -1;
}
if (snprintf(dirname, sizeof(dirname), "%s/%s", __CLIENT_KEYTAB_DIR,
uid_str) < 0) {
if (snprintf(dirname, sizeof(dirname), "%s/%s", __CLIENT_KEYTAB_DIR, uid_str) < 0) {
return -1;
}
if (snprintf(path, sizeof(path), "%s/client.keytab", dirname) < 0) {
Expand All @@ -88,20 +87,17 @@ TEST(binary_prints_expected_path) {
(void)memset(out, 0, sizeof(out));
(void)memset(expected, 0, sizeof(expected));

TEST_ASSERT_EQ(run_binary(out, sizeof(out)), 0,
"binary must exit successfully and print the path");
TEST_ASSERT_EQ(run_binary(out, sizeof(out)), 0, "binary must exit successfully and print the path");

TEST_ASSERT_EQ(compute_expected(expected, sizeof(expected)), 0,
"expected-path computation must succeed");
TEST_ASSERT_EQ(compute_expected(expected, sizeof(expected)), 0, "expected-path computation must succeed");

/* strip the trailing newline captured from stdout */
size_t len = strlen(out);
if (len > 0 && out[len - 1] == '\n') {
out[len - 1] = '\0';
}

TEST_ASSERT_STR_EQ(out, expected,
"binary stdout must equal the derived keytab path");
TEST_ASSERT_STR_EQ(out, expected, "binary stdout must equal the derived keytab path");
}

TEST(binary_output_has_no_extra_lines) {
Expand Down
169 changes: 77 additions & 92 deletions test/test_framework.h

Large diffs are not rendered by default.

102 changes: 51 additions & 51 deletions test/test_kcron_caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,34 +55,34 @@ static void thunks_valid(void) {
* ============================================================================ */

TEST(disable_capabilities_clears_set) {
/* Returns void and exits only on failure; reaching here means success. */
disable_capabilities();
TEST_ASSERT_EQ(1, 1, "disable_capabilities must clear the capability set");
/* Returns void and exits only on failure; reaching here means success. */
disable_capabilities();
TEST_ASSERT_EQ(1, 1, "disable_capabilities must clear the capability set");
}

/* ============================================================================
* print_cap_error
* ============================================================================ */

TEST(print_cap_error_reports_capabilities) {
/*
* With valid pointers this prints the capability name derived from
* cap_to_name(3) and returns; it never exits on the happy path.
*/
print_cap_error("EFFECTIVE", SAMPLE_CAPS, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must format and return");
/*
* With valid pointers this prints the capability name derived from
* cap_to_name(3) and returns; it never exits on the happy path.
*/
print_cap_error("EFFECTIVE", SAMPLE_CAPS, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must format and return");
}

TEST(print_cap_error_null_mode_is_safe) {
/* The NULL mode guard returns without printing. */
print_cap_error(NULL, SAMPLE_CAPS, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must tolerate NULL mode");
/* The NULL mode guard returns without printing. */
print_cap_error(NULL, SAMPLE_CAPS, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must tolerate NULL mode");
}

TEST(print_cap_error_null_array_is_safe) {
/* The NULL array guard returns without printing. */
print_cap_error("EFFECTIVE", NULL, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must tolerate NULL array");
/* The NULL array guard returns without printing. */
print_cap_error("EFFECTIVE", NULL, 1);
TEST_ASSERT_EQ(1, 1, "print_cap_error must tolerate NULL array");
}

/* ============================================================================
Expand All @@ -97,56 +97,56 @@ TEST(print_cap_error_null_array_is_safe) {
* keeping the parent fully able to continue and _exit(0) cleanly.
*/
static void expect_exit(void (*fn)(void)) {
pid_t pid = fork();
if (pid == 0) {
fn();
/* Unreachable: every enable_capabilities path calls exit(). */
_exit(7);
}
int status = 0;
waitpid(pid, &status, 0);
(void)status;
pid_t pid = fork();
if (pid == 0) {
fn();
/* Unreachable: every enable_capabilities path calls exit(). */
_exit(7);
}
int status = 0;
waitpid(pid, &status, 0);
(void)status;
}

TEST(enable_capabilities_null_array_exits) {
/* The NULL-array guard calls exit(EXIT_FAILURE). */
expect_exit(thunks_null);
/* The NULL-array guard calls exit(EXIT_FAILURE). */
expect_exit(thunks_null);
}

TEST(enable_capabilities_zero_count_exits) {
/* The num_caps <= 0 guard calls exit(EXIT_FAILURE). */
expect_exit(thunks_zero);
/* The num_caps <= 0 guard calls exit(EXIT_FAILURE). */
expect_exit(thunks_zero);
}

TEST(enable_capabilities_privilege_required) {
/*
* With a valid array the function raises the permitted/effective sets but
* cap_set_proc(3) fails for an unprivileged process, so it exits via
* print_cap_error("APPLY"). Confirms the documented non-return-on-failure.
*/
expect_exit(thunks_valid);
/*
* With a valid array the function raises the permitted/effective sets but
* cap_set_proc(3) fails for an unprivileged process, so it exits via
* print_cap_error("APPLY"). Confirms the documented non-return-on-failure.
*/
expect_exit(thunks_valid);
}

/* ============================================================================
* Test Runner
* ============================================================================ */

int main(int argc, char **argv) {
int result;

TEST_INIT(10, false, false); /* timeout, verbose, duration */

/* disable_capabilities */
RUN_TEST(disable_capabilities_clears_set);
/* print_cap_error */
RUN_TEST(print_cap_error_reports_capabilities);
RUN_TEST(print_cap_error_null_mode_is_safe);
RUN_TEST(print_cap_error_null_array_is_safe);
/* enable_capabilities */
RUN_TEST(enable_capabilities_null_array_exits);
RUN_TEST(enable_capabilities_zero_count_exits);
RUN_TEST(enable_capabilities_privilege_required);

result = TEST_EXECUTE();
return result;
int result;

TEST_INIT(10, false, false); /* timeout, verbose, duration */

/* disable_capabilities */
RUN_TEST(disable_capabilities_clears_set);
/* print_cap_error */
RUN_TEST(print_cap_error_reports_capabilities);
RUN_TEST(print_cap_error_null_mode_is_safe);
RUN_TEST(print_cap_error_null_array_is_safe);
/* enable_capabilities */
RUN_TEST(enable_capabilities_null_array_exits);
RUN_TEST(enable_capabilities_zero_count_exits);
RUN_TEST(enable_capabilities_privilege_required);

result = TEST_EXECUTE();
return result;
}
61 changes: 20 additions & 41 deletions test/test_kcron_filename.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@
* Recomputes what get_filenames() should produce using getuid() plus the same
* string pieces, so the test does not hardcode the UID or the base directory.
*/
static int compute_expected(char *keytab_dir, char *keytab_filename,
char *keytab) {
static int compute_expected(char *keytab_dir, char *keytab_filename, char *keytab) {
char uid_str[32]; /* 64-bit unsigned IDs fit in 21 characters */

if (snprintf(uid_str, sizeof(uid_str), "%u", (unsigned)getuid()) < 0) {
Expand All @@ -46,12 +45,10 @@ static int compute_expected(char *keytab_dir, char *keytab_filename,
if (snprintf(keytab_filename, FILE_PATH_MAX_LENGTH, "client.keytab") < 0) {
return -1;
}
if (snprintf(keytab_dir, FILE_PATH_MAX_LENGTH, "%s/%s", __CLIENT_KEYTAB_DIR,
uid_str) < 0) {
if (snprintf(keytab_dir, FILE_PATH_MAX_LENGTH, "%s/%s", __CLIENT_KEYTAB_DIR, uid_str) < 0) {
return -1;
}
if (snprintf(keytab, FILE_PATH_MAX_LENGTH, "%s/%s", keytab_dir,
keytab_filename) < 0) {
if (snprintf(keytab, FILE_PATH_MAX_LENGTH, "%s/%s", keytab_dir, keytab_filename) < 0) {
return -1;
}
return 0;
Expand All @@ -67,8 +64,7 @@ TEST(client_dirname_matches_config) {

(void)memset(buf, 0, sizeof(buf));
TEST_ASSERT_EQ(get_client_dirname(buf), 0, "expected success");
TEST_ASSERT_STR_EQ(buf, __CLIENT_KEYTAB_DIR,
"must equal the compiled-in client keytab directory");
TEST_ASSERT_STR_EQ(buf, __CLIENT_KEYTAB_DIR, "must equal the compiled-in client keytab directory");
}

TEST(client_dirname_buffer_has_null_terminator) {
Expand Down Expand Up @@ -96,8 +92,7 @@ TEST(get_filenames_succeeds) {
(void)memset(keytab_dir, 0, sizeof(keytab_dir));
(void)memset(keytab_filename, 0, sizeof(keytab_filename));
(void)memset(keytab, 0, sizeof(keytab));
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0,
"expected success");
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0, "expected success");
}

TEST(get_filenames_filename_is_constant) {
Expand All @@ -108,10 +103,8 @@ TEST(get_filenames_filename_is_constant) {
(void)memset(keytab_dir, 0, sizeof(keytab_dir));
(void)memset(keytab_filename, 0, sizeof(keytab_filename));
(void)memset(keytab, 0, sizeof(keytab));
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0,
"expected success");
TEST_ASSERT_STR_EQ(keytab_filename, "client.keytab",
"filename must always be client.keytab");
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0, "expected success");
TEST_ASSERT_STR_EQ(keytab_filename, "client.keytab", "filename must always be client.keytab");
}

TEST(get_filenames_directory_under_config) {
Expand All @@ -123,17 +116,12 @@ TEST(get_filenames_directory_under_config) {
(void)memset(keytab_dir, 0, sizeof(keytab_dir));
(void)memset(keytab_filename, 0, sizeof(keytab_filename));
(void)memset(keytab, 0, sizeof(keytab));
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0,
"expected success");
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0, "expected success");

dir_len = strlen(keytab_dir);
TEST_ASSERT_EQ(dir_len > strlen(__CLIENT_KEYTAB_DIR), 1,
"directory must be nested below the configured base");
TEST_ASSERT_EQ(strncmp(keytab_dir, __CLIENT_KEYTAB_DIR,
strlen(__CLIENT_KEYTAB_DIR)), 0,
"directory must start with the configured base");
TEST_ASSERT_EQ((int)keytab_dir[strlen(__CLIENT_KEYTAB_DIR)], (int)'/',
"base must be followed by a path separator");
TEST_ASSERT_EQ(dir_len > strlen(__CLIENT_KEYTAB_DIR), 1, "directory must be nested below the configured base");
TEST_ASSERT_EQ(strncmp(keytab_dir, __CLIENT_KEYTAB_DIR, strlen(__CLIENT_KEYTAB_DIR)), 0, "directory must start with the configured base");
TEST_ASSERT_EQ((int)keytab_dir[strlen(__CLIENT_KEYTAB_DIR)], (int)'/', "base must be followed by a path separator");
}

TEST(get_filenames_reproducible) {
Expand All @@ -151,17 +139,12 @@ TEST(get_filenames_reproducible) {
(void)memset(exp_name, 0, sizeof(exp_name));
(void)memset(exp_path, 0, sizeof(exp_path));

TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0,
"get_filenames must succeed");
TEST_ASSERT_EQ(compute_expected(exp_dir, exp_name, exp_path), 0,
"compute_expected must succeed");

TEST_ASSERT_STR_EQ(keytab_dir, exp_dir,
"computed directory must match independent computation");
TEST_ASSERT_STR_EQ(keytab_filename, exp_name,
"computed filename must match independent computation");
TEST_ASSERT_STR_EQ(keytab, exp_path,
"computed path must match independent computation");
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0, "get_filenames must succeed");
TEST_ASSERT_EQ(compute_expected(exp_dir, exp_name, exp_path), 0, "compute_expected must succeed");

TEST_ASSERT_STR_EQ(keytab_dir, exp_dir, "computed directory must match independent computation");
TEST_ASSERT_STR_EQ(keytab_filename, exp_name, "computed filename must match independent computation");
TEST_ASSERT_STR_EQ(keytab, exp_path, "computed path must match independent computation");
}

TEST(get_filenames_path_ends_with_filename) {
Expand All @@ -174,16 +157,12 @@ TEST(get_filenames_path_ends_with_filename) {
(void)memset(keytab_dir, 0, sizeof(keytab_dir));
(void)memset(keytab_filename, 0, sizeof(keytab_filename));
(void)memset(keytab, 0, sizeof(keytab));
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0,
"expected success");
TEST_ASSERT_EQ(get_filenames(keytab_dir, keytab_filename, keytab), 0, "expected success");

path_len = strlen(keytab);
name_len = strlen(keytab_filename);
TEST_ASSERT_EQ((int)(path_len >= name_len), 1,
"path must be at least as long as filename");
TEST_ASSERT_EQ(strncmp(keytab + (path_len - name_len), keytab_filename,
name_len), 0,
"path must end with the keytab filename");
TEST_ASSERT_EQ((int)(path_len >= name_len), 1, "path must be at least as long as filename");
TEST_ASSERT_EQ(strncmp(keytab + (path_len - name_len), keytab_filename, name_len), 0, "path must end with the keytab filename");
}

/* ============================================================================
Expand Down
9 changes: 3 additions & 6 deletions test/test_kcron_keytab.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,16 @@ TEST(write_empty_keytab_writes_exact_magic) {
return;
}

TEST_ASSERT_EQ(write_empty_keytab(filedescriptor), 0,
"expected successful write of magic bytes");
TEST_ASSERT_EQ(write_empty_keytab(filedescriptor), 0, "expected successful write of magic bytes");

(void)lseek(filedescriptor, 0, SEEK_SET);
nread = read(filedescriptor, readback, sizeof(readback));
(void)close(filedescriptor);

TEST_ASSERT_EQ(nread, 2, "must read exactly the two magic bytes");
if (nread == 2) {
TEST_ASSERT_EQ((int)readback[0], (int)EXPECTED_MAGIC[0],
"first magic byte must be the version (0x05)");
TEST_ASSERT_EQ((int)readback[1], (int)EXPECTED_MAGIC[1],
"second magic byte must be the format (0x02)");
TEST_ASSERT_EQ((int)readback[0], (int)EXPECTED_MAGIC[0], "first magic byte must be the version (0x05)");
TEST_ASSERT_EQ((int)readback[1], (int)EXPECTED_MAGIC[1], "second magic byte must be the format (0x02)");
}
}

Expand Down
Loading
Loading