diff --git a/libsemanage/include/semanage/handle.h b/libsemanage/include/semanage/handle.h index 946d69bc..0157be4f 100644 --- a/libsemanage/include/semanage/handle.h +++ b/libsemanage/include/semanage/handle.h @@ -66,6 +66,11 @@ extern void semanage_set_reload(semanage_handle_t * handle, int do_reload); * 1 for yes, 0 for no (default) */ extern void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild); +/* set whether to rebuild the policy on commit when potential changes + * to module files since last rebuild are detected, + * 1 for yes (default), 0 for no */ +extern void semanage_set_check_ext_changes(semanage_handle_t * handle, int do_check); + /* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path * corresponding to lang_ext. * Upon success returns 0, -1 on error. */ diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c index f0bd7716..d83941b0 100644 --- a/libsemanage/src/direct_api.c +++ b/libsemanage/src/direct_api.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include #include #include @@ -56,8 +58,7 @@ #include "semanage_store.h" #include "database_policydb.h" #include "policy.h" -#include -#include +#include "sha256.h" #define PIPE_READ 0 #define PIPE_WRITE 1 @@ -450,7 +451,7 @@ static int parse_module_headers(semanage_handle_t * sh, char *module_data, /* Writes a block of data to a file. Returns 0 on success, -1 on * error. */ static int write_file(semanage_handle_t * sh, - const char *filename, char *data, size_t num_bytes) + const char *filename, const char *data, size_t num_bytes) { int out; @@ -850,8 +851,21 @@ cleanup: return ret; } +static void update_checksum_with_len(Sha256Context *context, size_t s) +{ + int i; + uint8_t buffer[8]; + + for (i = 0; i < 8; i++) { + buffer[i] = s & 0xff; + s >>= 8; + } + Sha256Update(context, buffer, 8); +} + static int semanage_compile_module(semanage_handle_t *sh, - semanage_module_info_t *modinfo) + semanage_module_info_t *modinfo, + Sha256Context *context) { char cil_path[PATH_MAX]; char hll_path[PATH_MAX]; @@ -922,6 +936,11 @@ static int semanage_compile_module(semanage_handle_t *sh, goto cleanup; } + if (context) { + update_checksum_with_len(context, cil_data_len); + Sha256Update(context, cil_data, cil_data_len); + } + status = write_compressed_file(sh, cil_path, cil_data, cil_data_len); if (status == -1) { ERR(sh, "Failed to write %s\n", cil_path); @@ -950,18 +969,40 @@ cleanup: return status; } -static int semanage_compile_hll_modules(semanage_handle_t *sh, - semanage_module_info_t *modinfos, - int num_modinfos) +static int modinfo_cmp(const void *a, const void *b) { - int status = 0; - int i; + const semanage_module_info_t *ma = a; + const semanage_module_info_t *mb = b; + + return strcmp(ma->name, mb->name); +} + +static int semanage_compile_hll_modules(semanage_handle_t *sh, + semanage_module_info_t *modinfos, + int num_modinfos, + char *cil_checksum) +{ + /* to be incremented when checksum input data format changes */ + static const size_t CHECKSUM_EPOCH = 1; + + int i, status = 0; char cil_path[PATH_MAX]; struct stat sb; + Sha256Context context; + SHA256_HASH hash; + struct file_contents contents = {}; assert(sh); assert(modinfos); + /* Sort modules by name to get consistent ordering. */ + qsort(modinfos, num_modinfos, sizeof(*modinfos), &modinfo_cmp); + + Sha256Initialise(&context); + update_checksum_with_len(&context, CHECKSUM_EPOCH); + + /* prefix with module count to avoid collisions */ + update_checksum_with_len(&context, num_modinfos); for (i = 0; i < num_modinfos; i++) { status = semanage_module_get_path( sh, @@ -969,29 +1010,91 @@ static int semanage_compile_hll_modules(semanage_handle_t *sh, SEMANAGE_MODULE_PATH_CIL, cil_path, sizeof(cil_path)); - if (status != 0) { - goto cleanup; + if (status != 0) + return -1; + + if (!semanage_get_ignore_module_cache(sh)) { + status = stat(cil_path, &sb); + if (status == 0) { + status = map_compressed_file(sh, cil_path, &contents); + if (status < 0) { + ERR(sh, "Error mapping file: %s", cil_path); + return -1; + } + + /* prefix with length to avoid collisions */ + update_checksum_with_len(&context, contents.len); + Sha256Update(&context, contents.data, contents.len); + + unmap_compressed_file(&contents); + continue; + } else if (errno != ENOENT) { + ERR(sh, "Unable to access %s: %s\n", cil_path, + strerror(errno)); + return -1; //an error in the "stat" call + } } - if (semanage_get_ignore_module_cache(sh) == 0 && - (status = stat(cil_path, &sb)) == 0) { - continue; - } - if (status != 0 && errno != ENOENT) { - ERR(sh, "Unable to access %s: %s\n", cil_path, strerror(errno)); - goto cleanup; //an error in the "stat" call - } + status = semanage_compile_module(sh, &modinfos[i], &context); + if (status < 0) + return -1; + } + Sha256Finalise(&context, &hash); - status = semanage_compile_module(sh, &modinfos[i]); - if (status < 0) { - goto cleanup; + semanage_hash_to_checksum_string(hash.bytes, cil_checksum); + return 0; +} + +static int semanage_compare_checksum(semanage_handle_t *sh, const char *reference) +{ + const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM); + struct stat sb; + int fd, retval; + char *data; + + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT) { + ERR(sh, "Unable to open %s: %s\n", path, strerror(errno)); + return -1; } + /* Checksum file not present - force a rebuild. */ + return 1; } - status = 0; + if (fstat(fd, &sb) == -1) { + ERR(sh, "Unable to stat %s\n", path); + retval = -1; + goto out_close; + } -cleanup: - return status; + if (sb.st_size != (off_t)CHECKSUM_CONTENT_SIZE) { + /* Incompatible/invalid hash type - just force a rebuild. */ + WARN(sh, "Module checksum invalid - forcing a rebuild\n"); + retval = 1; + goto out_close; + } + + data = mmap(NULL, CHECKSUM_CONTENT_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); + if (data == MAP_FAILED) { + ERR(sh, "Unable to mmap %s\n", path); + retval = -1; + goto out_close; + } + + retval = memcmp(data, reference, CHECKSUM_CONTENT_SIZE) != 0; + munmap(data, sb.st_size); +out_close: + close(fd); + return retval; +} + +static int semanage_write_modules_checksum(semanage_handle_t *sh, + const char *checksum) +{ + const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM); + + return write_file(sh, path, checksum, CHECKSUM_CONTENT_SIZE); } /* Files that must exist in order to skip policy rebuild. */ @@ -1030,6 +1133,7 @@ static int semanage_direct_commit(semanage_handle_t * sh) semanage_module_info_t *modinfos = NULL; mode_t mask = umask(0077); struct stat sb; + char modules_checksum[CHECKSUM_CONTENT_SIZE + 1 /* '\0' */]; int do_rebuild, do_write_kernel, do_install; int fcontexts_modified, ports_modified, seusers_modified, @@ -1159,6 +1263,38 @@ static int semanage_direct_commit(semanage_handle_t * sh) } } + if (do_rebuild || sh->check_ext_changes) { + retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos); + if (retval < 0) { + goto cleanup; + } + + /* No modules - nothing to rebuild. */ + if (num_modinfos == 0) { + goto cleanup; + } + + retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos, + modules_checksum); + if (retval < 0) { + ERR(sh, "Failed to compile hll files into cil files.\n"); + goto cleanup; + } + + if (!do_rebuild && sh->check_ext_changes) { + retval = semanage_compare_checksum(sh, modules_checksum); + if (retval < 0) + goto cleanup; + do_rebuild = retval; + } + + retval = semanage_write_modules_checksum(sh, modules_checksum); + if (retval < 0) { + ERR(sh, "Failed to write module checksum file.\n"); + goto cleanup; + } + } + /* * If there were policy changes, or explicitly requested, or * any required files are missing, rebuild the policy. @@ -1166,21 +1302,6 @@ static int semanage_direct_commit(semanage_handle_t * sh) if (do_rebuild) { /* =================== Module expansion =============== */ - retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos); - if (retval < 0) { - goto cleanup; - } - - if (num_modinfos == 0) { - goto cleanup; - } - - retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos); - if (retval < 0) { - ERR(sh, "Failed to compile hll files into cil files.\n"); - goto cleanup; - } - retval = semanage_get_cil_paths(sh, modinfos, num_modinfos, &mod_filenames); if (retval < 0) goto cleanup; @@ -1703,7 +1824,7 @@ static int semanage_direct_extract(semanage_handle_t * sh, goto cleanup; } - rc = semanage_compile_module(sh, _modinfo); + rc = semanage_compile_module(sh, _modinfo, NULL); if (rc < 0) { goto cleanup; } diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c index bb1e6140..b2201ee3 100644 --- a/libsemanage/src/handle.c +++ b/libsemanage/src/handle.c @@ -116,20 +116,23 @@ semanage_handle_t *semanage_handle_create(void) void semanage_set_rebuild(semanage_handle_t * sh, int do_rebuild) { - assert(sh != NULL); sh->do_rebuild = do_rebuild; - return; } void semanage_set_reload(semanage_handle_t * sh, int do_reload) { - assert(sh != NULL); sh->do_reload = do_reload; - return; +} + +void semanage_set_check_ext_changes(semanage_handle_t * sh, int do_check) +{ + assert(sh != NULL); + + sh->check_ext_changes = do_check; } int semanage_get_hll_compiler_path(semanage_handle_t *sh, diff --git a/libsemanage/src/handle.h b/libsemanage/src/handle.h index e1ce83ba..4d2aae8f 100644 --- a/libsemanage/src/handle.h +++ b/libsemanage/src/handle.h @@ -61,6 +61,7 @@ struct semanage_handle { int is_in_transaction; int do_reload; /* whether to reload policy after commit */ int do_rebuild; /* whether to rebuild policy if there were no changes */ + int check_ext_changes; /* whether to rebuild if external changes are detected via checksum */ int commit_err; /* set by semanage_direct_commit() if there are * any errors when building or committing the * sandbox to kernel policy at /etc/selinux diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map index 00259fc8..c8214b26 100644 --- a/libsemanage/src/libsemanage.map +++ b/libsemanage/src/libsemanage.map @@ -348,4 +348,5 @@ LIBSEMANAGE_1.1 { LIBSEMANAGE_3.4 { semanage_module_compute_checksum; + semanage_set_check_ext_changes; } LIBSEMANAGE_1.1; diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c index fe937519..c3bd90ac 100644 --- a/libsemanage/src/modules.c +++ b/libsemanage/src/modules.c @@ -979,9 +979,9 @@ int semanage_module_remove_key(semanage_handle_t *sh, } static const char CHECKSUM_TYPE[] = "sha256"; -static const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE; +const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE; -static void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum) +void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum) { size_t i; diff --git a/libsemanage/src/modules.h b/libsemanage/src/modules.h index 64d4a157..cf2432c5 100644 --- a/libsemanage/src/modules.h +++ b/libsemanage/src/modules.h @@ -102,4 +102,7 @@ int semanage_module_get_path(semanage_handle_t *sh, char *path, size_t len); +extern const size_t CHECKSUM_CONTENT_SIZE; +void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum); + #endif diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c index 633ee731..767f05cb 100644 --- a/libsemanage/src/semanage_store.c +++ b/libsemanage/src/semanage_store.c @@ -115,6 +115,7 @@ static const char *semanage_sandbox_paths[SEMANAGE_STORE_NUM_PATHS] = { "/disable_dontaudit", "/preserve_tunables", "/modules/disabled", + "/modules_checksum", "/policy.kern", "/file_contexts.local", "/file_contexts.homedirs", diff --git a/libsemanage/src/semanage_store.h b/libsemanage/src/semanage_store.h index b9ec5664..1fc77da8 100644 --- a/libsemanage/src/semanage_store.h +++ b/libsemanage/src/semanage_store.h @@ -60,6 +60,7 @@ enum semanage_sandbox_defs { SEMANAGE_DISABLE_DONTAUDIT, SEMANAGE_PRESERVE_TUNABLES, SEMANAGE_MODULES_DISABLED, + SEMANAGE_MODULES_CHECKSUM, SEMANAGE_STORE_KERNEL, SEMANAGE_STORE_FC_LOCAL, SEMANAGE_STORE_FC_HOMEDIRS,