[Rpm-maint] [PATCH 06/12] Add rpmpoltrans struct and helper functions
Steve Lawrence
slawrence at tresys.com
Thu Oct 22 18:25:43 UTC 2009
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 | 269 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/rpmpol.h | 63 +++++++++++++
lib/rpmtypes.h | 1 +
macros.in | 1 +
5 files changed, 371 insertions(+), 1 deletions(-)
diff --git a/configure.ac b/configure.ac
index 4833169..54ec5d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -176,6 +176,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)
@@ -732,11 +733,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 5198bb1..18b8419 100644
--- a/lib/rpmpol.c
+++ b/lib/rpmpol.c
@@ -3,11 +3,14 @@
*/
#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"
@@ -28,6 +31,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) {
@@ -388,3 +400,260 @@ 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;
+ }
+
+ 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:
+ 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;
+}
diff --git a/lib/rpmpol.h b/lib/rpmpol.h
index ca5e5e4..985f761 100644
--- a/lib/rpmpol.h
+++ b/lib/rpmpol.h
@@ -175,6 +175,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 2736d97..86b2b01 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 const void * fnpyKey;
typedef void * rpmCallbackData;
diff --git a/macros.in b/macros.in
index ae46fcb..c7c6564 100644
--- a/macros.in
+++ b/macros.in
@@ -59,6 +59,7 @@
%__rm @__RM@
%__rsh @__RSH@
%__sed @__SED@
+%__semodule @__SEMODULE@
%__ssh @__SSH@
%__tar @__TAR@
%__unzip @__UNZIP@
--
1.6.0.6
More information about the Rpm-maint
mailing list