[Rpm-maint] [PATCH 12/18] Determine how policy should be installed based on SELinux state

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


The state of SELinux on a system changes how we install policy during
the rpm transaction. For example, if the /etc/selinux directory structure
hasn't been created, we can't install policy until a package creates the
directory. This patch adds a function (rpmtsInitSELinux) which performs
initialization and determines the state of SELinux on the system and what
actions should be taken to install policy.

This first checks to see if an existing policy is installed. If this is
the case, we install policy at the beginning of the transaction as normal,
installing all policy for the current policy type.

If a policy isn't already installed, we check to see if a base module
exists in the current transaction. If so, we postpone policy until the
end of the transaction. By doing this, we assume that either the package
providing the base module, or another package in the Requires tag, will
create the /etc/selinux directory structure so that policy can be installed.

If policy isn't already installed and no base module exists in the current
transaction, we disable policy installation.

Note: This patch requires libselinux >= 2.0.88
---
 configure.ac         |    4 +
 lib/rpmts.c          |   13 +++
 lib/rpmts.h          |   14 ++++
 lib/rpmts_internal.h |    2 +
 lib/transaction.c    |  199 ++++++++++++++++++++++++++++++++++++++++++--------
 5 files changed, 200 insertions(+), 32 deletions(-)

diff --git a/configure.ac b/configure.ac
index 4e3959a..47c2b82 100644
--- a/configure.ac
+++ b/configure.ac
@@ -729,8 +729,12 @@ AS_IF([test "$with_selinux" = yes],[
     save_LIBS="$LIBS"
     AC_CHECK_LIB([selinux],[is_selinux_enabled],[],[
       AC_MSG_ERROR([--with-selinux given, but is_selinux_enabled not found in libselinux])])
+    AC_CHECK_LIB([selinux],[selinux_file_context_path],[],[
+      AC_MSG_ERROR([--with-selinux given, but selinux_file_context_path not found in libselinux])])
     AC_CHECK_LIB([selinux],[selinux_getpolicytype],[],[
       AC_MSG_ERROR([--with-selinux given, but selinux_getpolicytype not found in libselinux])])
+    AC_CHECK_LIB([selinux],[selinux_reset_config],[],[
+      AC_MSG_ERROR([--with-selinux given, but selinux_reset_config not found in libselinux])])
     LIBS="$save_LIBS"
   ],[
     AC_MSG_ERROR([--with-selinux given, but selinux/selinux.h not found])
diff --git a/lib/rpmts.c b/lib/rpmts.c
index 236d34b..de640d4 100644
--- a/lib/rpmts.c
+++ b/lib/rpmts.c
@@ -698,6 +698,18 @@ int rpmtsSELinuxEnabled(rpmts ts)
     return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
 }
 
+int rpmtsPostponePolicy(rpmts ts)
+{
+	return (ts != NULL ? ts->postponePolicy : 0);
+}
+
+void rpmtsSetPostponePolicy(rpmts ts, int postpone)
+{
+	if (ts != NULL) {
+		ts->postponePolicy = postpone;
+	}
+}
+
 int rpmtsChrootDone(rpmts ts)
 {
     return (ts != NULL ? ts->chrootDone : 0);
@@ -920,6 +932,7 @@ rpmts rpmtsCreate(void)
     ts->chrootDone = 0;
 
     ts->selinuxEnabled = is_selinux_enabled();
+    ts->postponePolicy = 0;
 
     ts->numAddedPackages = 0;
     ts->addedPackages = NULL;
diff --git a/lib/rpmts.h b/lib/rpmts.h
index f2b731f..d263365 100644
--- a/lib/rpmts.h
+++ b/lib/rpmts.h
@@ -422,6 +422,20 @@ void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd);
 int rpmtsSELinuxEnabled(rpmts ts);
 
 /** \ingroup rpmts
+ * Should policy be postponed?
+ * @param ts		transaction set
+ * @return		non-zero = postpone policy, 0 = do not postpone policy
+ */
+int rpmtsPostponePolicy(rpmts ts);
+
+/** \ingroup rpmts
+ * Set if policy should be postponed or not
+ * @param ts		transaction set
+ * @param postpone		set if policy should be postponed
+ */
+void rpmtsSetPostponePolicy(rpmts ts, int postpone);
+
+/** \ingroup rpmts
  * Get chrootDone flag, i.e. has chroot(2) been performed?
  * @param ts		transaction set
  * @return		chrootDone flag
diff --git a/lib/rpmts_internal.h b/lib/rpmts_internal.h
index 87bf77d..6eef287 100644
--- a/lib/rpmts_internal.h
+++ b/lib/rpmts_internal.h
@@ -47,6 +47,8 @@ struct rpmts_s {
     int maxDepth;		/*!< Maximum depth of dependency tree(s). */
 
     int selinuxEnabled;		/*!< Is SE linux enabled? */
+    int postponePolicy;		/*!< Should policy be postponed? */
+
     int chrootDone;		/*!< Has chroot(2) been been done? */
     char * rootDir;		/*!< Path to top of install tree. */
     char * currDir;		/*!< Current working directory. */
diff --git a/lib/transaction.c b/lib/transaction.c
index 5f0549d..9e6ea28 100644
--- a/lib/transaction.c
+++ b/lib/transaction.c
@@ -4,6 +4,8 @@
 
 #include "system.h"
 
+#include <semanage/semanage.h>
+
 #include <rpm/rpmlib.h>		/* rpmMachineScore, rpmReadPackageFile */
 #include <rpm/rpmmacro.h>	/* XXX for rpmExpand */
 #include <rpm/rpmlog.h>
@@ -1278,6 +1280,128 @@ static void addFingerprints(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPr
     rpmFpHashFree(symlinks);
 }
 
+static rpmRC rpmtsInitSELinux(rpmts ts)
+{
+	rpmtsi pi = NULL;
+	rpmte p = NULL;
+	Header h = NULL;
+	const char *rootDir;
+	int dochroot;
+	rpmRC rc = RPMRC_FAIL;
+	int existingbase;
+	int installingbase = 0;
+	struct rpmtd_s policyFlags;
+	semanage_handle_t *sh;
+
+	/* if SELinux isn't enabled don't bother... */
+	if (!rpmtsSELinuxEnabled(ts)) {
+		rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS | RPMTRANS_FLAG_NOPOLICY));
+		return RPMRC_OK;
+	}
+
+	if ((rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS) && (rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOLICY)) {
+		return RPMRC_OK;
+	}
+
+	/* enter chroot if necessary */
+	rootDir = rpmtsRootDir(ts);
+	dochroot = (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/');
+	if (dochroot) {
+		if (chdir("/") == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to change directory: %s\n",
+			       strerror(errno));
+			goto err;
+		}
+		if (chroot(rootDir) == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to chroot to %s: %s\n",
+			       rootDir, strerror(errno));
+			goto err;
+		}
+	}
+
+	/* determine if there is a base module already installed */
+	sh = semanage_handle_create();
+	existingbase = (semanage_is_managed(sh) == 1);
+	semanage_handle_destroy(sh);
+
+	/* initialize matchpathcon */
+	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
+		if (matchpathcon_init(selinux_file_context_path()) == -1) {
+			rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
+		}
+	}
+
+	/* exit chroot */
+	if (dochroot) {
+		if (chroot(".") == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to exit chroot: %s\n", strerror(errno));
+			goto err;
+		}
+		chdir(rpmtsCurrDir(ts));
+	}
+
+	if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOLICY)) {
+		/* look for packages that contain a base module */
+		pi = rpmtsiInit(ts);
+		while (!installingbase && (p = rpmtsiNext(pi, TR_ADDED))) {
+			uint32_t *flag;
+
+			if (!rpmteOpen(p, ts, 0)) {
+				rpmlog(RPMLOG_ERR, _("Failed to open %s\n"), rpmteNEVRA(p));
+				goto err;
+			}
+
+			h = rpmteHeader(p);
+			if (!h) {
+				rpmlog(RPMLOG_ERR, _("Failed to get package header from %s\n"), rpmteNEVRA(p));
+				goto err;
+			}
+
+			if (!headerIsEntry(h, RPMTAG_POLICYFLAGS)) {
+				continue;
+			}
+
+			headerGet(h, RPMTAG_POLICYFLAGS, &policyFlags, HEADERGET_MINMEM);
+			while ((flag = rpmtdNextUint32(&policyFlags))) {
+				if (*flag & RPMPOL_FLAG_BASE) {
+					installingbase = 1;
+					break;
+				}
+			}
+			rpmtdFreeData(&policyFlags);
+
+			h = headerFree(h);
+			rpmteClose(p, ts, 0);
+		}
+		pi = rpmtsiFree(pi);
+
+		/* determine if we should disable/postpone policy installation */
+		if (existingbase) {
+			/* there is already an existing policy installed, so install all policy for that type
+			 * and don't postpone. This ignores the fact that a base policy might be being installed */
+			rpmtsSetPostponePolicy(ts, 0);
+		} else if (installingbase) {
+			/* no existing policy is installed, but a base module is availble that might be installed, so
+			 * postpone policy and try to install it at the end of the transaction */
+			rpmtsSetPostponePolicy(ts, 1);
+		} else {
+			/* no existing policy is currently installed and no base module is being installed */
+			/* assume they don't want policy */
+			rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOPOLICY));
+		}
+	}
+
+	rc = RPMRC_OK;
+
+ err:
+	h = headerFree(h);
+	rpmteClose(p, ts, 0);
+	pi = rpmtsiFree(pi);
+
+	return rc;
+}
+
+
 static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
 {
     rpm_tid_t tid = (rpm_tid_t) time(NULL);
@@ -1291,11 +1415,6 @@ static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
     if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
 	(void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
 
-    /* if SELinux isn't enabled or init fails, don't bother... */
-    if (!rpmtsSELinuxEnabled(ts)) {
-        rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS | RPMTRANS_FLAG_NOPOLICY));
-    }
-
     /* XXX Make sure the database is open RDWR for package install/erase. */
     if (rpmtsOpenDB(ts, dbmode)) {
 	return -1;	/* XXX W2DO? */
@@ -1312,32 +1431,8 @@ static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
     (void) rpmtsSetChrootDone(ts, 0);
     (void) rpmtsSetTid(ts, tid);
 
-    if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS) ||
-	    !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOPOLICY)) {
-	const char * rootDir = rpmtsRootDir(ts);
-	int dochroot = (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/');
-	if (dochroot) {
-	    if (chdir("/") == -1) {
-	        rpmlog(RPMLOG_ERR, "Failed to change directory: %s\n", strerror(errno));
-	        return -1;
-	    }
-	    if (chroot(rootDir) == -1) {
-	        rpmlog(RPMLOG_ERR, "Failed to chroot to %s: %s\n", rootDir, strerror(errno));
-	        return -1;
-	    }
-	}
-	char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
-	if (matchpathcon_init(fn) == -1) {
-	    rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
-	}
-	_free(fn);
-	if (dochroot) {
-	    if (chroot(".") == -1) {
-	        rpmlog(RPMLOG_ERR, "Failed to exit chroot: %s\n", strerror(errno));
-	        return -1;
-	    }
-	    chdir(rpmtsCurrDir(ts));
-	}
+    if (rpmtsInitSELinux(ts) != RPMRC_OK) {
+	return -1;
     }
 
     /* Get available space on mounted file systems. */
@@ -1530,10 +1625,16 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
     if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_NOPOLICY))
      	  || (rpmpsNumProblems(ts->probs) &&
 		(okProbs == NULL || rpmpsTrim(ts->probs, okProbs))))) {
-		if (rpmtsLoadPolicy(ts) != RPMRC_OK) {
+
+	if (!rpmtsPostponePolicy(ts)) {
+		int xx = rpmtsLoadPolicy(ts);
+		if (xx == RPMRC_MISSINGDEPS) {
+			rpmtsSetPostponePolicy(ts, 1);
+		} else if (xx != RPMRC_OK) {
 			goto exit;
 		}
 	}
+    }
 
     /* Run pre-transaction scripts, but only if there are no known
      * problems up to this point and not disabled otherwise. */
@@ -1566,6 +1667,40 @@ int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
 	runTransScripts(ts, RPMTAG_POSTTRANS);
     }
 
+    /* load policy and relabel files if policy was postponed */
+    if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_NOPOLICY)) && rpmtsPostponePolicy(ts)) {
+	const char *rootDir;
+	int dochroot;
+
+	/* enter chroot if necessary */
+	rootDir = rpmtsRootDir(ts);
+	dochroot = (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/');
+	if (dochroot) {
+		if (chdir("/") == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to change directory: %s\n. No policy will be installed.", strerror(errno));
+			goto exit;
+		}
+		if (chroot(rootDir) == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to chroot to %s: %s\n. No policy will be installed.", rootDir, strerror(errno));
+			goto exit;
+		}
+	}
+
+	/* re-read selinux config since it may have changed during package installation */
+	selinux_reset_config();
+
+	/* exit chroot */
+	if (dochroot) {
+		if (chroot(".") == -1) {
+			rpmlog(RPMLOG_ERR, "Failed to exit chroot: %s\n. No policy will be installed.", strerror(errno));
+			goto exit;
+		}
+		chdir(rpmtsCurrDir(ts));
+	}
+
+	rpmtsLoadPolicy(ts);
+    }
+
     /* Finish up... */
     (void) rpmtsFinish(ts);
 
-- 
1.6.0.6



More information about the Rpm-maint mailing list