From 9ba999a535b32a0b344ae7f5f0fd40f0eb565562 Mon Sep 17 00:00:00 2001 From: Kevin Day Date: Thu, 21 Apr 2022 23:09:50 -0500 Subject: [PATCH] Update: Add unit tests for f_environment, make changes to f_environment, and add new function f_environment_secure_is(). Add the unit tests for f_environment. The new function f_environment_secure_is() brings in libcap dependency requirements to f_environment. This is added to add support for something similar to secure_getenv() rather than wrapping a non-standard method. I am still not very experience with capabilities and do not guarantee an accurate replacement for secure_getenv(). Replace F_valid_not error returns with F_parameter error returns. --- level_0/f_environment/c/environment.c | 45 +++++- level_0/f_environment/c/environment.h | 45 +++++- level_0/f_environment/data/build/defines | 1 + .../f_environment/data/build/dependencies-tests | 3 + level_0/f_environment/data/build/settings | 4 +- level_0/f_environment/data/build/settings-mocks | 75 +++++++++ level_0/f_environment/data/build/settings-tests | 54 +++++++ level_0/f_environment/data/build/testfile | 45 ++++++ .../f_environment/tests/unit/c/mock-environment.c | 113 ++++++++++++++ .../f_environment/tests/unit/c/mock-environment.h | 55 +++++++ .../tests/unit/c/test-environment-clear.c | 32 ++++ .../tests/unit/c/test-environment-clear.h | 27 ++++ .../tests/unit/c/test-environment-exists.c | 45 ++++++ .../tests/unit/c/test-environment-exists.h | 34 +++++ .../tests/unit/c/test-environment-get.c | 84 ++++++++++ .../tests/unit/c/test-environment-get.h | 43 ++++++ .../tests/unit/c/test-environment-secure_is.c | 169 +++++++++++++++++++++ .../tests/unit/c/test-environment-secure_is.h | 27 ++++ .../tests/unit/c/test-environment-set.c | 70 +++++++++ .../tests/unit/c/test-environment-set.h | 34 +++++ .../tests/unit/c/test-environment-unset.c | 62 ++++++++ .../tests/unit/c/test-environment-unset.h | 34 +++++ .../f_environment/tests/unit/c/test-environment.c | 59 +++++++ .../f_environment/tests/unit/c/test-environment.h | 77 ++++++++++ 24 files changed, 1232 insertions(+), 5 deletions(-) create mode 100644 level_0/f_environment/data/build/dependencies-tests create mode 100644 level_0/f_environment/data/build/settings-mocks create mode 100644 level_0/f_environment/data/build/settings-tests create mode 100644 level_0/f_environment/data/build/testfile create mode 100644 level_0/f_environment/tests/unit/c/mock-environment.c create mode 100644 level_0/f_environment/tests/unit/c/mock-environment.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-clear.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-clear.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-exists.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-exists.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-get.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-get.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-secure_is.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-secure_is.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-set.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-set.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment-unset.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment-unset.h create mode 100644 level_0/f_environment/tests/unit/c/test-environment.c create mode 100644 level_0/f_environment/tests/unit/c/test-environment.h diff --git a/level_0/f_environment/c/environment.c b/level_0/f_environment/c/environment.c index 114cc5f..48346d6 100644 --- a/level_0/f_environment/c/environment.c +++ b/level_0/f_environment/c/environment.c @@ -69,6 +69,47 @@ extern "C" { } #endif // _di_f_environment_get_ +#ifndef _di_f_environment_secure_is_ + f_status_t f_environment_secure_is(void) { + + if (geteuid() == getuid() && getegid() == getgid()) { + return F_true; + } + + #ifndef _di_libcap_ + cap_t capability = cap_get_proc(); + + if (!capability) { + if (errno == EINVAL) return F_status_set_error(F_parameter); + if (errno == EPERM) return F_status_set_error(F_prohibited); + if (errno == ENOMEM) return F_status_set_error(F_memory_not); + + return F_status_set_error(F_failure); + } + + cap_flag_value_t value; + + memset(&value, 0, sizeof(cap_flag_value_t)); + + if (cap_get_flag(capability, CAP_SETUID, CAP_EFFECTIVE, &value) == -1) { + cap_free(capability); + + if (errno == EINVAL) return F_status_set_error(F_parameter); + + return F_status_set_error(F_failure); + } + + cap_free(capability); + + if (value == CAP_SET) { + return F_true; + } + #endif // _di_libcap_ + + return F_false; + } +#endif // _di_f_environment_secure_is_ + #ifndef _di_f_environment_set_ f_status_t f_environment_set(const f_string_static_t name, const f_string_static_t value, const bool replace) { @@ -77,7 +118,7 @@ extern "C" { } if (setenv(name.string, value.string, replace) < 0) { - if (errno == EINVAL) return F_status_set_error(F_valid_not); + if (errno == EINVAL) return F_status_set_error(F_parameter); if (errno == ENOMEM) return F_status_set_error(F_memory_not); return F_status_set_error(F_failure); @@ -95,7 +136,7 @@ extern "C" { } if (unsetenv(name.string) < 0) { - if (errno == EINVAL) return F_status_set_error(F_valid_not); + if (errno == EINVAL) return F_status_set_error(F_parameter); if (errno == ENOMEM) return F_status_set_error(F_memory_not); return F_status_set_error(F_failure); diff --git a/level_0/f_environment/c/environment.h b/level_0/f_environment/c/environment.h index 018ab01..b536bf5 100644 --- a/level_0/f_environment/c/environment.h +++ b/level_0/f_environment/c/environment.h @@ -12,7 +12,13 @@ // Libc includes. #include +#include #include +#include + +#ifndef _di_libcap_ + #include +#endif // _di_libcap_ // FLL-0 includes. #include @@ -97,6 +103,39 @@ extern "C" { #endif // _di_f_environment_get_ /** + * Check to see if the environment is secure for calling getenv() safely for "secure execution". + * + * This is intended to closely mimic the checks secure_getenv(). + * + * Any of these conditions must be true for secure environment: + * - The process' effective UID matches the real UID and the effective GID matches the real GID. + * - The process has the effective CAP_SETUID set. + * + * The documentation for secure_getenv() is unclear on which capabilities are expected to be set. + * This takes a conservative approach and only returns true for the above mentioned capabilities. + * + * @return + * F_true if the environment is secure according to the described rules. + * F_false if the environment is not secure according to the described rules. + * + * F_memory_not (with error bit) on out of memory. + * F_parameter (with error bit) if name is an invalid string. + * F_prohibited (with error bit) if the file system does not permit this operation (usually due to the cap_get_proc() call). + * + * F_failure (with error bit) on any other error. + * + * @see cap_get_flag() + * @see cap_get_proc() + * @see getegid() + * @see geteuid() + * @see getgid() + * @see getuid() + */ +#ifndef _di_f_environment_secure_is_ + extern f_status_t f_environment_secure_is(void); +#endif // _di_f_environment_secure_is_ + +/** * Assign the given value to the named environment variable. * * If the name does not exist, then it is created. @@ -117,7 +156,8 @@ extern "C" { * F_data_not if name.used is 0. * * F_memory_not (with error bit) on out of memory. - * F_valid_not (with error bit) if name is an invalid string. + * F_parameter (with error bit) if name is an invalid string. + * * F_failure (with error bit) on any other error. * * @see setenv() @@ -139,7 +179,8 @@ extern "C" { * F_data_not if name.used is 0. * * F_memory_not (with error bit) on out of memory. - * F_valid_not (with error bit) if name is an invalid string. + * F_parameter (with error bit) if name is an invalid string. + * * F_failure (with error bit) on any other error. * * @see unsetenv() diff --git a/level_0/f_environment/data/build/defines b/level_0/f_environment/data/build/defines index c665317..881dfb4 100644 --- a/level_0/f_environment/data/build/defines +++ b/level_0/f_environment/data/build/defines @@ -1,2 +1,3 @@ # fss-0000 +_di_libcap_ Disable libcap support, allow for compiling and linking without libcap (-lcap). diff --git a/level_0/f_environment/data/build/dependencies-tests b/level_0/f_environment/data/build/dependencies-tests new file mode 100644 index 0000000..dea3179 --- /dev/null +++ b/level_0/f_environment/data/build/dependencies-tests @@ -0,0 +1,3 @@ +# fss-0001 + +cmocka 1.* diff --git a/level_0/f_environment/data/build/settings b/level_0/f_environment/data/build/settings index f1b9c66..c3275f7 100644 --- a/level_0/f_environment/data/build/settings +++ b/level_0/f_environment/data/build/settings @@ -17,7 +17,7 @@ build_indexer ar build_indexer_arguments rcs build_language c -build_libraries -lc +build_libraries -lc -lcap build_libraries-individual -lf_memory -lf_string build_sources_library environment.c @@ -46,6 +46,8 @@ search_exclusive yes search_shared yes search_static yes +#defines -D_di_libcap_ + flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses flags-clang -Wno-logical-op-parentheses flags-test -fstack-protector diff --git a/level_0/f_environment/data/build/settings-mocks b/level_0/f_environment/data/build/settings-mocks new file mode 100644 index 0000000..8e0956d --- /dev/null +++ b/level_0/f_environment/data/build/settings-mocks @@ -0,0 +1,75 @@ +# fss-0001 +# +# Build the project with appropriate mocks linked in via the dynamic linker's "--wrap" functionality. +# +# The -Wl,--wrap does not work across shared files. +# Therefore, this file is a work-around to inject the mocks into the library for testing purposes. +# This should exactly match the "settings" file, except for the additional "-Wl,--wrap" parts and the additional mock source file. +# +# The flags -o0 must be passed to prevent the compiler from optimizing away any functions being mocked (which results in the mock not happening and a real function being called). +# Alternatively, figure out which optimization that is disabled by -o0 and have that specific optimization disabled. +# + +build_name f_environment + +version_major 0 +version_minor 5 +version_micro 9 +version_file micro +version_target minor + +modes individual clang test +modes_default individual + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcap +build_libraries-individual -lf_memory -lf_string + +build_sources_library environment.c ../../tests/unit/c/mock-environment.c + +build_sources_headers environment.h environment/common.h environment/type.h + +build_script yes +build_shared yes +build_static no + +path_headers fll/level_0 +path_library_script script +path_library_shared shared +path_library_static static + +has_path_standard yes +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +#defines -D_di_libcap_ + +flags -O0 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses +flags-clang -Wno-logical-op-parentheses +flags-test -fstack-protector + +flags_library -fPIC + +# Inject mocks. +flags -Wl,--wrap=clearenv +flags -Wl,--wrap=getegid +flags -Wl,--wrap=getenv +flags -Wl,--wrap=geteuid +flags -Wl,--wrap=getgid +flags -Wl,--wrap=getuid +flags -Wl,--wrap=setenv +flags -Wl,--wrap=unsetenv + +# Disable these mock injections when using "-D_di_libcap_". +flags -Wl,--wrap=cap_free +flags -Wl,--wrap=cap_get_flag +flags -Wl,--wrap=cap_get_proc +flags -Wl,--wrap=cap_set_proc diff --git a/level_0/f_environment/data/build/settings-tests b/level_0/f_environment/data/build/settings-tests new file mode 100644 index 0000000..3428a36 --- /dev/null +++ b/level_0/f_environment/data/build/settings-tests @@ -0,0 +1,54 @@ +# fss-0001 +# +# Builds a program that is links to the generated library and is executed to perform tests. +# +# Memory leaks in the test program can be checked for by running valgrind with this executable. +# + +build_name test-f_environment + +version_major 0 +version_minor 5 +version_micro 9 +version_file major +version_target major + +modes individual clang test +modes_default individual + +build_compiler gcc +build_compiler-clang clang +build_indexer ar +build_indexer_arguments rcs +build_language c + +build_libraries -lc -lcmocka +build_libraries-individual -lf_memory -lf_string -lf_environment + +build_sources_program test-environment-clear.c test-environment-exists.c test-environment-get.c test-environment-secure_is.c test-environment-set.c test-environment-unset.c +build_sources_program test-environment.c + +build_script no +build_shared yes +build_static no + +path_headers tests/unit/c +path_sources tests/unit/c + +has_path_standard no +preserve_path_headers yes + +search_exclusive yes +search_shared yes +search_static yes + +#defines -D_di_libcap_ +defines -Ibuild/includes +defines_static -Lbuild/libraries/static +defines_shared -Lbuild/libraries/shared + +flags -O2 -z now -g -fdiagnostics-color=always -Wno-logical-not-parentheses -Wno-parentheses +flags-clang -Wno-logical-op-parentheses +flags-test -fstack-protector + +flags_program -fPIE diff --git a/level_0/f_environment/data/build/testfile b/level_0/f_environment/data/build/testfile new file mode 100644 index 0000000..f1f064b --- /dev/null +++ b/level_0/f_environment/data/build/testfile @@ -0,0 +1,45 @@ +# fss-0005 iki-0002 + +settings: + load_build yes + fail exit + + environment LD_LIBRARY_PATH + +main: + build settings-mocks + build settings-tests + + operate ld_library_path + + if exists build/programs/shared/test-f_environment + shell build/programs/shared/test-f_environment + + if exists build/programs/static/test-f_environment + shell build/programs/static/test-f_environment + + if not exists build/programs/shared/test-f_environment + and not exists build/programs/static/test-f_environment + operate not_created + +not_created: + print + print 'context:"error"Failed to test due to being unable to find either a shared or static test binary to perform tests. context:"reset"' + + exit failure + +ld_library_path: + if defined environment LD_LIBRARY_PATH + and defined parameter work + define LD_LIBRARY_PATH 'build/libraries/shared:parameter:"work:value"libraries/shared:define:"LD_LIBRARY_PATH"' + + else + if defined environment LD_LIBRARY_PATH + define LD_LIBRARY_PATH 'build/libraries/shared:parameter:define:"LD_LIBRARY_PATH"' + + else + if defined parameter work + define LD_LIBRARY_PATH 'build/libraries/shared:parameter:"work:value"libraries/shared' + + else + define LD_LIBRARY_PATH build/libraries/shared diff --git a/level_0/f_environment/tests/unit/c/mock-environment.c b/level_0/f_environment/tests/unit/c/mock-environment.c new file mode 100644 index 0000000..889f668 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/mock-environment.c @@ -0,0 +1,113 @@ +#include "mock-environment.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _di_libcap_ + int __wrap_cap_free(void *obj_d) { + + return mock_type(int); + } + + int __wrap_cap_get_flag(cap_t cap_p, cap_value_t cap, cap_flag_t flag, cap_flag_value_t *value_p) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + *value_p = mock_type(cap_flag_value_t); + + return mock_type(int); + } + + cap_t __wrap_cap_get_proc(void) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return 0; + } + + return mock_type(cap_t); + } + + int __wrap_cap_set_proc(cap_t cap_p) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return mock_type(int); + } +#endif // _di_libcap_ + +int __wrap_clearenv(void) { + + return mock_type(int); +} + +int __wrap_getegid(void) { + + return mock_type(int); +} + +char *__wrap_getenv(const char *name) { + + return mock_type(char *); +} + +int __wrap_geteuid(void) { + + return mock_type(int); +} + +int __wrap_getgid(void) { + + return mock_type(int); +} + +int __wrap_getuid(void) { + + return mock_type(int); +} + +int __wrap_setenv(const char *name, const char *value, int overwrite) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return mock_type(int); +} + +int __wrap_unsetenv(const char *name) { + + const bool failure = mock_type(bool); + + if (failure) { + errno = mock_type(int); + + return -1; + } + + return mock_type(int); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/mock-environment.h b/level_0/f_environment/tests/unit/c/mock-environment.h new file mode 100644 index 0000000..0350384 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/mock-environment.h @@ -0,0 +1,55 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _MOCK__environment_h +#define _MOCK__environment_h + +// Libc includes. +#include +#include +#include +#include + +#ifndef _di_libcap_ + #include +#endif // _di_libcap_ + +// cmocka includes. +#include + +// FLL-0 includes. +#include + +#ifdef __cplusplus +extern "C" { +#endif + +const static int mock_errno_generic = 32767; + +#ifndef _di_libcap_ + extern int __wrap_cap_free(void *obj_d); + extern int __wrap_cap_get_flag(cap_t cap_p, cap_value_t cap, cap_flag_t flag, cap_flag_value_t *value_p); + extern cap_t __wrap_cap_get_proc(void); + extern int __wrap_cap_set_proc(cap_t cap_p); +#endif // _di_libcap_ + +extern int __wrap_clearenv(void); +extern int __wrap_getegid(void); +extern char *__wrap_getenv(const char *name); +extern int __wrap_geteuid(void); +extern int __wrap_getgid(void); +extern int __wrap_getuid(void); +extern int __wrap_setenv(const char *name, const char *value, int overwrite); +extern int __wrap_unsetenv(const char *name); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _MOCK__environment_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-clear.c b/level_0/f_environment/tests/unit/c/test-environment-clear.c new file mode 100644 index 0000000..449d07a --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-clear.c @@ -0,0 +1,32 @@ +#include "test-environment.h" +#include "test-environment-clear.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_clear__fails(void **state) { + + { + will_return(__wrap_clearenv, -1); + + const f_status_t status = f_environment_clear(); + + assert_int_equal(F_status_set_fine(status), F_failure); + } +} + +void test__f_environment_clear__works(void **state) { + + { + will_return(__wrap_clearenv, 0); + + const f_status_t status = f_environment_clear(); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-clear.h b/level_0/f_environment/tests/unit/c/test-environment-clear.h new file mode 100644 index 0000000..f12ab71 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-clear.h @@ -0,0 +1,27 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_clear_h +#define _TEST__F_environment_clear_h + +/** + * Test that function fails. + * + * @see f_environment_clear() + */ +extern void test__f_environment_clear__fails(void **state); + +/** + * Test that function works. + * + * @see f_environment_clear() + */ +extern void test__f_environment_clear__works(void **state); + +#endif // _TEST__F_environment_clear_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-exists.c b/level_0/f_environment/tests/unit/c/test-environment-exists.c new file mode 100644 index 0000000..1c37f9e --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-exists.c @@ -0,0 +1,45 @@ +#include "test-environment.h" +#include "test-environment-exists.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_exists__fails(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + will_return(__wrap_getenv, 0); + + const f_status_t status = f_environment_exists(path); + + assert_int_equal(status, F_false); + } +} + +void test__f_environment_exists__returns_data_not(void **state) { + + { + const f_status_t status = f_environment_exists(f_string_empty_s); + + assert_int_equal(status, F_data_not); + } +} + +void test__f_environment_exists__works(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + will_return(__wrap_getenv, path.string); + + const f_status_t status = f_environment_exists(path); + + assert_int_equal(status, F_true); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-exists.h b/level_0/f_environment/tests/unit/c/test-environment-exists.h new file mode 100644 index 0000000..294558b --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-exists.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_exists_h +#define _TEST__F_environment_exists_h + +/** + * Test that function fails. + * + * @see f_environment_exists() + */ +extern void test__f_environment_exists__fails(void **state); + +/** + * Test that function works but the path is empty. + * + * @see f_environment_exists() + */ +extern void test__f_environment_exists__returns_data_not(void **state); + +/** + * Test that function works. + * + * @see f_environment_exists() + */ +extern void test__f_environment_exists__works(void **state); + +#endif // _TEST__F_environment_exists_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-get.c b/level_0/f_environment/tests/unit/c/test-environment-get.c new file mode 100644 index 0000000..352b28b --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-get.c @@ -0,0 +1,84 @@ +#include "test-environment.h" +#include "test-environment-get.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_get__fails(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + + { + will_return(__wrap_getenv, 0); + + const f_status_t status = f_environment_get(path, &buffer); + + assert_int_equal(status, F_exist_not); + } + + f_string_dynamic_resize(0, &buffer); +} + +#ifndef _di_level_0_parameter_checking_ + void test__f_environment_get__parameter_checking(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + const f_status_t status = f_environment_get(path, 0); + + assert_int_equal(F_status_set_fine(status), F_parameter); + } + } +#endif // _di_level_0_parameter_checking_ + +void test__f_environment_get__returns_data_not(void **state) { + + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + + { + const f_status_t status = f_environment_get(f_string_empty_s, &buffer); + + assert_int_equal(status, F_data_not); + } + + f_string_dynamic_resize(0, &buffer); +} + +void test__f_environment_get__works(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + f_string_dynamic_t buffer = f_string_dynamic_t_initialize; + + { + will_return(__wrap_getenv, path.string); + + const f_status_t status = f_environment_get(path, &buffer); + + assert_int_equal(status, F_none); + assert_int_equal(buffer.used, path.used); + assert_string_equal(buffer.string, path.string); + } + + buffer.used = 0; + + { + will_return(__wrap_getenv, f_string_empty_s.string); + + const f_status_t status = f_environment_get(path, &buffer); + + assert_int_equal(status, F_none); + assert_int_equal(buffer.used, f_string_empty_s.used); + assert_string_equal(buffer.string, f_string_empty_s.string); + } + + f_string_dynamic_resize(0, &buffer); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-get.h b/level_0/f_environment/tests/unit/c/test-environment-get.h new file mode 100644 index 0000000..32edb8e --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-get.h @@ -0,0 +1,43 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_get_h +#define _TEST__F_environment_get_h + +/** + * Test that function fails. + * + * @see f_environment_get() + */ +extern void test__f_environment_get__fails(void **state); + +/** + * Test that parameter checking works as expected. + * + * @see f_environment_get() + */ +#ifndef _di_level_0_parameter_checking_ + extern void test__f_environment_get__parameter_checking(void **state); +#endif // _di_level_0_parameter_checking_ + +/** + * Test that function works but the path is empty. + * + * @see f_environment_get() + */ +extern void test__f_environment_get__returns_data_not(void **state); + +/** + * Test that function works. + * + * @see f_environment_get() + */ +extern void test__f_environment_get__works(void **state); + +#endif // _TEST__F_environment_get_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-secure_is.c b/level_0/f_environment/tests/unit/c/test-environment-secure_is.c new file mode 100644 index 0000000..80e78b3 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-secure_is.c @@ -0,0 +1,169 @@ +#include "test-environment.h" +#include "test-environment-secure_is.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_secure_is__fails(void **state) { + + #ifndef _di_libcap_ + { + int errnos[] = { + EINVAL, + EPERM, + ENOMEM, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_parameter, + F_prohibited, + F_memory_not, + F_failure, + }; + + for (int i = 0; i < 4; ++i) { + + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 2); + + will_return(__wrap_cap_get_proc, true); + will_return(__wrap_cap_get_proc, errnos[i]); + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(F_status_set_fine(status), statuss[i]); + } // for + } + + { + int errnos[] = { + EINVAL, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_parameter, + F_failure, + }; + + for (int i = 0; i < 2; ++i) { + + long stub = 0; + cap_t capability = (cap_t) &stub; + + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 1); + + will_return(__wrap_getegid, 3); + will_return(__wrap_getgid, 4); + + will_return(__wrap_cap_get_proc, false); + will_return(__wrap_cap_get_proc, capability); + + will_return(__wrap_cap_get_flag, true); + will_return(__wrap_cap_get_flag, errnos[i]); + + will_return(__wrap_cap_free, 0); + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(F_status_set_fine(status), statuss[i]); + } // for + } + #endif // _di_libcap_ +} + +void test__f_environment_secure_is__works(void **state) { + + { + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 1); + + will_return(__wrap_getegid, 1); + will_return(__wrap_getgid, 1); + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(status, F_true); + } + + { + #ifndef _di_libcap_ + long stub = 0; + cap_t capability = (cap_t) &stub; + #endif // _di_libcap_ + + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 0); + + #ifndef _di_libcap_ + will_return(__wrap_cap_get_proc, false); + will_return(__wrap_cap_get_proc, capability); + + will_return(__wrap_cap_get_flag, false); + will_return(__wrap_cap_get_flag, CAP_CLEAR); + will_return(__wrap_cap_get_flag, 0); + + will_return(__wrap_cap_free, 0); + #endif // _di_libcap_ + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(status, F_false); + } + + #ifndef _di_libcap_ + { + long stub = 0; + cap_t capability = (cap_t) &stub; + + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 1); + + will_return(__wrap_getegid, 1); + will_return(__wrap_getgid, 2); + + will_return(__wrap_cap_get_proc, false); + will_return(__wrap_cap_get_proc, capability); + + will_return(__wrap_cap_get_flag, false); + will_return(__wrap_cap_get_flag, CAP_CLEAR); + will_return(__wrap_cap_get_flag, 0); + + will_return(__wrap_cap_free, 0); + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(status, F_false); + } + #endif // _di_libcap_ + + #ifndef _di_libcap_ + { + long stub = 0; + cap_t capability = (cap_t) &stub;; + + will_return(__wrap_geteuid, 1); + will_return(__wrap_getuid, 2); + + will_return(__wrap_cap_get_proc, false); + will_return(__wrap_cap_get_proc, capability); + + will_return(__wrap_cap_get_flag, false); + will_return(__wrap_cap_get_flag, CAP_SET); + will_return(__wrap_cap_get_flag, 0); + + will_return(__wrap_cap_free, 0); + + const f_status_t status = f_environment_secure_is(); + + assert_int_equal(status, F_true); + } + #endif // _di_libcap_ +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-secure_is.h b/level_0/f_environment/tests/unit/c/test-environment-secure_is.h new file mode 100644 index 0000000..ceb7ab5 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-secure_is.h @@ -0,0 +1,27 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_secure_is_h +#define _TEST__F_environment_secure_is_h + +/** + * Test that function fails. + * + * @see f_environment_secure_is() + */ +extern void test__f_environment_secure_is__fails(void **state); + +/** + * Test that function works. + * + * @see f_environment_secure_is() + */ +extern void test__f_environment_secure_is__works(void **state); + +#endif // _TEST__F_environment_secure_is_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-set.c b/level_0/f_environment/tests/unit/c/test-environment-set.c new file mode 100644 index 0000000..6d15726 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-set.c @@ -0,0 +1,70 @@ +#include "test-environment.h" +#include "test-environment-set.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_set__fails(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + int errnos[] = { + EINVAL, + ENOMEM, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_parameter, + F_memory_not, + F_failure, + }; + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_setenv, true); + will_return(__wrap_setenv, errnos[i]); + + const f_status_t status = f_environment_set(path, path, F_false); + + assert_int_equal(F_status_set_fine(status), statuss[i]); + } // for + } +} + +void test__f_environment_set__returns_data_not(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + const f_status_t status = f_environment_set(f_string_empty_s, f_string_empty_s, F_false); + + assert_int_equal(status, F_data_not); + } + + { + const f_status_t status = f_environment_set(f_string_empty_s, path, F_false); + + assert_int_equal(status, F_data_not); + } +} + +void test__f_environment_set__works(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + will_return(__wrap_setenv, false); + will_return(__wrap_setenv, 0); + + const f_status_t status = f_environment_set(path, path, F_false); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-set.h b/level_0/f_environment/tests/unit/c/test-environment-set.h new file mode 100644 index 0000000..f45a3ff --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-set.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_set_h +#define _TEST__F_environment_set_h + +/** + * Test that function fails. + * + * @see f_environment_set() + */ +extern void test__f_environment_set__fails(void **state); + +/** + * Test that function works but the path is empty. + * + * @see f_environment_set() + */ +extern void test__f_environment_set__returns_data_not(void **state); + +/** + * Test that function works. + * + * @see f_environment_set() + */ +extern void test__f_environment_set__works(void **state); + +#endif // _TEST__F_environment_set_h diff --git a/level_0/f_environment/tests/unit/c/test-environment-unset.c b/level_0/f_environment/tests/unit/c/test-environment-unset.c new file mode 100644 index 0000000..2c630e2 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-unset.c @@ -0,0 +1,62 @@ +#include "test-environment.h" +#include "test-environment-unset.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void test__f_environment_unset__fails(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + int errnos[] = { + EINVAL, + ENOMEM, + mock_errno_generic, + }; + + f_status_t statuss[] = { + F_parameter, + F_memory_not, + F_failure, + }; + + for (int i = 0; i < 3; ++i) { + + will_return(__wrap_unsetenv, true); + will_return(__wrap_unsetenv, errnos[i]); + + const f_status_t status = f_environment_unset(path); + + assert_int_equal(F_status_set_fine(status), statuss[i]); + } // for + } +} + +void test__f_environment_unset__returns_data_not(void **state) { + + { + const f_status_t status = f_environment_unset(f_string_empty_s); + + assert_int_equal(status, F_data_not); + } +} + +void test__f_environment_unset__works(void **state) { + + const f_string_static_t path = macro_f_string_static_t_initialize("test", 0, 4); + + { + will_return(__wrap_unsetenv, false); + will_return(__wrap_unsetenv, 0); + + const f_status_t status = f_environment_unset(path); + + assert_int_equal(status, F_none); + } +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment-unset.h b/level_0/f_environment/tests/unit/c/test-environment-unset.h new file mode 100644 index 0000000..923f3e8 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment-unset.h @@ -0,0 +1,34 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_unset_h +#define _TEST__F_environment_unset_h + +/** + * Test that function fails. + * + * @see f_environment_unset() + */ +extern void test__f_environment_unset__fails(void **state); + +/** + * Test that function works but the path is empty. + * + * @see f_environment_unset() + */ +extern void test__f_environment_unset__returns_data_not(void **state); + +/** + * Test that function works. + * + * @see f_environment_unset() + */ +extern void test__f_environment_unset__works(void **state); + +#endif // _TEST__F_environment_unset_h diff --git a/level_0/f_environment/tests/unit/c/test-environment.c b/level_0/f_environment/tests/unit/c/test-environment.c new file mode 100644 index 0000000..8096ba7 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment.c @@ -0,0 +1,59 @@ +#include "test-environment.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int setup(void **state) { + + return 0; +} + +int setdown(void **state) { + + errno = 0; + + return 0; +} + +int main(void) { + + const struct CMUnitTest tests[] = { + cmocka_unit_test(test__f_environment_clear__fails), + cmocka_unit_test(test__f_environment_clear__works), + + cmocka_unit_test(test__f_environment_exists__fails), + cmocka_unit_test(test__f_environment_exists__returns_data_not), + cmocka_unit_test(test__f_environment_exists__works), + + cmocka_unit_test(test__f_environment_get__fails), + cmocka_unit_test(test__f_environment_get__returns_data_not), + cmocka_unit_test(test__f_environment_get__works), + + cmocka_unit_test(test__f_environment_secure_is__fails), + cmocka_unit_test(test__f_environment_secure_is__works), + + cmocka_unit_test(test__f_environment_set__fails), + cmocka_unit_test(test__f_environment_set__returns_data_not), + cmocka_unit_test(test__f_environment_set__works), + + cmocka_unit_test(test__f_environment_unset__fails), + cmocka_unit_test(test__f_environment_unset__returns_data_not), + cmocka_unit_test(test__f_environment_unset__works), + + #ifndef _di_level_0_parameter_checking_ + // f_environment_clear() doesn't use parameter checking. + // f_environment_exists() doesn't use parameter checking. + cmocka_unit_test(test__f_environment_get__parameter_checking), + // f_environment_secure_is() doesn't use parameter checking. + // f_environment_set() doesn't use parameter checking. + // f_environment_unset() doesn't use parameter checking. + #endif // _di_level_0_parameter_checking_ + }; + + return cmocka_run_group_tests(tests, setup, setdown); +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/level_0/f_environment/tests/unit/c/test-environment.h b/level_0/f_environment/tests/unit/c/test-environment.h new file mode 100644 index 0000000..a10e751 --- /dev/null +++ b/level_0/f_environment/tests/unit/c/test-environment.h @@ -0,0 +1,77 @@ +/** + * FLL - Level 0 + * + * Project: Environment + * API Version: 0.5 + * Licenses: lgpl-2.1-or-later + * + * Test the environment project. + */ +#ifndef _TEST__F_environment_h +#define _TEST__F_environment_h + +// Libc includes. +#include +#include +#include +#include + +// cmocka includes. +#include + +// FLL-0 includes. +#include + +// Mock includes. +#include "mock-environment.h" + +// Test includes. +#include "test-environment-clear.h" +#include "test-environment-exists.h" +#include "test-environment-get.h" +#include "test-environment-secure_is.h" +#include "test-environment-set.h" +#include "test-environment-unset.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Perform any setup operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setup(void **state); + +/** + * Peform any setdown operations. + * + * @param state + * The test state. + * + * @return + * The status of this function, where 0 means success. + */ +extern int setdown(void **state); + +/** + * Run all tests. + * + * @return + * The final result of the tests. + * + * @see cmocka_run_group_tests() + * @see cmocka_unit_test() + */ +extern int main(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _TEST__F_environment_h -- 1.8.3.1