summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorSteve Lawrence <slawrence@tresys.com>2010-08-30 16:32:31 -0400
committerPanu Matilainen <pmatilai@redhat.com>2010-09-01 09:31:03 +0300
commit46cdd1b5073a6b7cdf0e23723f54958ec2f8c97e (patch)
tree539e6c5180aafcc38624536b96a732f25b03a08f /plugins
parent34b5d67c2d3d68a48f0975dc6111a8c184d1c2dd (diff)
downloadrpm-46cdd1b5073a6b7cdf0e23723f54958ec2f8c97e.tar.gz
rpm-46cdd1b5073a6b7cdf0e23723f54958ec2f8c97e.tar.bz2
rpm-46cdd1b5073a6b7cdf0e23723f54958ec2f8c97e.zip
Add SELinux policy plugin
This adds a new plugin specifically for a collection to load SELinux policy. This implements the post_add and pre_remove plugin hooks. The only time anything happens during the pre_remove hook is if post_add was not called (i.e. if the transaction only removes policies). This plugin extracts all the policy information from packages in the sepolicy collection during the open te hook. It then determines which policies should be installed/removed based on if the package is being installed/removed and the type of the policy and the system policy. It then executes semodule (or uses libsemanage if semodule cannot be executed or installing in a chroot) to remove and install the necessary policies. It then reloads the selinux state, reloads the file contexts, and if necessary, relabels the file system.
Diffstat (limited to 'plugins')
-rw-r--r--plugins/Makefile.am6
-rw-r--r--plugins/sepolicy.c679
2 files changed, 684 insertions, 1 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 07560f07a..25774c42c 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -13,7 +13,11 @@ AM_LDFLAGS = -avoid-version -module -shared
pluginsdir = $(rpmconfigdir)/plugins
-plugins_LTLIBRARIES = exec.la
+plugins_LTLIBRARIES = exec.la sepolicy.la
exec_la_SOURCES = plugin.h exec.c
exec_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
+
+sepolicy_la_SOURCES = plugin.h sepolicy.c
+sepolicy_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la @WITH_SELINUX_LIB@
+
diff --git a/plugins/sepolicy.c b/plugins/sepolicy.c
new file mode 100644
index 000000000..cfd45d062
--- /dev/null
+++ b/plugins/sepolicy.c
@@ -0,0 +1,679 @@
+#include "plugin.h"
+
+#if WITH_SELINUX
+
+#include <errno.h>
+#include <selinux/selinux.h>
+#include <semanage/semanage.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <rpm/rpmpol.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmmacro.h>
+
+#include "rpmio/base64.h"
+#include "lib/rpmte_internal.h"
+
+rpmPluginHook PLUGIN_HOOKS = \
+ PLUGINHOOK_INIT | \
+ PLUGINHOOK_CLEANUP | \
+ PLUGINHOOK_OPENTE | \
+ PLUGINHOOK_COLL_POST_ADD | \
+ PLUGINHOOK_COLL_PRE_REMOVE;
+
+typedef enum sepolAction {
+ SEPOL_ACTION_IGNORE,
+ SEPOL_ACTION_INSTALL,
+ SEPOL_ACTION_REMOVE
+} sepolAction;
+
+typedef struct sepol {
+ char *data; /*!< policy data */
+ char *name; /*!< policy names */
+ ARGV_t types; /*!< policy types */
+ uint32_t flags; /*!< policy flags */
+ sepolAction action; /*!< install/remove/ignore */
+ struct sepol *next; /*!< next in linked list */
+} sepol;
+
+typedef struct sepoltrans {
+ 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 */
+ char *semodulepath; /*!< path to semodule binary */
+ 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 */
+} sepoltrans;
+
+
+static char * name;
+static rpmts ts;
+
+static sepol * policiesHead;
+static sepol * policiesTail;
+
+
+static sepol *sepolNew(rpmte te);
+static sepol *sepolFree(sepol * pol);
+static int sepolHasType(const sepol * pol, const char *type);
+
+static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype);
+static rpmRC sepolWritePolicy(const sepol * pol, char **path);
+static rpmRC sepolLoadPolicies(const sepol * pols);
+
+static sepoltrans *sepoltransNew(void);
+static sepoltrans *sepoltransFree(sepoltrans * pt);
+
+static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol);
+static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol);
+static rpmRC sepoltransCommit(sepoltrans * pt);
+
+
+static sepol *sepolNew(rpmte te)
+{
+ sepol *head = NULL;
+ sepol *ret = NULL;
+ sepolAction action;
+ Header h;
+ struct rpmtd_s policies, names, types, typesidx, flags;
+ int i, j;
+ int count;
+
+ rpmtdReset(&policies);
+ rpmtdReset(&names);
+ rpmtdReset(&types);
+ rpmtdReset(&typesidx);
+ rpmtdReset(&flags);
+
+ h = rpmteHeader(te);
+ if (!h) {
+ goto exit;
+ }
+
+ if (!headerIsEntry(h, RPMTAG_POLICIES)) {
+ goto exit;
+ }
+
+ if (!headerGet(h, RPMTAG_POLICIES, &policies, HEADERGET_MINMEM)) {
+ goto exit;
+ }
+
+ count = rpmtdCount(&policies);
+ if (count <= 0) {
+ goto exit;
+ }
+
+ if (!headerGet(h, RPMTAG_POLICYNAMES, &names, HEADERGET_MINMEM)
+ || rpmtdCount(&names) != count) {
+ goto exit;
+ }
+
+ if (!headerGet(h, RPMTAG_POLICYFLAGS, &flags, HEADERGET_MINMEM)
+ || rpmtdCount(&flags) != count) {
+ goto exit;
+ }
+
+ if (!headerGet(h, RPMTAG_POLICYTYPES, &types, HEADERGET_MINMEM)) {
+ goto exit;
+ }
+
+ if (!headerGet(h, RPMTAG_POLICYTYPESINDEXES, &typesidx, HEADERGET_MINMEM)
+ || rpmtdCount(&types) != rpmtdCount(&typesidx)) {
+ goto exit;
+ }
+
+ action = (rpmteType(te) == TR_ADDED) ? SEPOL_ACTION_INSTALL : SEPOL_ACTION_REMOVE;
+
+ for (i = 0; i < count; i++) {
+ sepol *pol = xcalloc(1, sizeof(*pol));
+ pol->next = head;
+ head = pol;
+
+ pol->data = xstrdup(rpmtdNextString(&policies));
+ pol->name = xstrdup(rpmtdNextString(&names));
+ pol->flags = *rpmtdNextUint32(&flags);
+ pol->action = action;
+
+ for (j = 0; j < rpmtdCount(&types); j++) {
+ uint32_t index = ((uint32_t *) typesidx.data)[j];
+ if (index < 0 || index >= count) {
+ goto exit;
+ }
+ if (index != i) {
+ continue;
+ }
+ argvAdd(&pol->types, rpmtdNextString(&types));
+ }
+ argvSort(pol->types, NULL);
+ }
+
+ ret = head;
+
+ exit:
+ headerFree(h);
+
+ rpmtdFreeData(&policies);
+ rpmtdFreeData(&names);
+ rpmtdFreeData(&types);
+ rpmtdFreeData(&typesidx);
+ rpmtdFreeData(&flags);
+
+ if (!ret) {
+ sepolFree(head);
+ }
+
+ return ret;
+}
+
+static sepol *sepolFree(sepol * pol)
+{
+ while (pol) {
+ sepol *next = pol->next;
+
+ pol->data = _free(pol->data);
+ pol->name = _free(pol->name);
+ pol->types = argvFree(pol->types);
+ pol->next = NULL;
+ _free(pol);
+
+ pol = next;
+ }
+
+ return NULL;
+}
+
+int sepolHasType(const sepol * pol, const char *type)
+{
+ if (!pol || !type) {
+ return 0;
+ }
+
+ return (argvSearch(pol->types, type, NULL) != NULL) ||
+ (argvSearch(pol->types, RPMPOL_TYPE_DEFAULT, NULL) != NULL);
+}
+
+static rpmRC sepolPreparePolicies(sepol * pols, const char *policytype)
+{
+ sepol *pol;
+ rpmRC rc = RPMRC_OK;
+
+ for (pol = pols; pol; pol = pol->next) {
+ if (!sepolHasType(pol, policytype)) {
+ pol->action = SEPOL_ACTION_IGNORE;
+ }
+ }
+
+ return rc;
+}
+
+static rpmRC sepolWritePolicy(const sepol * pol, char **path)
+{
+ char *tmppath = NULL;
+ FD_t fd = NULL;
+ char *policy = NULL;
+ size_t policylen;
+ int rc = RPMRC_FAIL;
+
+ if (b64decode(pol->data, (void **) &policy, &policylen) != 0) {
+ rpmlog(RPMLOG_ERR, _("Failed to decode policy for %s\n"),
+ pol->name);
+ goto exit;
+ }
+
+ fd = rpmMkTempFile(NULL, &tmppath);
+ if (fd == NULL || Ferror(fd)) {
+ rpmlog(RPMLOG_ERR, _("Failed to create temporary file for %s: %s\n"),
+ pol->name, strerror(errno));
+ goto exit;
+ }
+
+ if (!Fwrite(policy, sizeof(*policy), policylen, fd)) {
+ rpmlog(RPMLOG_ERR, _("Failed to write %s policy to file %s\n"),
+ pol->name, tmppath);
+ goto exit;
+ }
+
+ *path = tmppath;
+ rc = RPMRC_OK;
+
+ exit:
+ if (fd)
+ Fclose(fd);
+ _free(policy);
+ if (rc != RPMRC_OK)
+ _free(tmppath);
+
+ return rc;
+}
+
+static rpmRC sepolLoadPolicies(const sepol * pols)
+{
+ const sepol *pol;
+ sepoltrans *pt;
+ rpmRC rc = RPMRC_FAIL;
+
+ pt = sepoltransNew();
+ if (!pt) {
+ rc = RPMRC_FAIL;
+ goto err;
+ }
+
+ for (pol = pols; pol; pol = pol->next) {
+ switch (pol->action) {
+ case SEPOL_ACTION_REMOVE:
+ rc = sepoltransRemove(pt, pol);
+ break;
+ case SEPOL_ACTION_INSTALL:
+ rc = sepoltransInstall(pt, pol);
+ break;
+ case SEPOL_ACTION_IGNORE:
+ default:
+ rc = RPMRC_OK;
+ break;
+ }
+
+ if (rc != RPMRC_OK) {
+ goto err;
+ }
+ }
+
+ rc = sepoltransCommit(pt);
+ if (rc != RPMRC_OK) {
+ goto err;
+ }
+
+ err:
+ pt = sepoltransFree(pt);
+
+ return rc;
+}
+
+static sepoltrans *sepoltransNew(void)
+{
+ sepoltrans *pt = xcalloc(1, sizeof(*pt));
+ pt->semodulepath = rpmExpand("%{__semodule}", NULL);
+ pt->execsemodule = (!rpmChrootDone() && access(pt->semodulepath, X_OK) == 0);
+ pt->changes = 0;
+
+ if (pt->execsemodule) {
+ argvAdd(&pt->semodargs, "semodule");
+ } else {
+ 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);
+ 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, !rpmChrootDone());
+ }
+
+ 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;
+}
+
+static sepoltrans *sepoltransFree(sepoltrans * 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;
+}
+
+static rpmRC sepoltransInstall(sepoltrans * pt, const sepol * pol)
+{
+ rpmRC rc = RPMRC_OK;
+ char *path = NULL;
+
+ rc = sepolWritePolicy(pol, &path);
+ if (rc != RPMRC_OK) {
+ return rc;
+ }
+ argvAdd(&pt->filelist, path);
+
+ if (pt->execsemodule) {
+ const char *flag = (pol->flags & RPMPOL_FLAG_BASE) ? "-b" : "-i";
+ if (argvAdd(&pt->semodargs, flag) < 0 || argvAdd(&pt->semodargs, path) < 0) {
+ rc = RPMRC_FAIL;
+ }
+ } else {
+ if (pol->flags & RPMPOL_FLAG_BASE) {
+ if (semanage_module_install_base_file(pt->sh, 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"),
+ pol->name, path);
+ } else {
+ pt->changes++;
+ }
+
+ _free(path);
+
+ return rc;
+}
+
+static rpmRC sepoltransRemove(sepoltrans * pt, const sepol * pol)
+{
+ rpmRC rc = RPMRC_OK;
+
+ if (pol->flags & RPMPOL_FLAG_BASE) {
+ return RPMRC_FAIL;
+ }
+
+ if (pt->execsemodule) {
+ if (argvAdd(&pt->semodargs, "-r") < 0 || argvAdd(&pt->semodargs, pol->name) < 0) {
+ rc = RPMRC_FAIL;
+ }
+ } else {
+ if (semanage_module_remove(pt->sh, (char *) pol->name) < 0) {
+ rc = RPMRC_FAIL;
+ }
+ }
+
+ if (rc != RPMRC_OK) {
+ rpmlog(RPMLOG_ERR, _("Failed to remove policy module: %s\n"),
+ pol->name);
+ } else {
+ pt->changes++;
+ }
+
+ return rc;
+}
+
+static rpmRC sepoltransCommit(sepoltrans * pt)
+{
+ rpmRC rc = RPMRC_OK;
+
+ if (pt->changes == 0) {
+ return rc;
+ }
+
+ if (pt->execsemodule) {
+ int status;
+ pid_t pid = fork();
+ int fd;
+
+ switch (pid) {
+ case -1:
+ rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
+ strerror(errno));
+ rc = RPMRC_FAIL;
+ break;
+ case 0:
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ 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;
+}
+
+static rpmRC sepolRelabelFiles(void)
+{
+ rpmRC rc = RPMRC_OK;
+ pid_t pid;
+ int fd;
+ int status;
+ char *restoreconPath = rpmExpand("%{__restorecon}", NULL);
+
+ if (!restoreconPath) {
+ rpmlog(RPMLOG_ERR, _("Failed to expand restorecon path"));
+ return RPMRC_FAIL;
+ }
+
+ /* execute restorecon -R / */
+ pid = fork();
+ switch (pid) {
+ case -1:
+ rpmlog(RPMLOG_ERR, _("Failed to fork process: %s\n"),
+ strerror(errno));
+ rc = RPMRC_FAIL;
+ break;
+ case 0:
+ fd = open("/dev/null", O_RDWR);
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ execl(restoreconPath, "restorecon", "-R", "/", NULL);
+ rpmlog(RPMLOG_ERR, _("Failed to execute %s: %s\n"), restoreconPath,
+ strerror(errno));
+ exit(1);
+ default:
+ waitpid(pid, &status, 0);
+ if (!WIFEXITED(status)) {
+ rpmlog(RPMLOG_ERR, _("%s terminated abnormally\n"),
+ restoreconPath);
+ rc = RPMRC_FAIL;
+ } else if (WEXITSTATUS(status)) {
+ rpmlog(RPMLOG_ERR, _("%s failed with exit code %i\n"),
+ restoreconPath, WEXITSTATUS(status));
+ rc = RPMRC_FAIL;
+ }
+ }
+
+ _free(restoreconPath);
+
+ return rc;
+}
+
+static rpmRC sepolGo()
+{
+ semanage_handle_t *sh;
+ int existingPolicy;
+ char *policytype = NULL;
+ int rc = RPMRC_FAIL;
+
+ static int performed = 0;
+ if (performed) {
+ return RPMRC_OK;
+ }
+ performed = 1;
+
+ if (rpmChrootIn()) {
+ goto exit;
+ }
+
+ if (selinux_getpolicytype(&policytype) < 0) {
+ goto exit;
+ }
+
+ sepolPreparePolicies(policiesHead, policytype);
+
+ /* determine if this is the first time installing policy */
+ sh = semanage_handle_create();
+ existingPolicy = (semanage_is_managed(sh) == 1);
+ semanage_handle_destroy(sh);
+
+ /* now load the policies */
+ rc = sepolLoadPolicies(policiesHead);
+
+ /* re-init selinux and re-read the files contexts, since things may have changed */
+ selinux_reset_config();
+ if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
+ if (rpmtsSELabelInit(ts, selinux_file_context_path()) == RPMRC_OK) {
+ /* if this was the first time installing policy, every package before
+ * policy was installed will be mislabeled (e.g. semodule). So, relabel
+ * the entire filesystem if this is the case */
+ if (!existingPolicy) {
+ if (sepolRelabelFiles() != RPMRC_OK) {
+ rpmlog(RPMLOG_WARNING, _("Failed to relabel filesystem. Files may be mislabeled\n"));
+ }
+ }
+ } else {
+ rpmlog(RPMLOG_WARNING, _("Failed to reload file contexts. Files may be mislabeled\n"));
+ }
+ }
+
+ exit:
+ if (rpmChrootOut()) {
+ rc = RPMRC_FAIL;
+ }
+
+ _free(policytype);
+
+ return rc;
+}
+
+static rpmRC sepolAddTE(rpmte te)
+{
+ sepol *pol;
+ sepol *polTail;
+
+ if (!rpmteHasCollection(te, name)) {
+ return RPMRC_OK;
+ }
+
+ pol = sepolNew(te);
+ if (!pol) {
+ /* something's wrong with the policy information, either missing or
+ * corrupt. abort */
+ rpmlog(RPMLOG_ERR, _("Failed to extract policy from %s\n"),
+ rpmteNEVRA(te));
+ return RPMRC_FAIL;
+ }
+
+ /* find the tail of pol */
+ polTail = pol;
+ while (polTail->next) {
+ polTail = polTail->next;
+ }
+
+ /* add the new policy to the list */
+ if (!policiesHead) {
+ policiesHead = pol;
+ policiesTail = polTail;
+ } else {
+ if (rpmteType(te) == TR_ADDED) {
+ /* add to the end of the list */
+ policiesTail->next = pol;
+ policiesTail = polTail;
+ } else {
+ /* add to the beginning of the list */
+ polTail->next = policiesHead;
+ policiesHead = pol;
+ }
+ }
+
+ return RPMRC_OK;
+}
+
+#endif /* WITH_SELINUX */
+
+
+rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *_name, const char *_opts)
+{
+#if WITH_SELINUX
+ ts = _ts;
+ name = strdup(_name);
+ policiesHead = policiesTail = NULL;
+#endif
+ return RPMRC_OK;
+}
+
+rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
+{
+#if WITH_SELINUX
+ _free(name);
+ ts = NULL;
+ policiesHead = policiesTail = sepolFree(policiesHead);
+#endif
+ return RPMRC_OK;
+}
+
+rpmRC PLUGINHOOK_OPENTE_FUNC(rpmte te)
+{
+ rpmRC rc = RPMRC_OK;
+#if WITH_SELINUX
+ rc = sepolAddTE(te);
+#endif
+ return rc;
+}
+
+rpmRC PLUGINHOOK_COLL_POST_ADD_FUNC(void)
+{
+ rpmRC rc = RPMRC_OK;
+#if WITH_SELINUX
+ rc = sepolGo();
+#endif
+ return rc;
+}
+
+rpmRC PLUGINHOOK_COLL_PRE_REMOVE_FUNC(void)
+{
+ rpmRC rc = RPMRC_OK;
+#if WITH_SELINUX
+ rc = sepolGo();
+#endif
+ return rc;
+}