[Rpm-maint] [PATCH 06/19] Add rpmpoltrans struct and helper functions

Steve Lawrence slawrence at tresys.com
Tue Feb 2 20:25:09 UTC 2010


The rpmpoltrans struct maintains the state of a policy transaction,
abstracting away the policy installation method (either semodule or
libsemanage). If the semodule binary does not exist or cannot be executed
(the path specified in the new %{__semodule} macro), the policy transaction
falls back to using libsemanage. Additionally, if the transaction is
taking place in a chroot (as specified by the --root option),
libsemanage is used as well. This is required as we cannot rely on files
in a chroot being executable (as in the case of cross installs).

The way the rpmpoltrans struct is used is the actions are set for
individual rpmpol structures (install/upgrade/etc.) via rpmpolSetAction,
and then added to a transaction via rpmpolAdd. That will call the right
functions based on the action to install the policy.

Note: This patch requires libsemanage >= 2.0.40
---
 configure.ac   |   38 ++++++++-
 lib/rpmpol.c   |  276 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/rpmpol.h   |   63 +++++++++++++
 lib/rpmtypes.h |    1 +
 macros.in      |    1 +
 5 files changed, 378 insertions(+), 1 deletions(-)

diff --git a/configure.ac b/configure.ac
index 60c5318..45e7835 100644
--- a/configure.ac
+++ b/configure.ac
@@ -126,6 +126,7 @@ AC_PATH_PROG(__PYTHON, python, /usr/bin/python, $MYPATH)
 AC_PATH_PROG(__RM, rm, /bin/rm, $MYPATH)
 AC_PATH_PROG(__RSH, rsh, /usr/bin/rsh, $MYPATH)
 AC_PATH_PROG(__SED, sed, /bin/sed, $MYPATH)
+AC_PATH_PROG(__SEMODULE, semodule, /usr/sbin/semodule, $MYPATH)
 AC_PATH_PROG(__SSH, ssh, /usr/bin/ssh, $MYPATH)
 AC_PATH_PROG(__TAR, tar, /bin/tar, $MYPATH)
 
@@ -589,11 +590,46 @@ AS_IF([test "$with_selinux" = yes],[
   ],[
     AC_MSG_ERROR([--with-selinux given, but selinux/selinux.h not found])
   ])
+
+  AC_CHECK_HEADER([semanage/semanage.h],[
+    save_LIBS="$LIBS"
+    AC_CHECK_LIB([semanage],[semanage_begin_transaction],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_begin_transaction missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_commit],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_commit missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_connect],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_connect missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_disconnect],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_disconnect missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_handle_create],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_handle_create missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_handle_destroy],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_handle_destroy missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_is_connected],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_is_connected missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_module_install_base_file],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_module_install_base_file missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_module_install_file],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_module_install_file missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_module_remove],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_module_remove missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_select_store],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_select_store missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_set_check_contexts],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_set_check_contexts missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_set_create_store],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_set_create_store missing in libsemanage])])
+    AC_CHECK_LIB([semanage],[semanage_set_reload],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_set_reload missing in libsemanage])])
+    LIBS="$save_LIBS"
+  ],[
+    AC_MSG_ERROR([--with-selinux given, but semanage/semanage.h not found])
+  ])
 ])
 
 AS_IF([test "$with_selinux" = yes],[
   AC_DEFINE(WITH_SELINUX, 1, [Build with selinux support?])
-  WITH_SELINUX_LIB="-lselinux"
+  WITH_SELINUX_LIB="-lselinux -lsemanage"
 ])
 AC_SUBST(WITH_SELINUX_LIB)
 AM_CONDITIONAL(SELINUX,[test "$with_selinux" = yes])
diff --git a/lib/rpmpol.c b/lib/rpmpol.c
index de692fe..3515e82 100644
--- a/lib/rpmpol.c
+++ b/lib/rpmpol.c
@@ -6,11 +6,17 @@
 
 #if WITH_SELINUX
 
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <semanage/semanage.h>
+
 #include <rpm/rpmtypes.h>
 #include <rpm/rpmlib.h>
 #include <rpm/rpmpol.h>
 #include <rpm/rpmlog.h>
 #include <rpm/rpmfileutil.h>
+#include <rpm/rpmmacro.h>
 
 #include "rpmio/base64.h"
 
@@ -31,6 +37,15 @@ struct rpmpol_s {
 	int index;             /*!< index for iterator */
 };
 
+struct rpmpoltrans_s {
+	char * semodulepath;    /*!< path to semodule binary */
+	int execsemodule;       /*!< 0 = use libsemanage to install policy; non-zero = use semodule */
+	semanage_handle_t * sh; /*!< handle to libsemanage, only used when execsemodule is zero */
+	ARGV_t semodargs;       /*!< argument list to pass to semodule, only used when execsemodule is non-zero */
+	ARGV_t filelist;        /*!< list of temporary files that have been written to disk during the transaction */
+	int changes;            /*!< number of changes made during the transaction */
+};
+
 rpmpol rpmpolUnlink(rpmpol pol)
 {
 	if (pol) {
@@ -391,4 +406,265 @@ exit:
 
 	return rc;
 }
+
+rpmpoltrans rpmpoltransBegin(int chroot, const char * type)
+{
+	rpmpoltrans pt = xcalloc(1, sizeof(*pt));
+	pt->semodulepath = rpmExpand("%{__semodule}", NULL);
+	pt->execsemodule = (!chroot && access(pt->semodulepath, X_OK) == 0);
+	pt->changes = 0;
+
+	if (pt->execsemodule) {
+		argvAdd(&pt->semodargs, "semodule");
+		if (type) {
+			argvAdd(&pt->semodargs, "-s");
+			argvAdd(&pt->semodargs, type);
+		}
+	} else {
+		pt->semodulepath = _free(pt->semodulepath);
+
+		pt->sh = semanage_handle_create();
+		if (!pt->sh) {
+			rpmlog(RPMLOG_ERR, "Failed to create semanage handle\n");
+			goto err;
+		}
+		if (type) {
+			semanage_select_store(pt->sh, type, SEMANAGE_CON_DIRECT);
+		}
+		semanage_set_create_store(pt->sh, 1);
+		semanage_set_check_contexts(pt->sh, 0);
+		if (semanage_connect(pt->sh) < 0) {
+			rpmlog(RPMLOG_ERR, "Failed to connect to policy handler\n");
+			goto err;
+		}
+		if (semanage_begin_transaction(pt->sh) < 0) {
+			rpmlog(RPMLOG_ERR, "Failed to begin policy transaction: %s\n", errno ? strerror(errno) : "");
+			goto err;
+		}
+		semanage_set_reload(pt->sh, !chroot);
+	}
+
+	return pt;
+
+err:
+	if (pt->sh) {
+		if (semanage_is_connected(pt->sh)) {
+			semanage_disconnect(pt->sh);
+		}
+		semanage_handle_destroy(pt->sh);
+	}
+	pt = _free(pt);
+
+	return pt;
+}
+
+rpmRC rpmpoltransAdd(rpmpoltrans pt, rpmpol pol)
+{
+	char * path = NULL;
+	const char * name;
+	rpmRC rc = RPMRC_FAIL;
+	int action;
+
+	if (!pt || !pol) {
+		return rc;
+	}
+
+	if (!rpmpolIsValidIndex(pol)) {
+		return rc;
+	}
+
+	action = rpmpolGetAction(pol);
+	if (action == RPMPOL_ACTION_INSTALL || action == RPMPOL_ACTION_UPGRADE) {
+		rc = rpmpolWritePolicy(pol, &path);
+		if (rc != RPMRC_OK) {
+			return rc;
+		}
+		argvAdd(&pt->filelist, path);
+	}
+
+	name = rpmpolGetName(pol);
+
+	switch(action) {
+	case RPMPOL_ACTION_REMOVE:
+		if (rpmpolGetFlags(pol) & RPMPOL_FLAG_BASE) {
+			rc = RPMRC_FAIL; /* can't remove a base module */
+		} else {
+			rc = rpmpoltransRemove(pt, name);
+		}
+		break;
+	case RPMPOL_ACTION_INSTALL:
+	case RPMPOL_ACTION_UPGRADE:
+		if (rpmpolGetFlags(pol) & RPMPOL_FLAG_BASE) {
+			rc = rpmpoltransInstallBase(pt, path, name);
+		} else {
+			rc = rpmpoltransInstall(pt, path, name);
+		}
+		break;
+	case RPMPOL_ACTION_IGNORE:
+	default:
+		rc = RPMRC_OK;
+		break;
+	}
+
+	_free(path);
+
+	return rc;
+}
+
+rpmRC rpmpoltransInstall(rpmpoltrans pt, const char * path, const char * name)
+{
+	rpmRC rc = RPMRC_OK;
+
+	if (!pt || !path) {
+		return RPMRC_FAIL;
+	}
+
+	if (pt->execsemodule) {
+		if (argvAdd(&pt->semodargs, "-i") < 0 || argvAdd(&pt->semodargs, path) < 0) {
+			rc = RPMRC_FAIL;
+		}
+	} else {
+		if (semanage_module_install_file(pt->sh, path) < 0) {
+			rc = RPMRC_FAIL;
+		}
+	}
+
+	if (rc != RPMRC_OK) {
+		rpmlog(RPMLOG_ERR, "Failed to install policy module: %s (%s)\n", name, path);
+	} else {
+		pt->changes++;
+	}
+
+	return rc;
+}
+
+rpmRC rpmpoltransInstallBase(rpmpoltrans pt, const char * path, const char * name)
+{
+	rpmRC rc = RPMRC_OK;
+
+	if (!pt || !path) {
+		return RPMRC_FAIL;
+	}
+
+	if (pt->execsemodule) {
+		if (argvAdd(&pt->semodargs, "-b") < 0 || argvAdd(&pt->semodargs, path) < 0 ) {
+			rc = RPMRC_FAIL;
+		}
+	} else {
+		if (semanage_module_install_base_file(pt->sh, path) < 0) {
+			rc = RPMRC_FAIL;
+		}
+	}
+
+	if (rc != RPMRC_OK) {
+		rpmlog(RPMLOG_ERR, "Failed to install base policy module: %s (%s)\n", name, path);
+	} else {
+		pt->changes++;
+	}
+
+	return rc;
+}
+
+rpmRC rpmpoltransRemove(rpmpoltrans pt, const char * name)
+{
+	rpmRC rc = RPMRC_OK;
+
+	if (!pt || !name) {
+		return RPMRC_FAIL;
+	}
+
+	if (pt->execsemodule) {
+		if (argvAdd(&pt->semodargs, "-r") < 0 || argvAdd(&pt->semodargs, name) < 0) {
+			rc = RPMRC_FAIL;
+		}
+	} else {
+		if (semanage_module_remove(pt->sh, name) < 0) {
+			rc = RPMRC_FAIL;
+		}
+	}
+
+	if (rc != RPMRC_OK) {
+		rpmlog(RPMLOG_ERR, "Failed to remove policy module: %s\n", name);
+	} else {
+		pt->changes++;
+	}
+
+	return rc;
+}
+
+rpmRC rpmpoltransCommit(rpmpoltrans pt)
+{
+	pid_t pid;
+	int status;
+	rpmRC rc = RPMRC_OK;
+
+	if (!pt) {
+		return RPMRC_FAIL;
+	}
+
+	if (pt->execsemodule) {
+		if (pt->changes > 0) {
+			pid = fork();
+			switch (pid) {
+			case -1:
+				rpmlog(RPMLOG_ERR, "Failed to fork process: %s\n", strerror(errno));
+				rc = RPMRC_FAIL;
+				break;
+			case 0:
+				if (!rpmIsDebug()) {
+					freopen("/dev/null", "r", stdin);
+					freopen("/dev/null", "w", stdout);
+					freopen("/dev/null", "w", stderr);
+				}
+				execv(pt->semodulepath, pt->semodargs);
+				rpmlog(RPMLOG_ERR, "Failed to execute %s: %s\n", pt->semodulepath, strerror(errno));
+				exit(1);
+			default:
+				waitpid(pid, &status, 0);
+				if (!WIFEXITED(status)) {
+					rpmlog(RPMLOG_ERR, "%s terminated abnormally\n", pt->semodulepath);
+					rc = RPMRC_FAIL;
+				} else if(WEXITSTATUS(status)) {
+					rpmlog(RPMLOG_ERR, "%s failed with exit code %i\n", pt->semodulepath, WEXITSTATUS(status));
+					rc = RPMRC_FAIL;
+				}
+			}
+		}
+	} else {
+		if (semanage_commit(pt->sh) < 0) {
+			rpmlog(RPMLOG_ERR, "Failed to commit policy changes\n");
+			rc = RPMRC_FAIL;
+		}
+	}
+
+	return rc;
+}
+
+rpmpoltrans rpmpoltransFree(rpmpoltrans pt)
+{
+	ARGV_t file;
+
+	if (!pt) {
+		return NULL;
+	}
+
+	for (file = pt->filelist; file && *file; file++) {
+		if (unlink(*file) < 0) {
+			rpmlog(RPMLOG_WARNING, "Failed to remove temporary policy file %s: %s\n", *file, strerror(errno));
+		}
+	}
+	argvFree(pt->filelist);
+
+	if (pt->execsemodule) {
+		argvFree(pt->semodargs);
+	} else {
+		semanage_disconnect(pt->sh);
+		semanage_handle_destroy(pt->sh);
+	}
+
+	pt->semodulepath = _free(pt->semodulepath);
+
+	pt = _free(pt);
+	return NULL;
+}
 #endif				/* WITH_SELINUX */
diff --git a/lib/rpmpol.h b/lib/rpmpol.h
index c3b78cb..1902238 100644
--- a/lib/rpmpol.h
+++ b/lib/rpmpol.h
@@ -177,6 +177,69 @@ rpmRC rpmpolWritePolicy(const rpmpol pol, char ** path);
 RPM_GNUC_INTERNAL
 int rpmpolIsValidIndex(const rpmpol pol);
 
+/** \ingroup rpmpol
+ * Begin an rpm policy transaction
+ * @param chroot	boolean, is the policy being installed in a chroot? 0 for no, non-zero for yes
+ * @param type	type to install to during the transaction, or NULL to use the default
+ * @return		new rpmpoltrans struct, or NULL on failure
+ */
+RPM_GNUC_INTERNAL
+rpmpoltrans rpmpoltransBegin(int chroot, const char * type);
+
+/** \ingroup rpmpol
+ * Add an rpm policy element to an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @param pol	rpm policy element
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransAdd(rpmpoltrans pt, rpmpol pol);
+
+/** \ingroup rpmpol
+ * Commit the rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransCommit(rpmpoltrans pt);
+
+/** \ingroup rpmpol
+ * Free an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @return		NULL
+ */
+RPM_GNUC_INTERNAL
+rpmpoltrans rpmpoltransFree(rpmpoltrans pt);
+
+/** \ingroup rpmpol
+ * Install a module in an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @param path	path to module
+ * @param name	module name
+ * @return		RPMRC_OK on sucess, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransInstall(rpmpoltrans pt, const char * path, const char * name);
+
+/** \ingroup rpmpol
+ * Install a base module in an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @param path	path to base module
+ * @param name	module name
+ * @return		RPMRC_OK on sucess, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransInstallBase(rpmpoltrans pt, const char * path, const char * name);
+
+/** \ingroup rpmpol
+ * Remove a module in an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @param name	module name to remove
+ * @return		RPMRC_OK on sucess, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransRemove(rpmpoltrans pt, const char * name);
+
 
 #ifdef __cplusplus
 }
diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
index 6a7b5ec..f2a0509 100644
--- a/lib/rpmtypes.h
+++ b/lib/rpmtypes.h
@@ -62,6 +62,7 @@ typedef struct rpmds_s * rpmds;
 typedef struct rpmfi_s * rpmfi;
 typedef struct rpmdb_s * rpmdb;
 typedef struct rpmpol_s * rpmpol;
+typedef struct rpmpoltrans_s * rpmpoltrans;
 typedef struct rpmdbMatchIterator_s * rpmdbMatchIterator;
 typedef struct rpmtsi_s * rpmtsi;
 
diff --git a/macros.in b/macros.in
index 870a309..be98ba0 100644
--- a/macros.in
+++ b/macros.in
@@ -60,6 +60,7 @@
 %__rm			@__RM@
 %__rsh			@__RSH@
 %__sed			@__SED@
+%__semodule		@__SEMODULE@
 %__ssh			@__SSH@
 %__tar			@__TAR@
 %__unzip		@__UNZIP@
-- 
1.6.2.5



More information about the Rpm-maint mailing list