Blame SOURCES/0012-libsemanage-optionally-rebuild-policy-when-modules-a.patch

83eaee
From 090a82e33be97d42eaa75bec85a32ccb6b7a13e8 Mon Sep 17 00:00:00 2001
83eaee
From: Ondrej Mosnacek <omosnace@redhat.com>
83eaee
Date: Thu, 3 Feb 2022 17:53:26 +0100
83eaee
Subject: [PATCH] libsemanage: optionally rebuild policy when modules are
83eaee
 changed externally
83eaee
83eaee
In Fedora/RHEL's selinux-policy package we ship a pre-built SELinux
83eaee
policy store in the RPMs. When updating the main policy RPM, care must
83eaee
be taken to rebuild the policy using `semodule -B` if there are any
83eaee
other SELinux modules installed (whether shipped via another RPM or
83eaee
manually installed locally).
83eaee
83eaee
However, this way of shipping/managing the policy creates complications
83eaee
on systems, where system files are managed by rpm-ostree (such as Fedora
83eaee
CoreOS or Red Hat CoreOS), where the "package update" process is more
83eaee
sophisticated.
83eaee
83eaee
(Disclaimer: The following is written according to my current limited
83eaee
understanding of rpm-ostree and may not be entirely accurate, but the
83eaee
gist of it should match the reality.)
83eaee
83eaee
Basically, one can think of rpm-ostree as a kind of Git for system
83eaee
files. The package content is provided on a "branch", where each
83eaee
"commit" represents a set of package updates layered on top of the
83eaee
previous commit (i.e. it is a rolling release with some defined
83eaee
package content snapshots). The user can then maintain their own branch
83eaee
with additional package updates/installations/... and "rebase" it on top
83eaee
of the main branch as needed. On top of that, the user can also have
83eaee
additional configuration files (or modifications to existing files) in
83eaee
/etc, which represent an additional layer on top of the package content.
83eaee
83eaee
When updating the system (i.e. rebasing on a new "commit" of the "main
83eaee
branch"), the files on the running system are not touched and the new
83eaee
system state is prepared under a new root directory, which is chrooted
83eaee
into on the next reboot.
83eaee
83eaee
When an rpm-ostree system is updated, there are three moments when the
83eaee
SELinux module store needs to be rebuilt to ensure that all modules are
83eaee
included in the binary policy:
83eaee
1. When the local RPM installations are applied on top of the base
83eaee
   system snapshot.
83eaee
2. When local user configuartion is applied on top of that.
83eaee
3. On system shutdown, to ensure that any changes in local configuration
83eaee
   performed since (2.) are reflected in the final new system image.
83eaee
83eaee
Forcing a full rebuild at each step is not optimal and in many cases is
83eaee
not necessary, as the user may not have any custom modules installed.
83eaee
83eaee
Thus, this patch extends libsemanage to compute a checksum of the
83eaee
content of all enabled modules, which is stored in the store, and adds a
83eaee
flag to the libsemanage handle that instructs it to check the module
83eaee
content checksum against the one from the last successful transaction
83eaee
and force a full policy rebuild if they don't match.
83eaee
83eaee
This will allow rpm-ostree systems to potentially reduce delays when
83eaee
reconciling the module store when applying updates.
83eaee
83eaee
I wasn't able to measure any noticeable overhead of the hash
83eaee
computation, which is now added for every transaction (both before and
83eaee
after this change a full policy rebuild took about 7 seconds on my test
83eaee
x86 VM). With the new option check_ext_changes enabled, rebuilding a
83eaee
policy store with unchanged modules took only about 0.96 seconds.
83eaee
83eaee
Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
83eaee
---
83eaee
 libsemanage/include/semanage/handle.h |   5 +
83eaee
 libsemanage/src/direct_api.c          | 187 +++++++++++++++++++++-----
83eaee
 libsemanage/src/handle.c              |  11 +-
83eaee
 libsemanage/src/handle.h              |   1 +
83eaee
 libsemanage/src/libsemanage.map       |   1 +
83eaee
 libsemanage/src/modules.c             |   4 +-
83eaee
 libsemanage/src/modules.h             |   3 +
83eaee
 libsemanage/src/semanage_store.c      |   1 +
83eaee
 libsemanage/src/semanage_store.h      |   1 +
83eaee
 9 files changed, 175 insertions(+), 39 deletions(-)
83eaee
83eaee
diff --git a/libsemanage/include/semanage/handle.h b/libsemanage/include/semanage/handle.h
83eaee
index c8165900..7f298a49 100644
83eaee
--- a/libsemanage/include/semanage/handle.h
83eaee
+++ b/libsemanage/include/semanage/handle.h
83eaee
@@ -66,6 +66,11 @@ void semanage_set_reload(semanage_handle_t * handle, int do_reload);
83eaee
  * 1 for yes, 0 for no (default) */
83eaee
 void semanage_set_rebuild(semanage_handle_t * handle, int do_rebuild);
83eaee
 
83eaee
+/* set whether to rebuild the policy on commit when potential changes
83eaee
+ * to module files since last rebuild are detected,
83eaee
+ * 1 for yes (default), 0 for no */
83eaee
+extern void semanage_set_check_ext_changes(semanage_handle_t * handle, int do_check);
83eaee
+
83eaee
 /* Fills *compiler_path with the location of the hll compiler sh->conf->compiler_directory_path
83eaee
  * corresponding to lang_ext.
83eaee
  * Upon success returns 0, -1 on error. */
83eaee
diff --git a/libsemanage/src/direct_api.c b/libsemanage/src/direct_api.c
83eaee
index 7e99a59f..bbdca2b2 100644
83eaee
--- a/libsemanage/src/direct_api.c
83eaee
+++ b/libsemanage/src/direct_api.c
83eaee
@@ -33,6 +33,8 @@
83eaee
 #include <unistd.h>
83eaee
 #include <sys/stat.h>
83eaee
 #include <sys/types.h>
83eaee
+#include <sys/mman.h>
83eaee
+#include <sys/wait.h>
83eaee
 #include <limits.h>
83eaee
 #include <errno.h>
83eaee
 #include <dirent.h>
83eaee
@@ -56,8 +58,7 @@
83eaee
 #include "semanage_store.h"
83eaee
 #include "database_policydb.h"
83eaee
 #include "policy.h"
83eaee
-#include <sys/mman.h>
83eaee
-#include <sys/wait.h>
83eaee
+#include "sha256.h"
83eaee
 
83eaee
 #define PIPE_READ 0
83eaee
 #define PIPE_WRITE 1
83eaee
@@ -450,7 +451,7 @@ static int parse_module_headers(semanage_handle_t * sh, char *module_data,
83eaee
 /* Writes a block of data to a file.  Returns 0 on success, -1 on
83eaee
  * error. */
83eaee
 static int write_file(semanage_handle_t * sh,
83eaee
-		      const char *filename, char *data, size_t num_bytes)
83eaee
+		      const char *filename, const char *data, size_t num_bytes)
83eaee
 {
83eaee
 	int out;
83eaee
 
83eaee
@@ -850,8 +851,21 @@ cleanup:
83eaee
 	return ret;
83eaee
 }
83eaee
 
83eaee
+static void update_checksum_with_len(Sha256Context *context, size_t s)
83eaee
+{
83eaee
+	int i;
83eaee
+	uint8_t buffer[8];
83eaee
+
83eaee
+	for (i = 0; i < 8; i++) {
83eaee
+		buffer[i] = s & 0xff;
83eaee
+		s >>= 8;
83eaee
+	}
83eaee
+	Sha256Update(context, buffer, 8);
83eaee
+}
83eaee
+
83eaee
 static int semanage_compile_module(semanage_handle_t *sh,
83eaee
-				semanage_module_info_t *modinfo)
83eaee
+				   semanage_module_info_t *modinfo,
83eaee
+				   Sha256Context *context)
83eaee
 {
83eaee
 	char cil_path[PATH_MAX];
83eaee
 	char hll_path[PATH_MAX];
83eaee
@@ -922,6 +936,11 @@ static int semanage_compile_module(semanage_handle_t *sh,
83eaee
 		goto cleanup;
83eaee
 	}
83eaee
 
83eaee
+	if (context) {
83eaee
+		update_checksum_with_len(context, cil_data_len);
83eaee
+		Sha256Update(context, cil_data, cil_data_len);
83eaee
+	}
83eaee
+
83eaee
 	status = write_compressed_file(sh, cil_path, cil_data, cil_data_len);
83eaee
 	if (status == -1) {
83eaee
 		ERR(sh, "Failed to write %s\n", cil_path);
83eaee
@@ -950,18 +969,40 @@ cleanup:
83eaee
 	return status;
83eaee
 }
83eaee
 
83eaee
+static int modinfo_cmp(const void *a, const void *b)
83eaee
+{
83eaee
+	const semanage_module_info_t *ma = a;
83eaee
+	const semanage_module_info_t *mb = b;
83eaee
+
83eaee
+	return strcmp(ma->name, mb->name);
83eaee
+}
83eaee
+
83eaee
 static int semanage_compile_hll_modules(semanage_handle_t *sh,
83eaee
-				semanage_module_info_t *modinfos,
83eaee
-				int num_modinfos)
83eaee
+					semanage_module_info_t *modinfos,
83eaee
+					int num_modinfos,
83eaee
+					char *cil_checksum)
83eaee
 {
83eaee
-	int status = 0;
83eaee
-	int i;
83eaee
+	/* to be incremented when checksum input data format changes */
83eaee
+	static const size_t CHECKSUM_EPOCH = 1;
83eaee
+
83eaee
+	int i, status = 0;
83eaee
 	char cil_path[PATH_MAX];
83eaee
 	struct stat sb;
83eaee
+	Sha256Context context;
83eaee
+	SHA256_HASH hash;
83eaee
+	struct file_contents contents = {};
83eaee
 
83eaee
 	assert(sh);
83eaee
 	assert(modinfos);
83eaee
 
83eaee
+	/* Sort modules by name to get consistent ordering. */
83eaee
+	qsort(modinfos, num_modinfos, sizeof(*modinfos), &modinfo_cmp);
83eaee
+
83eaee
+	Sha256Initialise(&context);
83eaee
+	update_checksum_with_len(&context, CHECKSUM_EPOCH);
83eaee
+
83eaee
+	/* prefix with module count to avoid collisions */
83eaee
+	update_checksum_with_len(&context, num_modinfos);
83eaee
 	for (i = 0; i < num_modinfos; i++) {
83eaee
 		status = semanage_module_get_path(
83eaee
 				sh,
83eaee
@@ -969,29 +1010,91 @@ static int semanage_compile_hll_modules(semanage_handle_t *sh,
83eaee
 				SEMANAGE_MODULE_PATH_CIL,
83eaee
 				cil_path,
83eaee
 				sizeof(cil_path));
83eaee
-		if (status != 0) {
83eaee
-			goto cleanup;
83eaee
-		}
83eaee
+		if (status != 0)
83eaee
+			return -1;
83eaee
 
83eaee
-		if (semanage_get_ignore_module_cache(sh) == 0 &&
83eaee
-				(status = stat(cil_path, &sb)) == 0) {
83eaee
-			continue;
83eaee
-		}
83eaee
-		if (status != 0 && errno != ENOENT) {
83eaee
-			ERR(sh, "Unable to access %s: %s\n", cil_path, strerror(errno));
83eaee
-			goto cleanup; //an error in the "stat" call
83eaee
+		if (!semanage_get_ignore_module_cache(sh)) {
83eaee
+			status = stat(cil_path, &sb);
83eaee
+			if (status == 0) {
83eaee
+				status = map_compressed_file(sh, cil_path, &contents);
83eaee
+				if (status < 0) {
83eaee
+					ERR(sh, "Error mapping file: %s", cil_path);
83eaee
+					return -1;
83eaee
+				}
83eaee
+
83eaee
+				/* prefix with length to avoid collisions */
83eaee
+				update_checksum_with_len(&context, contents.len);
83eaee
+				Sha256Update(&context, contents.data, contents.len);
83eaee
+
83eaee
+				unmap_compressed_file(&contents);
83eaee
+				continue;
83eaee
+			} else if (errno != ENOENT) {
83eaee
+				ERR(sh, "Unable to access %s: %s\n", cil_path,
83eaee
+				    strerror(errno));
83eaee
+				return -1; //an error in the "stat" call
83eaee
+			}
83eaee
 		}
83eaee
 
83eaee
-		status = semanage_compile_module(sh, &modinfos[i]);
83eaee
-		if (status < 0) {
83eaee
-			goto cleanup;
83eaee
+		status = semanage_compile_module(sh, &modinfos[i], &context);
83eaee
+		if (status < 0)
83eaee
+			return -1;
83eaee
+	}
83eaee
+	Sha256Finalise(&context, &hash);
83eaee
+
83eaee
+	semanage_hash_to_checksum_string(hash.bytes, cil_checksum);
83eaee
+	return 0;
83eaee
+}
83eaee
+
83eaee
+static int semanage_compare_checksum(semanage_handle_t *sh, const char *reference)
83eaee
+{
83eaee
+	const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM);
83eaee
+	struct stat sb;
83eaee
+	int fd, retval;
83eaee
+	char *data;
83eaee
+
83eaee
+	fd = open(path, O_RDONLY);
83eaee
+	if (fd == -1) {
83eaee
+		if (errno != ENOENT) {
83eaee
+			ERR(sh, "Unable to open %s: %s\n", path, strerror(errno));
83eaee
+			return -1;
83eaee
 		}
83eaee
+		/* Checksum file not present - force a rebuild. */
83eaee
+		return 1;
83eaee
+	}
83eaee
+
83eaee
+	if (fstat(fd, &sb) == -1) {
83eaee
+		ERR(sh, "Unable to stat %s\n", path);
83eaee
+		retval = -1;
83eaee
+		goto out_close;
83eaee
 	}
83eaee
 
83eaee
-	status = 0;
83eaee
+	if (sb.st_size != (off_t)CHECKSUM_CONTENT_SIZE) {
83eaee
+		/* Incompatible/invalid hash type - just force a rebuild. */
83eaee
+		WARN(sh, "Module checksum invalid - forcing a rebuild\n");
83eaee
+		retval = 1;
83eaee
+		goto out_close;
83eaee
+	}
83eaee
 
83eaee
-cleanup:
83eaee
-	return status;
83eaee
+	data = mmap(NULL, CHECKSUM_CONTENT_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
83eaee
+	if (data == MAP_FAILED) {
83eaee
+		ERR(sh, "Unable to mmap %s\n", path);
83eaee
+		retval = -1;
83eaee
+		goto out_close;
83eaee
+	}
83eaee
+
83eaee
+	retval = memcmp(data, reference, CHECKSUM_CONTENT_SIZE) != 0;
83eaee
+	munmap(data, sb.st_size);
83eaee
+out_close:
83eaee
+	close(fd);
83eaee
+	return retval;
83eaee
+}
83eaee
+
83eaee
+static int semanage_write_modules_checksum(semanage_handle_t *sh,
83eaee
+					   const char *checksum)
83eaee
+{
83eaee
+	const char *path = semanage_path(SEMANAGE_TMP, SEMANAGE_MODULES_CHECKSUM);
83eaee
+
83eaee
+	return write_file(sh, path, checksum, CHECKSUM_CONTENT_SIZE);
83eaee
 }
83eaee
 
83eaee
 /* Files that must exist in order to skip policy rebuild. */
83eaee
@@ -1030,6 +1133,7 @@ static int semanage_direct_commit(semanage_handle_t * sh)
83eaee
 	semanage_module_info_t *modinfos = NULL;
83eaee
 	mode_t mask = umask(0077);
83eaee
 	struct stat sb;
83eaee
+	char modules_checksum[CHECKSUM_CONTENT_SIZE + 1 /* '\0' */];
83eaee
 
83eaee
 	int do_rebuild, do_write_kernel, do_install;
83eaee
 	int fcontexts_modified, ports_modified, seusers_modified,
83eaee
@@ -1159,28 +1263,45 @@ static int semanage_direct_commit(semanage_handle_t * sh)
83eaee
 		}
83eaee
 	}
83eaee
 
83eaee
-	/*
83eaee
-	 * If there were policy changes, or explicitly requested, or
83eaee
-	 * any required files are missing, rebuild the policy.
83eaee
-	 */
83eaee
-	if (do_rebuild) {
83eaee
-		/* =================== Module expansion =============== */
83eaee
-
83eaee
+	if (do_rebuild || sh->check_ext_changes) {
83eaee
 		retval = semanage_get_active_modules(sh, &modinfos, &num_modinfos);
83eaee
 		if (retval < 0) {
83eaee
 			goto cleanup;
83eaee
 		}
83eaee
 
83eaee
+		/* No modules - nothing to rebuild. */
83eaee
 		if (num_modinfos == 0) {
83eaee
 			goto cleanup;
83eaee
 		}
83eaee
 
83eaee
-		retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos);
83eaee
+		retval = semanage_compile_hll_modules(sh, modinfos, num_modinfos,
83eaee
+						      modules_checksum);
83eaee
 		if (retval < 0) {
83eaee
 			ERR(sh, "Failed to compile hll files into cil files.\n");
83eaee
 			goto cleanup;
83eaee
 		}
83eaee
 
83eaee
+		if (!do_rebuild && sh->check_ext_changes) {
83eaee
+			retval = semanage_compare_checksum(sh, modules_checksum);
83eaee
+			if (retval < 0)
83eaee
+				goto cleanup;
83eaee
+			do_rebuild = retval;
83eaee
+		}
83eaee
+
83eaee
+		retval = semanage_write_modules_checksum(sh, modules_checksum);
83eaee
+		if (retval < 0) {
83eaee
+			ERR(sh, "Failed to write module checksum file.\n");
83eaee
+			goto cleanup;
83eaee
+		}
83eaee
+	}
83eaee
+
83eaee
+	/*
83eaee
+	 * If there were policy changes, or explicitly requested, or
83eaee
+	 * any required files are missing, rebuild the policy.
83eaee
+	 */
83eaee
+	if (do_rebuild) {
83eaee
+		/* =================== Module expansion =============== */
83eaee
+
83eaee
 		retval = semanage_get_cil_paths(sh, modinfos, num_modinfos, &mod_filenames);
83eaee
 		if (retval < 0)
83eaee
 			goto cleanup;
83eaee
@@ -1696,7 +1817,7 @@ static int semanage_direct_extract(semanage_handle_t * sh,
83eaee
 			goto cleanup;
83eaee
 		}
83eaee
 
83eaee
-		rc = semanage_compile_module(sh, _modinfo);
83eaee
+		rc = semanage_compile_module(sh, _modinfo, NULL);
83eaee
 		if (rc < 0) {
83eaee
 			goto cleanup;
83eaee
 		}
83eaee
diff --git a/libsemanage/src/handle.c b/libsemanage/src/handle.c
83eaee
index e5109aef..8a01c53a 100644
83eaee
--- a/libsemanage/src/handle.c
83eaee
+++ b/libsemanage/src/handle.c
83eaee
@@ -118,20 +118,23 @@ semanage_handle_t *semanage_handle_create(void)
83eaee
 
83eaee
 void semanage_set_rebuild(semanage_handle_t * sh, int do_rebuild)
83eaee
 {
83eaee
-
83eaee
 	assert(sh != NULL);
83eaee
 
83eaee
 	sh->do_rebuild = do_rebuild;
83eaee
-	return;
83eaee
 }
83eaee
 
83eaee
 void semanage_set_reload(semanage_handle_t * sh, int do_reload)
83eaee
 {
83eaee
-
83eaee
 	assert(sh != NULL);
83eaee
 
83eaee
 	sh->do_reload = do_reload;
83eaee
-	return;
83eaee
+}
83eaee
+
83eaee
+void semanage_set_check_ext_changes(semanage_handle_t * sh, int do_check)
83eaee
+{
83eaee
+	assert(sh != NULL);
83eaee
+
83eaee
+	sh->check_ext_changes = do_check;
83eaee
 }
83eaee
 
83eaee
 int semanage_get_hll_compiler_path(semanage_handle_t *sh,
83eaee
diff --git a/libsemanage/src/handle.h b/libsemanage/src/handle.h
83eaee
index a91907b0..c4a6e7ea 100644
83eaee
--- a/libsemanage/src/handle.h
83eaee
+++ b/libsemanage/src/handle.h
83eaee
@@ -62,6 +62,7 @@ struct semanage_handle {
83eaee
 	int is_in_transaction;
83eaee
 	int do_reload;		/* whether to reload policy after commit */
83eaee
 	int do_rebuild;		/* whether to rebuild policy if there were no changes */
83eaee
+	int check_ext_changes;	/* whether to rebuild if external changes are detected via checksum */
83eaee
 	int commit_err;		/* set by semanage_direct_commit() if there are
83eaee
 				 * any errors when building or committing the
83eaee
 				 * sandbox to kernel policy at /etc/selinux
83eaee
diff --git a/libsemanage/src/libsemanage.map b/libsemanage/src/libsemanage.map
83eaee
index a986b2d2..1ef664be 100644
83eaee
--- a/libsemanage/src/libsemanage.map
83eaee
+++ b/libsemanage/src/libsemanage.map
83eaee
@@ -66,4 +66,5 @@ LIBSEMANAGE_1.1 {
83eaee
 
83eaee
 LIBSEMANAGE_3.4 {
83eaee
     semanage_module_compute_checksum;
83eaee
+    semanage_set_check_ext_changes;
83eaee
 } LIBSEMANAGE_1.1;
83eaee
diff --git a/libsemanage/src/modules.c b/libsemanage/src/modules.c
83eaee
index 3a82d275..f1fe160d 100644
83eaee
--- a/libsemanage/src/modules.c
83eaee
+++ b/libsemanage/src/modules.c
83eaee
@@ -1149,9 +1149,9 @@ int semanage_module_remove_key(semanage_handle_t *sh,
83eaee
 }
83eaee
 
83eaee
 static const char CHECKSUM_TYPE[] = "sha256";
83eaee
-static const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE;
83eaee
+const size_t CHECKSUM_CONTENT_SIZE = sizeof(CHECKSUM_TYPE) + 1 + 2 * SHA256_HASH_SIZE;
83eaee
 
83eaee
-static void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum)
83eaee
+void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum)
83eaee
 {
83eaee
 	size_t i;
83eaee
 
83eaee
diff --git a/libsemanage/src/modules.h b/libsemanage/src/modules.h
83eaee
index 8a5c01f4..b828a534 100644
83eaee
--- a/libsemanage/src/modules.h
83eaee
+++ b/libsemanage/src/modules.h
83eaee
@@ -109,4 +109,7 @@ int semanage_module_get_path(semanage_handle_t *sh,
83eaee
 			     char *path,
83eaee
 			     size_t len);
83eaee
 
83eaee
+extern const size_t CHECKSUM_CONTENT_SIZE;
83eaee
+void semanage_hash_to_checksum_string(const uint8_t *hash, char *checksum);
83eaee
+
83eaee
 #endif
83eaee
diff --git a/libsemanage/src/semanage_store.c b/libsemanage/src/semanage_store.c
83eaee
index c5ce071c..1fff667d 100644
83eaee
--- a/libsemanage/src/semanage_store.c
83eaee
+++ b/libsemanage/src/semanage_store.c
83eaee
@@ -115,6 +115,7 @@ static const char *semanage_sandbox_paths[SEMANAGE_STORE_NUM_PATHS] = {
83eaee
 	"/disable_dontaudit",
83eaee
 	"/preserve_tunables",
83eaee
 	"/modules/disabled",
83eaee
+	"/modules_checksum",
83eaee
 	"/policy.kern",
83eaee
 	"/file_contexts.local",
83eaee
 	"/file_contexts.homedirs",
83eaee
diff --git a/libsemanage/src/semanage_store.h b/libsemanage/src/semanage_store.h
83eaee
index b9ec5664..1fc77da8 100644
83eaee
--- a/libsemanage/src/semanage_store.h
83eaee
+++ b/libsemanage/src/semanage_store.h
83eaee
@@ -60,6 +60,7 @@ enum semanage_sandbox_defs {
83eaee
 	SEMANAGE_DISABLE_DONTAUDIT,
83eaee
 	SEMANAGE_PRESERVE_TUNABLES,
83eaee
 	SEMANAGE_MODULES_DISABLED,
83eaee
+	SEMANAGE_MODULES_CHECKSUM,
83eaee
 	SEMANAGE_STORE_KERNEL,
83eaee
 	SEMANAGE_STORE_FC_LOCAL,
83eaee
 	SEMANAGE_STORE_FC_HOMEDIRS,
83eaee
-- 
83eaee
2.30.2
83eaee