[Rpm-maint] [PATCH 17/19] Add hook for verifying policy modules
Steve Lawrence
slawrence at tresys.com
Tue Feb 2 20:25:20 UTC 2010
In the current state, there is no verification that a file specified with
the %module tag is actually a policy module. This patch adds an rpmbuild
hook that can be used to perform module verification. A %__check_policies
macro has been added which can point to a script/executable that performs
module validation at rpmbuild time. The patch also adds a simple script,
check-policies, that checks the magic number of modules to ensure that they
are valid modules. Similar to the check-files script, the check-policies
script takes a list of files on stdin and echos any files that are not
valid modules to stdout.
---
build/policies.c | 61 ++++++++++++++-
configure.ac | 22 +++++-
macros.in | 2 +
scripts/Makefile.am | 8 ++
scripts/check-policies.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 290 insertions(+), 3 deletions(-)
create mode 100644 scripts/check-policies.c
diff --git a/build/policies.c b/build/policies.c
index a1ee679..7da9ab7 100644
--- a/build/policies.c
+++ b/build/policies.c
@@ -9,6 +9,7 @@
#include <rpm/argv.h>
#include <rpm/rpmlog.h>
#include <rpm/rpmpol.h>
+#include <rpm/rpmfc.h>
#include "rpmio/rpmio_internal.h"
#include "rpmio/base64.h"
@@ -18,6 +19,8 @@
#define SKIPSPACE(s) { while ((s) && *(s) && risspace(*(s))) (s)++; }
#define SKIPNONSPACE(s) { while ((s) && *(s) && !risspace(*(s))) (s)++; }
+static StringBuf check_policyList = NULL;
+
typedef struct ModuleRec_s {
char *path;
char *name;
@@ -234,6 +237,12 @@ static rpmRC writeModuleToHeader(ModuleRec mod, Package pkg)
headerPutUint32(pkg->header, RPMTAG_POLICYOBSOLETESBY, &count, 1);
}
+ /* add module to list of modules to check */
+ if (check_policyList) {
+ appendStringBuf(check_policyList, mod->path);
+ appendStringBuf(check_policyList, "\n");
+ }
+
rc = RPMRC_OK;
exit:
@@ -457,6 +466,47 @@ exit:
}
+/**
+ * Check packaged policies.
+ * @param fileList packaged policy list
+ * @return RPMRC_OK if all policies are ok, RPMRC_FAIL otherwise
+ */
+static int checkPolicies(StringBuf policyList)
+{
+ static char *const av_ckpolicy[] = { "%{?__check_policies}", NULL };
+ StringBuf sb_stdout = NULL;
+ char *s;
+ int ret;
+ int rc = RPMRC_FAIL;
+
+ s = rpmExpand(av_ckpolicy[0], NULL);
+ if (!(s && *s)) {
+ goto exit;
+ }
+
+ rpmlog(RPMLOG_NOTICE, _("Checking policy module(s): %s\n"), s);
+
+ ret = rpmfcExec(av_ckpolicy, policyList, &sb_stdout, 0);
+ if (ret < 0) {
+ goto exit;
+ }
+
+ if (sb_stdout) {
+ const char *t = getStringBuf(sb_stdout);
+ if ((*t != '\0') && (*t != '\n')) {
+ rpmlog(RPMLOG_ERR, _("Invalid policy module(s) found:\n%s"), t);
+ goto exit;
+ }
+ }
+
+ rc = RPMRC_OK;
+
+exit:
+ sb_stdout = freeStringBuf(sb_stdout);
+ s = _free(s);
+ return rc;
+}
+
static rpmRC processPolicies(rpmSpec spec, Package pkg, int test)
{
ARGV_t policies = NULL;
@@ -514,6 +564,8 @@ int processBinaryPolicies(rpmSpec spec, int test)
char *nvr;
#if WITH_SELINUX
+ check_policyList = newStringBuf();
+
for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
if (pkg->policyList == NULL) {
continue;
@@ -525,11 +577,16 @@ int processBinaryPolicies(rpmSpec spec, int test)
if (processPolicies(spec, pkg, test) != RPMRC_OK) {
rc = RPMRC_FAIL;
- break;
+ goto exit;
}
}
-#endif
+ rc = checkPolicies(check_policyList);
+
+exit:
+ check_policyList = freeStringBuf(check_policyList);
+
+#endif
return rc;
}
diff --git a/configure.ac b/configure.ac
index 34feb63..d58cfcb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -634,11 +634,31 @@ AS_IF([test "$with_selinux" = yes],[
],[
AC_MSG_ERROR([--with-selinux given, but semanage/semanage.h not found])
])
+
+ AC_CHECK_HEADER([sepol/sepol.h],[
+ save_LIBS="$LIBS"
+ AC_CHECK_LIB([sepol],[sepol_module_package_create],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_module_package_create missing in libsemanage])])
+ AC_CHECK_LIB([sepol],[sepol_module_package_free],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_module_package_free missing in libsemanage])])
+ AC_CHECK_LIB([sepol],[sepol_module_package_read],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_module_package_read missing in libsemanage])])
+ AC_CHECK_LIB([sepol],sepol_policy_file_create[],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_policy_file_create missing in libsemanage])])
+ AC_CHECK_LIB([sepol],[sepol_policy_file_free],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_policy_file_free missing in libsemanage])])
+ AC_CHECK_LIB([sepol],[sepol_policy_file_set_fp],[],[
+ AC_MSG_ERROR([--with-selinux given, but sepol_policy_file_set_fp missing in libsemanage])])
+ LIBS="$save_LIBS"
+ ],[
+ AC_MSG_ERROR([--with-selinux given, but sepol/sepol.h not found])
+ ])
+
])
AS_IF([test "$with_selinux" = yes],[
AC_DEFINE(WITH_SELINUX, 1, [Build with selinux support?])
- WITH_SELINUX_LIB="-lselinux -lsemanage"
+ WITH_SELINUX_LIB="-lselinux -lsemanage -lsepol"
])
AC_SUBST(WITH_SELINUX_LIB)
AM_CONDITIONAL(SELINUX,[test "$with_selinux" = yes])
diff --git a/macros.in b/macros.in
index 3be717d..a0e2853 100644
--- a/macros.in
+++ b/macros.in
@@ -371,6 +371,8 @@ package or when debugging this package.\
# Note: Disable (by commenting out) for legacy compatibility.
%__check_files %{_rpmconfigdir}/check-files %{buildroot}
+%__check_policies %{_rpmconfigdir}/check-policies %{_builddir}/%{?buildsubdir}/
+
#
# Should unpackaged files in a build root terminate a build?
#
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index b2927d9..f0c17d5 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -4,6 +4,8 @@ include $(top_srcdir)/rpm.am
CLEANFILES =
+rpmconfig_PROGRAMS =
+
EXTRA_DIST = \
brp-compress brp-python-bytecompile brp-java-gcjcompile \
brp-strip brp-strip-comment-note brp-python-hardlink \
@@ -41,3 +43,9 @@ rpmconfig_DATA = \
rpmdiff.cgi rpm.daily rpm.log rpm.xinetd \
macros.perl macros.php macros.python
+if SELINUX
+rpmconfig_PROGRAMS += check-policies
+check_policies_SOURCES = check-policies.c
+check_policies_LDADD = @WITH_SELINUX_LIB@
+endif
+
diff --git a/scripts/check-policies.c b/scripts/check-policies.c
new file mode 100644
index 0000000..b119c0f
--- /dev/null
+++ b/scripts/check-policies.c
@@ -0,0 +1,200 @@
+/* Author: Joshua Brindle <method at manicmethod.com>
+ * Chad Sellers <csellers at tresys.com>
+ *
+ * Copyright (C) 2010 Tresys Technology, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <sepol/module.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <bzlib.h>
+#include <stdio_ext.h>
+
+
+void usage(char* cmd);
+ssize_t bunzip(FILE *f, char **data);
+int verify_package(char *filename);
+
+
+void usage(char *cmd) {
+ printf("Usage: %s ROOT_PATH\n", cmd);
+ exit(1);
+}
+
+#define BZ2_MAGICSTR "BZh"
+#define BZ2_MAGICLEN (sizeof(BZ2_MAGICSTR)-1)
+
+/* bunzip() a file to '*data', returning the total number of uncompressed bytes
+ * * in the file. Returns -1 if file could not be decompressed. */
+ssize_t bunzip(FILE *f, char **data)
+{
+ BZFILE* b;
+ size_t nBuf;
+ char *buf = NULL;
+ size_t size = 1<<18;
+ int bzerror;
+ size_t total = 0;
+ ssize_t ret = -1;
+
+ buf = malloc(size);
+ if (!buf)
+ goto bad;
+ bzerror = fread(buf, 1, BZ2_MAGICLEN, f);
+ rewind(f);
+ if ((bzerror != BZ2_MAGICLEN) || memcmp(buf, BZ2_MAGICSTR, BZ2_MAGICLEN))
+ goto bad;
+
+ b = BZ2_bzReadOpen ( &bzerror, f, 0, 1, NULL, 0 );
+ if ( bzerror != BZ_OK ) {
+ BZ2_bzReadClose ( &bzerror, b );
+ goto bad;
+ }
+
+ char *uncompress = realloc(NULL, size);
+
+ while ( bzerror == BZ_OK) {
+ nBuf = BZ2_bzRead ( &bzerror, b, buf, sizeof(buf));
+ if (( bzerror == BZ_OK ) || ( bzerror == BZ_STREAM_END )) {
+ if (total + nBuf > size) {
+ size *= 2;
+ uncompress = realloc(uncompress, size);
+ }
+ memcpy(&uncompress[total], buf, nBuf);
+ total += nBuf;
+ }
+ }
+ if ( bzerror != BZ_STREAM_END ) {
+ BZ2_bzReadClose ( &bzerror, b );
+ free(uncompress);
+ goto bad;
+ }
+ else {
+ ret = total;
+ }
+ BZ2_bzReadClose ( &bzerror, b );
+
+ *data = uncompress;
+bad:
+ free(buf);
+ return ret;
+}
+
+/* Verify a policy package at filename
+ * return -1 for program error (e.g. out of memory)
+ * return 1 for invalid package
+ * return 0 for good pacakge
+ */
+int verify_package(char *filename)
+{
+ int ret = -1;
+ FILE *fp = NULL;
+ struct sepol_policy_file *pf = NULL;
+ sepol_module_package_t *p = NULL;
+ ssize_t size;
+ char *data = NULL;
+
+
+ if (sepol_module_package_create(&p)) {
+ fprintf(stderr, "Out of memory\n");
+ goto bad;
+ }
+ if (sepol_policy_file_create(&pf)) {
+ fprintf(stderr, "Out of memory\n");
+ goto bad;
+ }
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr, "Could not open file %s: %s\n", filename, strerror(errno));
+ goto bad;
+ }
+
+ if ((size = bunzip(fp, &data)) > 0) {
+ fclose(fp);
+ fp = fmemopen(data, size, "rb");
+ if (!fp) {
+ fprintf(stderr, "Out of memory!\n");
+ goto bad;
+ }
+ }
+
+ rewind(fp);
+ __fsetlocking(fp, FSETLOCKING_BYCALLER);
+ sepol_policy_file_set_fp(pf, fp);
+
+ ret = sepol_module_package_read(p, pf, 0);
+ if (ret) {
+ fprintf(stderr, "Error while reading package from %s\n", filename);
+ ret = 1;
+ goto bad;
+ }
+
+ ret = 0;
+
+bad:
+ free(data);
+ sepol_module_package_free(p);
+ sepol_policy_file_free(pf);
+ if (fp)
+ fclose(fp);
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ struct stat s;
+ char path[PATH_MAX];
+ char module[PATH_MAX];
+ int ret;
+
+ if (argc != 2)
+ usage(argv[0]);
+
+ char *root_dir = argv[1];
+
+ if (stat(root_dir, &s) < 0) {
+ fprintf(stderr, "ROOT_PATH %s does not exist\n", root_dir);
+ exit(1);
+ }
+
+ if (!S_ISDIR(s.st_mode)) {
+ fprintf(stderr, "ROOT_PATH %s is not a directory\n", root_dir);
+ exit(1);
+ }
+
+ while (fgets(module, PATH_MAX, stdin)) {
+ if (module[strlen(module) - 1] == '\n') {
+ module[strlen(module) - 1] = '\0';
+ }
+ snprintf(path, PATH_MAX, "%s/%s", root_dir, module);
+ ret = verify_package(path);
+ if (ret == -1) {
+ /* program error, we need to bail */
+ fprintf(stdout, "Error verifying policy %s\n", path);
+ exit(1);
+ } else if (ret == 1) {
+ /* invalid package, print filename to tell caller */
+ fprintf(stdout, " %s\n", path);
+ }
+ }
+ return 0;
+}
--
1.6.2.5
More information about the Rpm-maint
mailing list