[Rpm-maint] [RFC PATCH 2/3] Add new structures/functions for managing policy information

Steve Lawrence slawrence at tresys.com
Fri Sep 4 15:29:03 UTC 2009


Two new structures are added: rpmpol and rpmpoltrans

rpmpol is essentially a list of policies extracted from a single package.
It contains an iterator and getters to get the necessary information.
As more policy information is added to rpm headers (based on a new
%policy directive) this struct will be updated to store the new
information and functions will be added to get/set the new data.

rpmpoltrans maintains the state of the policy transaction, abstracting
away the policy installation method (either semodule or libsemanage).
Currently, which method is chosen is based on the existence of semodule,
but this will change somewhat as chroot functionality is added.

Also added is a new macro which can be used to define the path of
semodule.
---
 Makefile.am     |    1 +
 configure.ac    |   34 +++++-
 lib/Makefile.am |    2 +-
 lib/rpmpol.c    |  354 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/rpmpol.h    |  142 ++++++++++++++++++++++
 lib/rpmtypes.h  |    2 +
 macros.in       |    1 +
 preinstall.am   |    4 +
 8 files changed, 536 insertions(+), 4 deletions(-)
 create mode 100644 lib/rpmpol.c
 create mode 100644 lib/rpmpol.h

diff --git a/Makefile.am b/Makefile.am
index aa87b8c..40354c8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -62,6 +62,7 @@ pkginclude_HEADERS += lib/rpmfi.h
 pkginclude_HEADERS += lib/rpmgi.h
 pkginclude_HEADERS += lib/rpmlegacy.h
 pkginclude_HEADERS += lib/rpmps.h
+pkginclude_HEADERS += lib/rpmpol.h
 pkginclude_HEADERS += lib/rpmtag.h
 pkginclude_HEADERS += lib/rpmtd.h
 pkginclude_HEADERS += lib/rpmte.h
diff --git a/configure.ac b/configure.ac
index 26edfa3..56808bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -177,6 +177,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)
 
@@ -728,16 +729,43 @@ esac],
 
 AS_IF([test "$with_selinux" = yes],[
   AC_CHECK_HEADER([selinux/selinux.h],[
-    AC_CHECK_LIB(selinux,[is_selinux_enabled],[with_selinux=yes],[
-      AC_MSG_ERROR([--with-selinux given, but libselinux not found])])
+    save_LIBS="$LIBS"
+    AC_CHECK_LIB([selinux],[is_selinux_enabled],[],[
+      AC_MSG_ERROR([--with-selinux given, but is_selinux_enabled not found in libselinux])])
+    LIBS="$save_LIBS"
   ],[
     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_file],[],[
+      AC_MSG_ERROR([--with-selinux given, but semanage_module_install_file 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])])
+    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/Makefile.am b/lib/Makefile.am
index 1467a55..cf28b54 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -27,7 +27,7 @@ librpm_la_SOURCES = \
 	cpio.c cpio.h depends.c order.c formats.c tagexts.c fs.c fsm.c fsm.h \
 	manifest.c manifest.h misc.c package.c \
 	poptALL.c poptI.c poptQV.c psm.c psm.h query.c \
-	rpmal.c rpmal.h rpmchecksig.c rpmds.c rpmfi.c rpmfi_internal.h rpmgi.c \
+	rpmal.c rpmal.h rpmchecksig.c rpmds.c rpmfi.c rpmfi_internal.h rpmgi.c rpmpol.c \
 	rpminstall.c rpmts_internal.h \
 	rpmlead.c rpmlead.h rpmps.c rpmrc.c \
 	rpmte.c rpmte_internal.h rpmts.c \
diff --git a/lib/rpmpol.c b/lib/rpmpol.c
new file mode 100644
index 0000000..c29c99e
--- /dev/null
+++ b/lib/rpmpol.c
@@ -0,0 +1,354 @@
+/** \ingroup rpmdep
+ * \file lib/rpmpol.c
+ */
+#include "system.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"
+
+#include "debug.h"
+
+/**
+ * Package policy set.
+ */
+struct rpmpol_s {
+	char ** data;          /*!< policy data */
+	int count;             /*!< number of policies */
+	int nrefs;             /*!< reference counting */
+	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 */
+};
+
+rpmpol rpmpolUnlink(rpmpol pol)
+{
+	if (pol) {
+		pol->nrefs--;
+	}
+	return NULL;
+}
+
+rpmpol rpmpolLink(rpmpol pol)
+{
+	if (pol) {
+		pol->nrefs++;
+	}
+	return pol;
+}
+
+rpmpol rpmpolFree(rpmpol pol)
+{
+	if (pol == NULL) {
+		return NULL;
+	}
+
+	if (pol->nrefs > 1) {
+		return rpmpolUnlink(pol);
+	}
+
+	pol->data  = _free(pol->data);
+
+	rpmpolUnlink(pol);
+	pol = _free(pol);
+	return NULL;
+}
+
+rpmpol rpmpolNew(Header h)
+{
+	rpmpol pol = NULL;
+	struct rpmtd_s policies;
+	int count;
+
+	rpmtdReset(&policies);
+
+	if (!headerIsEntry(h, RPMTAG_POLICIES)) {
+		goto err;
+	}
+
+	if (!headerGet(h, RPMTAG_POLICIES, &policies, HEADERGET_ALLOC | HEADERGET_ARGV)) {
+		goto err;
+	}
+
+	count = rpmtdCount(&policies);
+	if (count <= 0) {
+		goto err;
+	}
+
+	pol = xcalloc(1, sizeof(*pol));
+	pol->count = count;
+	pol->index = -1;
+	pol->data = policies.data;
+
+	pol = rpmpolLink(pol);
+
+	return pol;
+
+ err:
+	rpmtdFreeData(&policies);
+	return NULL;
+}
+
+rpmpol rpmpolInitIterator(rpmpol pol)
+{
+	if (pol) {
+		pol->index = -1;
+	}
+
+	return pol;
+}
+
+int rpmpolIsValidIndex(const rpmpol pol)
+{
+	return (pol && pol->index >= 0 && pol->index < pol->count);
+}
+
+int rpmpolNext(rpmpol pol)
+{
+	int i = -1;
+
+	if (pol) {
+		pol->index++;
+		if (rpmpolIsValidIndex(pol)) {
+			i = pol->index;
+		} else {
+			pol->index = pol->count;
+		}
+	}
+
+	return i;
+}
+
+int rpmpolCount(rpmpol pol)
+{
+	if (!pol) {
+		return -1;
+	}
+
+	return pol->count;
+}
+
+const char *rpmpolGetData(const rpmpol pol)
+{
+	if (pol && pol->data && rpmpolIsValidIndex(pol)) {
+		return pol->data[pol->index];
+	}
+
+	return NULL;
+}
+
+rpmRC rpmpolWritePolicy(const rpmpol pol, char ** path)
+{
+	char * tmppath = NULL;
+	FD_t fd = NULL;
+	char * policy = NULL;
+	size_t policylen;
+	int rc = RPMRC_FAIL;
+	const char * data;
+
+	data = rpmpolGetData(pol);
+	if (data == NULL) {
+		goto exit;
+	}
+
+	if (b64decode(data, (void**)&policy, &policylen) != 0) {
+		rpmlog(RPMLOG_ERR, _("Failed to decode policy\n"));
+		goto exit;
+	}
+
+	fd = rpmMkTempFile(NULL, &tmppath);
+	if (fd == NULL || Ferror(fd)) {
+		rpmlog(RPMLOG_ERR, _("Failed to create temporary file %s\n"), tmppath);
+		goto exit;
+	}
+
+	if (!Fwrite(policy, sizeof(*policy), policylen, fd)) {
+		rpmlog(RPMLOG_ERR, _("Failed to write policy to file %s\n"), tmppath);
+		goto exit;
+	}
+
+	*path = tmppath;
+	rc = RPMRC_OK;
+
+exit:
+	if (fd) Fclose(fd);
+	_free(policy);
+	if (rc != RPMRC_OK) _free(tmppath);
+
+	return rc;
+}
+
+rpmpoltrans rpmpoltransBegin()
+{
+	rpmpoltrans pt = xcalloc(1, sizeof(*pt));
+	pt->semodulepath = rpmExpand("%{__semodule}", NULL);
+	pt->execsemodule = (access(pt->semodulepath, X_OK) == 0);
+
+	if (pt->execsemodule) {
+		argvAdd(&pt->semodargs, "semodule");
+	} 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;
+		}
+		semanage_set_create_store(pt->sh, 1);
+		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;
+		}
+		goto success;
+err:
+		if (pt->sh) {
+			if (semanage_is_connected(pt->sh)) {
+				semanage_disconnect(pt->sh);
+			}
+			semanage_handle_destroy(pt->sh);
+		}
+		pt = _free(pt);
+	}
+success:
+
+	return pt;
+}
+
+rpmRC rpmpoltransAdd(rpmpoltrans pt, rpmpol pol)
+{
+	char * path = NULL;
+	rpmRC rc = RPMRC_FAIL;
+
+	if (!pt) {
+		return rc;
+	}
+
+	if (!rpmpolIsValidIndex(pol)) {
+		return rc;
+	}
+
+	rc = rpmpolWritePolicy(pol, &path);
+	if (rc != RPMRC_OK) {
+		return rc;
+	}
+	argvAdd(&pt->filelist, path);
+
+	rc = rpmpoltransInstall(pt, path);
+
+	return rc;
+}
+
+rpmRC rpmpoltransInstall(rpmpoltrans pt, const char * path)
+{
+	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) {
+			rpmlog(RPMLOG_ERR, "Failed to install policy file: %s\n", path);
+			rc = RPMRC_FAIL;
+		}
+	}
+
+	return rc;
+}
+
+rpmRC rpmpoltransCommit(rpmpoltrans pt)
+{
+	pid_t pid;
+	int status;
+	rpmRC rc = RPMRC_OK;
+
+	if (!pt) {
+		return RPMRC_FAIL;
+	}
+
+	if (pt->execsemodule) {
+		/* the first argument in pt->semodargs is always 'semodule'. So only exec
+		 * semodule if there are some additional arguments that were passed to it */
+		if (argvCount(pt->semodargs) > 1) {
+			pid = fork();
+			switch (pid) {
+			case -1:
+				rpmlog(RPMLOG_ERR, "Failed to fork process: %s\n", strerror(errno));
+				rc = RPMRC_FAIL;
+				break;
+			case 0:
+				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 semodule: %s\n", strerror(errno));
+				exit(1);
+			default:
+				waitpid(pid, &status, 0);
+				if (!WIFEXITED(status)) {
+					rpmlog(RPMLOG_ERR, "semodule terminated abnormally\n");
+					rc = RPMRC_FAIL;
+				} else if(WEXITSTATUS(status)) {
+					rpmlog(RPMLOG_ERR, "semodule failed with exit code %i\n", 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;
+}
diff --git a/lib/rpmpol.h b/lib/rpmpol.h
new file mode 100644
index 0000000..fb38054
--- /dev/null
+++ b/lib/rpmpol.h
@@ -0,0 +1,142 @@
+#ifndef H_RPMPOL
+#define H_RPMPOL
+
+/** \ingroup rpmpol
+ * \file lib/rpmpol.h
+ * Structure(s) used for policy sets.
+ */
+
+#include <rpm/rpmtypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \ingroup rpmpol
+ * Unreference a policy set instance.
+ * @param pol	policy set
+ * @return		NULL always
+ */
+RPM_GNUC_INTERNAL
+rpmpol rpmpolUnlink(rpmpol pol);
+
+/** \ingroup rpmpol
+ * Reference a policy set instance.
+ * @param pol		policy set
+ * @return		new policy set reference
+ */
+RPM_GNUC_INTERNAL
+rpmpol rpmpolLink(rpmpol pol);
+
+/** \ingroup rpmpol
+ * Destroy a policy set.
+ * @param pol		policy set
+ * @return		NULL always
+ */
+RPM_GNUC_INTERNAL
+rpmpol rpmpolFree(rpmpol pol);
+
+/** \ingroup rpmpol
+ * Create a new policy set.
+ * @param h		header
+ * @return		new policy set
+ */
+RPM_GNUC_INTERNAL
+rpmpol rpmpolNew(Header h);
+
+/** \ingroup rpmpol
+ * Create policy element iterator.
+ * @param pol		policy set
+ * @return		policy set
+ */
+RPM_GNUC_INTERNAL
+rpmpol rpmpolInitIterator(rpmpol pol);
+
+/** \ingroup rpmpol
+ * Return index of next policy element.
+ * @param pol		policy set
+ * @return		next index of policy set, -1 on termination
+ */
+RPM_GNUC_INTERNAL
+int rpmpolNext(rpmpol pol);
+
+/** \ingroup rpmpol
+ * Return policy set count.
+ * @param pol		policy set
+ * @return		current count
+ */
+RPM_GNUC_INTERNAL
+int rpmpolCount(const rpmpol pol);
+
+/** \ingroup rpmpol
+ * Return current policy data.
+ * @param pol		policy set
+ * @return		current policy data, NULL on invalid
+ */
+RPM_GNUC_INTERNAL
+const char *rpmpolGetData(const rpmpol pol);
+
+/** \ingroup rpmpol
+ * Write current policy to a temporary file.
+ * @param pol		policy set
+ * @param path		path to file created, or unchanged if RPMRC_OK is not returned.
+ *                  If RPMRC_OK is returned, the caller must free path
+ * @return		RPMRC_OK on success, RPMRC_FAILED on failure
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpolWritePolicy(const rpmpol pol, char ** path);
+
+/** \ingroup rpmpol
+ * Determine if the current policy index if valid
+ * @param pol		policy set
+ * @return		1 if index is valid, 0 otherwise
+ */
+RPM_GNUC_INTERNAL
+int rpmpolIsValidIndex(const rpmpol pol);
+
+/** \ingroup rpmpol
+ * Begin an rpm policy transaction
+ * @return		new rpmpoltrans struct, or NULL on failure
+ */
+RPM_GNUC_INTERNAL
+rpmpoltrans rpmpoltransBegin(void);
+
+/** \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
+ * @return		RPMRC_OK on sucess, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransInstall(rpmpoltrans pt, const char * path);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* H_rpmpol */
diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
index 99a28e2..86b2b01 100644
--- a/lib/rpmtypes.h
+++ b/lib/rpmtypes.h
@@ -61,6 +61,8 @@ typedef struct rpmte_s * rpmte;
 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 const void * fnpyKey;
 typedef void * rpmCallbackData;
diff --git a/macros.in b/macros.in
index c4de1fa..520c721 100644
--- a/macros.in
+++ b/macros.in
@@ -58,6 +58,7 @@
 %__rm			@__RM@
 %__rsh			@__RSH@
 %__sed			@__SED@
+%__semodule		@__SEMODULE@
 %__ssh			@__SSH@
 %__tar			@__TAR@
 %__unzip		@__UNZIP@
diff --git a/preinstall.am b/preinstall.am
index d79a9f0..1e0cfa2 100644
--- a/preinstall.am
+++ b/preinstall.am
@@ -86,6 +86,10 @@ include/rpm/rpmps.h: lib/rpmps.h include/rpm/$(dirstamp)
 	$(INSTALL_DATA) $(top_srcdir)/lib/rpmps.h include/rpm/rpmps.h
 BUILT_SOURCES += include/rpm/rpmps.h
 CLEANFILES += include/rpm/rpmps.h
+include/rpm/rpmpol.h: lib/rpmpol.h include/rpm/$(dirstamp)
+	$(INSTALL_DATA) $(top_srcdir)/lib/rpmpol.h include/rpm/rpmpol.h
+BUILT_SOURCES += include/rpm/rpmpol.h
+CLEANFILES += include/rpm/rpmpol.h
 include/rpm/rpmtag.h: lib/rpmtag.h include/rpm/$(dirstamp)
 	$(INSTALL_DATA) $(top_srcdir)/lib/rpmtag.h include/rpm/rpmtag.h
 BUILT_SOURCES += include/rpm/rpmtag.h
-- 
1.6.0.6



More information about the Rpm-maint mailing list