[Rpm-maint] [PATCH 07/18] Add rpmpols struct plus some helper functions

Steve Lawrence slawrence at tresys.com
Wed Dec 23 20:57:27 UTC 2009


The policy set structure stores information about all the policies and is
used to perform various checks and preparations for the policy installation.

The order in which the functions should be called (and their purpose) is as follows:

rpmpolsNew
 Create an empty rpmpols structure

rpmpolsAdd
 Add rpmpol structures to the policy set

rpmpolsSetType
 Set the type of policy to deal with

rpmpolsPrepare
 Calculate the actions to take on the rpmpol structures. Base on what is
 currently installed and what is obsoleted, policies are assigned actions
 (upgrade/install/etc). These actions are later used during the policy
 transaction to determine how what and how policies should be installed.

rpmpolsInstall
 Create a new policy transaction, add the policies to the transaction
 (based on their actions) and finally commit the transaction, installing
 all policies.

rpmpolsSaveStave
 Save the policies changes to the rpm database. Due to security reasons,
 policy is not removed when the package that installed it is removed. Because
 of this, we need a way to keep track of which policies have been installed
 and their properties. To accomplish this, we create pseudo packages
 (akin to gpg-pubkey) containing the necessary information and update the
 rpm database.

rpmpolsFree
 Clean up the rpmols structure
---
 lib/rpmpol.c   |  441 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/rpmpol.h   |   87 +++++++++++
 lib/rpmtypes.h |    1 +
 3 files changed, 529 insertions(+), 0 deletions(-)

diff --git a/lib/rpmpol.c b/lib/rpmpol.c
index 18b8419..bd09689 100644
--- a/lib/rpmpol.c
+++ b/lib/rpmpol.c
@@ -11,6 +11,9 @@
 #include <rpm/rpmlog.h>
 #include <rpm/rpmfileutil.h>
 #include <rpm/rpmmacro.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmds.h>
+#include <rpm/rpmts.h>
 
 #include "rpmio/base64.h"
 
@@ -31,6 +34,14 @@ struct rpmpol_s {
 	int index;             /*!< index for iterator */
 };
 
+struct rpmpols_s {
+	char * type;
+	rpmpol * pols;          /*!< array of rpmpols */
+	ARGV_t obsoleted;       /*!< list of obsoleted policies */
+	unsigned int count;     /*!< number of rpmpols */
+	size_t capacity;        /*!< alloced size */
+};
+
 struct rpmpoltrans_s {
 	char * semodulepath;    /*!< path to semodule binary */
 	int execsemodule;       /*!< 0 = use libsemanage to install policy; non-zero = use semodule */
@@ -401,6 +412,63 @@ exit:
 	return rc;
 }
 
+Header rpmpolMakeHeader(const rpmpol pol, const char * type, rpm_tid_t tid)
+{
+	Header h = NULL;
+	uint32_t zero = 0;
+	const char * data;
+	const char * name;
+	uint32_t flags;
+	ARGV_const_t types;
+	ARGV_const_t obs;
+	ARGV_const_t arg;
+
+	if (!pol || !rpmpolIsValidIndex(pol) || !type) {
+		goto exit;
+	}
+
+	data = rpmpolGetData(pol);
+	name = rpmpolGetName(pol);
+	flags = rpmpolGetFlags(pol);
+	types = rpmpolGetTypes(pol);
+	obs = rpmpolGetObsoletes(pol);
+
+	if (!data || !name || !types) {
+		goto exit;
+	}
+
+	h = headerNew();
+
+	/* package name = RPMPOL_PKG_NAME-<type>-<name> */
+	headerPutString(h, RPMTAG_NAME, RPMPOL_PKG_NAME);
+	headerPutString(h, RPMTAG_VERSION, type);
+	headerPutString(h, RPMTAG_RELEASE, name);
+
+	headerPutString(h, RPMTAG_POLICIES, data);
+	headerPutString(h, RPMTAG_POLICYNAMES, name);
+	headerPutUint32(h, RPMTAG_POLICYFLAGS, &flags, 1);
+
+	for (arg = types; arg && *arg; arg++) {
+		headerPutString(h, RPMTAG_POLICYTYPES, *arg);
+		headerPutUint32(h, RPMTAG_POLICYTYPESINDEXES, &zero, 1);
+	}
+
+	for (arg = obs; arg && *arg; arg++) {
+		headerPutString(h, RPMTAG_POLICYOBSOLETES, *arg);
+		headerPutUint32(h, RPMTAG_POLICYOBSOLETESBY, &zero, 1);
+	}
+
+	headerPutUint32(h, RPMTAG_SIZE, &zero, 1);
+	headerPutUint32(h, RPMTAG_BUILDTIME, &tid, 1);
+	headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1);
+	headerPutString(h, RPMTAG_RPMVERSION, RPMVERSION);
+	headerPutString(h, RPMTAG_BUILDHOST, "localhost");
+	headerPutString(h, RPMTAG_SOURCERPM, "(none)");
+
+exit:
+	return h;
+}
+
 rpmpoltrans rpmpoltransBegin(int chroot, const char * type)
 {
 	rpmpoltrans pt = xcalloc(1, sizeof(*pt));
@@ -452,6 +520,43 @@ err:
 	return pt;
 }
 
+rpmRC rpmpoltransAddSet(rpmpoltrans pt, rpmpols ps)
+{
+	rpmRC rc = RPMRC_FAIL;
+	ARGV_const_t obs;
+	unsigned int i;
+
+	if (!pt || !ps) {
+		return rc;
+	}
+
+	/* remove all obsoleted policies */
+	for (obs = ps->obsoleted; obs && *obs; obs++) {
+		rc = rpmpoltransRemove(pt, *obs);
+		if (rc != RPMRC_OK) {
+			goto exit;
+		}
+	}
+
+	/* add the to-be-installed policies */
+	for (i = 0; i < ps->count; i++) {
+		rpmpol pol = ps->pols[i];
+
+		rpmpolInitIterator(pol);
+		while (rpmpolNext(pol) >= 0) {
+			rc = rpmpoltransAdd(pt, pol);
+			if (rc != RPMRC_OK) {
+				goto exit;
+			}
+		}
+	}
+
+	rc = RPMRC_OK;
+
+exit:
+	return rc;
+}
+
 rpmRC rpmpoltransAdd(rpmpoltrans pt, rpmpol pol)
 {
 	char * path = NULL;
@@ -657,3 +762,339 @@ rpmpoltrans rpmpoltransFree(rpmpoltrans pt)
 	pt = _free(pt);
 	return NULL;
 }
+
+rpmpols rpmpolsNew(void)
+{
+	rpmpols ps = xcalloc(1, sizeof(*ps));
+	ps->type = NULL;
+	ps->obsoleted = NULL;
+	ps->count = 0;
+	ps->capacity = 16;
+	ps->pols = xcalloc(ps->capacity, sizeof(*ps->pols));
+	return ps;
+}
+
+rpmpols rpmpolsFree(rpmpols ps)
+{
+	unsigned int i;
+
+	if (!ps) {
+		return NULL;
+	}
+
+	for (i = 0; i < ps->count; i++) {
+		ps->pols[i] = rpmpolFree(ps->pols[i]);
+	}
+	ps->pols = _free(ps->pols);
+	ps->type = _free(ps->type);
+	ps->obsoleted = argvFree(ps->obsoleted);
+	ps = _free(ps);
+	return NULL;
+}
+
+rpmRC rpmpolsAdd(rpmpols ps, rpmpol pol)
+{
+	if (!ps || !pol) {
+		return RPMRC_FAIL;
+	}
+
+	if (ps->count == ps->capacity) {
+		ps->capacity *= 2;
+		ps->pols = xrealloc(ps->pols, ps->capacity);
+	}
+
+	ps->pols[ps->count] = rpmpolLink(pol);
+	ps->count++;
+
+	return RPMRC_OK;
+}
+
+void rpmpolsSetType(rpmpols ps, const char * type)
+{
+	ps->type = xstrdup(type);
+}
+
+const char * rpmpolsGetType(rpmpols ps)
+{
+	return ps ? ps->type : NULL;
+}
+
+/*
+ * Determine whether to install/udpate/remove/etc each rpmpol element
+ *
+ * First, we essentially get three sets: Obsoletes Policies, Currently
+ * Installed Policies, and Policies to be installed by this rpm
+ * transaction. The set of obsoleted policies contains policies
+ * obsoleted by currently installed policies as well as policies obsoleted
+ * by to be installed policies.
+ *
+ * With these three sets, below is how we determine what action to take on
+ * the policies:
+ *
+ *          `:::::::::::::-    .:::::::::::::-
+ *        `//-             `+ss-             .:/-
+ *      `/:               `+: `/:               ./-
+ *     -+`               :/     .o`               -+
+ *    -+   Obsoleted    :/       `o`   Currently   .o
+ *    s    Policies    `o         .o    Installed   -/
+ *   ::                +.    R     o`   Policies     s
+ *   +`                o           +.                o
+ *   :-      X         o/::::::::::y:`       X       s
+ *   `o             -/:-+         `o`-//`           -/
+ *    -/          -/.   /:  X/R   o`    :/`        `o
+ *     -+        +-      //     `o`      `+-      .+
+ *      `+:     o`   X    .+- `//     U    /-   `/:
+ *        .//. /-           +sy.            o.:/:
+ *           .:h:::----:::::`  -::::--.--:::+o`
+ *             o   ````              ````    o
+ *             s                            .+
+ *             +.      To-Be-Installed      o`
+ *              s`        Policies         :/
+ *              `o.                       /:
+ *                //`         I         -+.
+ *                 `:/-              `:/-
+ *                    `:::::-...-::::-
+ *                          `..``
+ * I - Install
+ * U - Upgrade
+ * R - Remove
+ * X - Do Nothing/Ignore/Don't Install
+ *
+ * Note: The center is marked as X/R because to-be-installed policies are
+ * ignored (not installed), but the policies are added to the list of curerntly
+ * installed policies to remove (this prevents removing a policy module twice)
+ */
+rpmRC rpmpolsPrepare(rpmpols ps, rpmdb db)
+{
+	rpmpol pol;
+	Header h = NULL;
+	rpmdbMatchIterator mi = NULL;
+	ARGV_t obsList = NULL;
+	ARGV_t curList = NULL;
+	ARGV_t obs;
+	ARGV_t cur;
+	int changes = 0;
+	unsigned int i;
+	rpmRC rc = RPMRC_FAIL;
+
+	if (!ps || !db) {
+		return rc;
+	}
+
+	if(ps->count == 0) {
+		return RPMRC_NOTFOUND;
+	}
+
+	/* generate list of policies obsoleted by the to-be-installed policies */
+	for (i = 0; i < ps->count; i++) {
+		pol = ps->pols[i];
+
+		rpmpolInitIterator(pol);
+		while (rpmpolNext(pol) >= 0) {
+			if (!rpmpolHasType(pol, ps->type)) {
+				continue;
+			}
+			argvAppend(&obsList, rpmpolGetObsoletes(pol));
+		}
+	}
+
+	/* generate list of currently installed policies and what they obsolete */
+	mi = rpmdbInitIterator(db, RPMTAG_NAME, RPMPOL_PKG_NAME, 0);
+	rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, ps->type);
+	while ((h = rpmdbNextIterator(mi)) != NULL) {
+		pol = rpmpolNew(h);
+		if (!pol) {
+			rpmlog(RPMLOG_ERR, _("Corrupt policy-module found in database"));
+			goto exit;
+		}
+		rpmpolInitIterator(pol);
+		rpmpolNext(pol); /* there is only one rpmpol in a RPMPOL_PKG_NAME */
+
+		if (rpmpolHasType(pol, ps->type)) {
+			argvAdd(&curList, rpmpolGetName(pol));
+			argvAppend(&obsList, rpmpolGetObsoletes(pol));
+		}
+
+		rpmpolFree(pol);
+	}
+	mi = rpmdbFreeIterator(mi);
+
+	argvSort(curList, NULL);
+	argvSort(obsList, NULL);
+
+	/* determine the actions for all the policies to be installed */
+	for (i = 0; i < ps->count; i++) {
+		pol = ps->pols[i];
+		if (!pol) {
+			goto exit;
+		}
+
+		rpmpolInitIterator(pol);
+		while (rpmpolNext(pol) >= 0) {
+			int action;
+			const char * name;
+
+			if (!rpmpolHasType(pol, ps->type)) {
+				rpmpolSetAction(pol, RPMPOL_ACTION_IGNORE);
+				continue;
+			}
+
+			name = rpmpolGetName(pol);
+			cur = argvSearch(curList, name, NULL);
+			obs = argvSearch(obsList, name, NULL);
+
+			if (obs) {
+				action = RPMPOL_ACTION_IGNORE;
+			} else if (cur) {
+				action = RPMPOL_ACTION_UPGRADE;
+				changes = 1;
+			} else {
+				action = RPMPOL_ACTION_INSTALL;
+				changes = 1;
+			}
+
+			rpmpolSetAction(pol, action);
+		}
+	}
+
+	/* remember currently installed policies that have been obsoleted for removal */
+	for (obs = obsList; obs && *obs; obs++) {
+		for (cur = curList; cur && *cur; cur++) {
+			if (!strcmp(*cur, *obs)) {
+				argvAdd(&ps->obsoleted, *cur);
+				changes = 1;
+			}
+		}
+	}
+
+	if (changes == 0) {
+		rc = RPMRC_NOTFOUND;
+		goto exit;
+	}
+
+	rc = RPMRC_OK;
+
+exit:
+	rpmdbFreeIterator(mi);
+	obsList = argvFree(obsList);
+	curList = argvFree(curList);
+
+	return rc;
+}
+
+rpmRC rpmpolsSaveState(rpmpols ps, rpmdb db, rpm_tid_t tid)
+{
+	Header h;
+	rpmpol pol;
+	unsigned int i;
+	ARGV_const_t obs;
+
+	if (!ps || !db) {
+		return RPMRC_FAIL;
+	}
+
+	/* remove obsoleted policies from the database */
+	for (obs = ps->obsoleted; obs && *obs; obs++) {
+		rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, RPMPOL_PKG_NAME, 0);
+		rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, ps->type);
+		rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, *obs);
+		while ((h = rpmdbNextIterator(mi)) != NULL) {
+			rpmdbRemove(db, tid, headerGetInstance(h), NULL, NULL);
+		}
+		rpmdbFreeIterator(mi);
+	}
+
+	for (i = 0; i < ps->count; i++) {
+		pol = ps->pols[i];
+
+		rpmpolInitIterator(pol);
+		while (rpmpolNext(pol) >= 0) {
+			rpmpolAction action = rpmpolGetAction(pol);
+
+			if (action == RPMPOL_ACTION_IGNORE) {
+				continue;
+			}
+
+			if (action == RPMPOL_ACTION_REMOVE || action == RPMPOL_ACTION_UPGRADE) {
+				rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, RPMPOL_PKG_NAME, 0);
+				rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, ps->type);
+				rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmpolGetName(pol));
+				while ((h = rpmdbNextIterator(mi)) != NULL) {
+					rpmdbRemove(db, tid, headerGetInstance(h), NULL, NULL);
+				}
+				rpmdbFreeIterator(mi);
+			}
+
+			if (action == RPMPOL_ACTION_INSTALL || action == RPMPOL_ACTION_UPGRADE) {
+				h = rpmpolMakeHeader(pol, ps->type, tid);
+				rpmdbAdd(db, tid, h, NULL, NULL);
+				headerFree(h);
+			}
+		}
+	}
+
+	return RPMRC_OK;
+}
+
+rpmRC rpmpolsInstall(rpmpols ps, rpmts ts)
+{
+	rpmpoltrans ptrans = NULL;
+	rpmRC rc = RPMRC_FAIL;
+	int chrootDone = 0;
+	const char * root = rpmtsRootDir(ts);
+	const char * cwd = rpmtsCurrDir(ts);
+
+	if (!ps || (root && !cwd)) {
+		return rc;
+	}
+
+	int dochroot = (root != NULL && !rstreq(root, "/") && *root == '/');
+	if (dochroot) {
+		if (chdir("/") == -1) {
+			rpmlog(RPMLOG_ERR, _("Failed to chdir: %s\n"), strerror(errno));
+			rc = RPMRC_FAIL;
+			goto err;
+		}
+
+		if (chroot(root) == -1) {
+			rpmlog(RPMLOG_ERR, _("Failed to chroot: %s\n"), strerror(errno));
+			rc = RPMRC_FAIL;
+			goto err;
+		}
+
+		chrootDone = 1;
+	}
+
+	ptrans = rpmpoltransBegin(chrootDone, rpmpolsGetType(ps));
+	if (!ptrans) {
+		rc = RPMRC_FAIL;
+		goto err;
+	}
+
+	rc = rpmpoltransAddSet(ptrans, ps);
+	if (rc != RPMRC_OK) {
+		goto err;
+	}
+
+	rc = rpmpoltransCommit(ptrans);
+	if (rc != RPMRC_OK) {
+		goto err;
+	}
+
+	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
+		matchpathcon_fini();
+		if (matchpathcon_init(selinux_file_context_path()) == -1) {
+			rpmlog(RPMLOG_WARNING, _("Failed to read new file contexts. New files may be mislabeled.\n"));
+		}
+	}
+
+err:
+	ptrans = rpmpoltransFree(ptrans);
+
+	if (chrootDone) {
+		chroot(".");
+		chdir(cwd);
+	}
+
+	return rc;
+}
diff --git a/lib/rpmpol.h b/lib/rpmpol.h
index 985f761..6c5d5c5 100644
--- a/lib/rpmpol.h
+++ b/lib/rpmpol.h
@@ -25,6 +25,7 @@ typedef enum rpmpolFlags_e {
 } rpmpolFlags;
 
 #define RPMPOL_TYPE_DEFAULT "default"
+#define RPMPOL_PKG_NAME "policy-module"
 
 /** \ingroup rpmpol
  * Unreference a policy set instance.
@@ -176,6 +177,16 @@ RPM_GNUC_INTERNAL
 int rpmpolIsValidIndex(const rpmpol pol);
 
 /** \ingroup rpmpol
+ * Create a header than can be used to create a faux policy package
+ * @param pol	rpm policy element
+ * @param type	policy type
+ * @param tid	rpm transaction id
+ * @return		new header, or NULL on failure. caller must free return header if not null
+ */
+RPM_GNUC_INTERNAL
+Header rpmpolMakeHeader(const rpmpol pol, const char * type, rpm_tid_t tid);
+
+/** \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
@@ -194,6 +205,15 @@ RPM_GNUC_INTERNAL
 rpmRC rpmpoltransAdd(rpmpoltrans pt, rpmpol pol);
 
 /** \ingroup rpmpol
+ * Add an rpm policy set to an rpm policy transaction
+ * @param pt	rpm policy transaction
+ * @param ps	rpm policy set
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpoltransAddSet(rpmpoltrans pt, rpmpols ps);
+
+/** \ingroup rpmpol
  * Commit the rpm policy transaction
  * @param pt	rpm policy transaction
  * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
@@ -238,6 +258,73 @@ rpmRC rpmpoltransInstallBase(rpmpoltrans pt, const char * path, const char * nam
 RPM_GNUC_INTERNAL
 rpmRC rpmpoltransRemove(rpmpoltrans pt, const char * name);
 
+/** \ingroup rpmpol
+ * Create an empty policy set
+ * @return		empty policy set
+ */
+RPM_GNUC_INTERNAL
+rpmpols rpmpolsNew(void);
+
+/** \ingroup rpmpol
+ * Free a policy set
+ * @param ps	rpm policy set
+ * @return		NULL
+ */
+RPM_GNUC_INTERNAL
+rpmpols rpmpolsFree(rpmpols ps);
+
+/** \ingroup rpmpol
+ * Add a policy element to a policy set
+ * @param ps	rpm policy set
+ * @param pol
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpolsAdd(rpmpols ps, rpmpol pol);
+
+/** \ingroup rpmpol
+ * Set the type the policy set should use for dependency checks
+ * @param ps	rpm policy set
+ * @param type	policy type
+ */
+RPM_GNUC_INTERNAL
+void rpmpolsSetType(rpmpols ps, const char * type);
+
+/** \ingroup rpmpol
+ * Get the type of the policy set
+ * @param ps	rpm policy set
+ * @return		type of policy set
+ */
+RPM_GNUC_INTERNAL
+const char * rpmpolsGetType(rpmpols ps);
+
+/** \ingroup rpmpol
+ * Set the actions on the policies
+ * @param ps	rpm policy set
+ * @param db	rpm database
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpolsPrepare(rpmpols ps, rpmdb db);
+
+/** \ingroup rpmpol
+ * Write the policy set to the rpm database
+ * @param ps	rpm policy set
+ * @param db	rpm database
+ * @param tid	rpm transaction id
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpolsSaveState(rpmpols ps, rpmdb db, rpm_tid_t tid);
+
+/** \ingroup rpmpol
+ * Install policy from a policy set
+ * @param ps	rpm policy set
+ * @param ts	rpm transaction set
+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+RPM_GNUC_INTERNAL
+rpmRC rpmpolsInstall(rpmpols ps, rpmts ts);
 
 #ifdef __cplusplus
 }
diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
index 86b2b01..8ba0df8 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 rpmpols_s * rpmpols;
 typedef struct rpmpoltrans_s * rpmpoltrans;
 typedef struct rpmdbMatchIterator_s * rpmdbMatchIterator;
 typedef const void * fnpyKey;
-- 
1.6.0.6



More information about the Rpm-maint mailing list