summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElena Reshetova <elena.reshetova@intel.com>2013-01-04 12:46:42 +0200
committerAnas Nashif <anas.nashif@intel.com>2013-02-02 16:44:45 -0800
commit61613c459b741f11d26531c0795a432c11fbf4e7 (patch)
treeb3867bd575902efb6388d7fb69c37ac151db3361
parent86e7c4ecfd94737f5ede5cadcc2600ea654c399f (diff)
downloadrpm-61613c459b741f11d26531c0795a432c11fbf4e7.tar.gz
rpm-61613c459b741f11d26531c0795a432c11fbf4e7.tar.bz2
rpm-61613c459b741f11d26531c0795a432c11fbf4e7.zip
Adding security msm plugin
-rw-r--r--configure.ac55
-rw-r--r--lib/fsm.c26
-rw-r--r--lib/package.c9
-rw-r--r--lib/rpmplugins.c72
-rw-r--r--lib/rpmplugins.h62
-rw-r--r--lib/transaction.c9
-rw-r--r--macros.in6
-rw-r--r--packaging/rpm.spec17
-rw-r--r--plugins/Makefile.am7
-rw-r--r--plugins/msm-plugin.c855
-rw-r--r--plugins/msm.h468
-rw-r--r--plugins/msmconfig.c265
-rw-r--r--plugins/msmmanifest.c1501
-rw-r--r--plugins/msmmatch.c71
-rw-r--r--plugins/msmxattr.c1327
-rw-r--r--plugins/plugin.h6
16 files changed, 4739 insertions, 17 deletions
diff --git a/configure.ac b/configure.ac
index 7a8a3085e..e5a11e7cc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -665,6 +665,61 @@ AC_SUBST(WITH_SELINUX_LIB)
AC_SUBST(WITH_SEMANAGE_LIB)
AM_CONDITIONAL(SELINUX,[test "$with_selinux" = yes])
+
+WITH_MSM_LIB=
+AC_ARG_WITH(msm, [AS_HELP_STRING([--with-msm],[build with msm support])],
+[case "$with_msm" in
+yes|no) ;;
+*) AC_MSG_ERROR([invalid argument to --with-msm])
+ ;;
+esac],
+[with_msm=no])
+
+AS_IF([test "$with_msm" = yes],[
+ save_LIBS="$LIBS"
+ AC_CHECK_LIB([xml2],[xmlReaderForMemory],[],[
+ AC_MSG_ERROR([--with-msm given, but xmlReaderForMemory not found in libxml2])])
+ LIBS="$save_LIBS"
+ AC_CHECK_HEADER([sys/capability.h],[
+ save_LIBS="$LIBS"
+ AC_CHECK_LIB([cap],[cap_set_file],[],[
+ AC_MSG_ERROR([--with-msm given, but cap_set_file not found in libcap])])
+ LIBS="$save_LIBS"
+ ],[
+ AC_MSG_ERROR([--with-msm given, but sys/capability.h not found])
+ ])
+ AC_CHECK_HEADER([attr/xattr.h],[
+ save_LIBS="$LIBS"
+ AC_CHECK_LIB([attr],[setxattr],[],[
+ AC_MSG_ERROR([--with-msm given, but setxattr not found in libattr])])
+ LIBS="$save_LIBS"
+ ],[
+ AC_MSG_ERROR([--with-msm given, but attr/xattr.h not found])
+ ])
+ AC_CHECK_HEADER([uthash.h],[
+ save_LIBS="$LIBS"
+ LIBS="$save_LIBS"
+ ],[
+ AC_MSG_ERROR([--with-msm given, but uthash.h not found])
+ ])
+ AC_CHECK_HEADER([sys/smack.h],[
+ save_LIBS="$LIBS"
+ LIBS="$save_LIBS"
+ ],[
+ AC_MSG_ERROR([--with-msm given, but smack.h not found])
+ ])
+])
+
+AS_IF([test "$with_msm" = yes],[
+ AC_DEFINE(WITH_MSM, 1, [Build with msm support?])
+ WITH_MSM_LIB="`xml2-config --libs` -lcap -lattr -lsmack -lmagic"
+ WITH_MSM_INCLUDE="`xml2-config --cflags`"
+])
+AC_SUBST(WITH_MSM_LIB)
+AC_SUBST(WITH_MSM_INCLUDE)
+AM_CONDITIONAL(MSM,[test "$with_msm" = yes])
+
+
# libcap
WITH_CAP_LIB=
AC_ARG_WITH(cap, [AS_HELP_STRING([--with-cap],[build with capability support])],
diff --git a/lib/fsm.c b/lib/fsm.c
index 5ebf28d4d..ab4e18034 100644
--- a/lib/fsm.c
+++ b/lib/fsm.c
@@ -23,6 +23,7 @@
#include "lib/rpmfi_internal.h" /* XXX fi->apath, ... */
#include "lib/rpmte_internal.h" /* XXX rpmfs */
#include "lib/rpmts_internal.h" /* rpmtsSELabelFoo() only */
+#include "lib/rpmplugins.h" /* rpm plugins hooks */
#include "lib/rpmug.h"
#include "lib/cpio.h"
@@ -108,6 +109,7 @@ struct fsm_s {
const char * dirName; /*!< File directory name. */
const char * baseName; /*!< File base name. */
struct selabel_handle *sehandle; /*!< SELinux label handle (if any). */
+ rpmPlugins plugins; /*!< Rpm plugins handle */
unsigned fflags; /*!< File flags. */
rpmFileAction action; /*!< File disposition. */
@@ -1151,9 +1153,10 @@ static int fsmMknod(const char *path, mode_t mode, dev_t dev)
* Create (if necessary) directories not explicitly included in package.
* @param dnli file state machine data
* @param sehandle selinux label handle (bah)
+ * @param plugins rpm plugins handle
* @return 0 on success
*/
-static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle)
+static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle, rpmPlugins plugins)
{
DNLI_t dnli = dnlInitIterator(fi, fs, 0);
struct stat sb;
@@ -1221,6 +1224,10 @@ static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle)
"%s directory created with perms %04o\n",
dn, (unsigned)(mode & 07777));
}
+ if (!rc) {
+ /* Run file closed hook for all plugins */
+ rc = rpmpluginsCallFsmCommit(plugins, dn, mode, DIR_TYPE_UNOWNED);
+ }
*te = '/';
}
if (rc)
@@ -1551,6 +1558,10 @@ static int fsmCommit(FSM_t fsm, int ix)
if (!rc && !getuid()) {
rc = fsmSetSELabel(fsm->sehandle, fsm->path, fsm->sb.st_mode);
}
+ /* Call fsm commit hook for all plugins */
+ if (!rc) {
+ rc = rpmpluginsCallFsmCommit(fsm->plugins, fsm->path, fsm->sb.st_mode, DIR_TYPE_NORMAL);
+ }
if (S_ISLNK(st->st_mode)) {
if (!rc && !getuid())
rc = fsmLChown(fsm->path, fsm->sb.st_uid, fsm->sb.st_gid);
@@ -1640,12 +1651,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfi fi, FD_t cfd,
rc = CPIOERR_INTERNAL;
fsm->sehandle = rpmtsSELabelHandle(ts);
+ fsm->plugins = rpmtsPlugins(ts);
+
/* transaction id used for temporary path suffix while installing */
rasprintf(&fsm->suffix, ";%08x", (unsigned)rpmtsGetTid(ts));
/* Detect and create directories not explicitly in package. */
if (!rc) {
- rc = fsmMkdirs(fi, rpmteGetFileStates(te), fsm->sehandle);
+ rc = fsmMkdirs(fi, rpmteGetFileStates(te), fsm->sehandle, fsm->plugins);
}
while (!rc) {
@@ -1684,6 +1697,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfi fi, FD_t cfd,
break;
}
+ /* Run fsm init hook for all plugins */
+ rc = rpmpluginsCallFsmInit(fsm->plugins, fsm->path, fsm->sb.st_mode);
+
+ /* Exit on error. */
+ if (rc) {
+ fsm->postpone = 1;
+ break;
+ }
+
if (S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1)
fsm->postpone = saveHardLink(fsm, &li);
diff --git a/lib/package.c b/lib/package.c
index b6bea09c7..157c96900 100644
--- a/lib/package.c
+++ b/lib/package.c
@@ -18,6 +18,8 @@
#include "rpmio/rpmio_internal.h" /* fd digest bits */
#include "lib/header_internal.h" /* XXX headerCheck */
+#include "lib/rpmplugins.h" /* rpm plugins hooks */
+
#include "debug.h"
static const unsigned int nkeyids_max = 256;
@@ -495,7 +497,7 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg)
return rc;
}
-static rpmRC rpmpkgRead(rpmKeyring keyring, rpmVSFlags vsflags,
+static rpmRC rpmpkgRead(rpmPlugins plugins, rpmKeyring keyring, rpmVSFlags vsflags,
FD_t fd, const char * fn, Header * hdrp)
{
pgpDigParams sig = NULL;
@@ -646,6 +648,9 @@ static rpmRC rpmpkgRead(rpmKeyring keyring, rpmVSFlags vsflags,
/** @todo Implement disable/enable/warn/error/anal policy. */
rc = rpmVerifySignature(keyring, &sigtd, sig, ctx, &msg);
+
+ /* Run verify hook for all plugins */
+ rc = rpmpluginsCallVerify(plugins, keyring, &sigtd, sig, ctx, rc);
switch (rc) {
case RPMRC_OK: /* Signature is OK. */
@@ -714,7 +719,7 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
rpmVSFlags vsflags = rpmtsVSFlags(ts);
rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
- rc = rpmpkgRead(keyring, vsflags, fd, fn, hdrp);
+ rc = rpmpkgRead(rpmtsPlugins(ts), keyring, vsflags, fd, fn, hdrp);
rpmKeyringFree(keyring);
diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
index d5ab09a9d..2d4fb01cd 100644
--- a/lib/rpmplugins.c
+++ b/lib/rpmplugins.c
@@ -314,3 +314,75 @@ rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int ty
return rc;
}
+
+rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd,
+ pgpDigParams sig, DIGEST_CTX ctx, int res)
+{
+ rpmRC (*hookFunc)(rpmKeyring, rpmtd, pgpDigParams, DIGEST_CTX, int);
+ int i;
+ rpmRC rc = RPMRC_OK;
+ const char *name = NULL;
+
+ for (i = 0; i < plugins->count; i++) {
+ name = plugins->names[i];
+ RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_VERIFY);
+ if (hookFunc(keyring, sigtd, sig, ctx, res) == RPMRC_FAIL)
+ rc = RPMRC_FAIL;
+ }
+
+ return rc;
+}
+
+rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path,
+ mode_t mode)
+{
+ rpmRC (*hookFunc)(const char*, mode_t);
+ int i;
+ rpmRC rc = RPMRC_OK;
+ const char *name = NULL;
+
+ for (i = 0; i < plugins->count; i++) {
+ name = plugins->names[i];
+ RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FSM_INIT);
+ if (hookFunc(path, mode) == RPMRC_FAIL)
+ rc = RPMRC_FAIL;
+ }
+
+ return rc;
+}
+
+rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path,
+ mode_t mode, int type)
+{
+ rpmRC (*hookFunc)(const char*, mode_t, int);
+ int i;
+ rpmRC rc = RPMRC_OK;
+ const char *name = NULL;
+
+ for (i = 0; i < plugins->count; i++) {
+ name = plugins->names[i];
+ RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FSM_COMMIT);
+ if (hookFunc(path, mode, type) == RPMRC_FAIL)
+ rc = RPMRC_FAIL;
+ }
+
+ return rc;
+}
+
+rpmRC rpmpluginsCallFileConflict(rpmPlugins plugins, rpmts ts, char* path,
+ Header oldHeader, rpmfi oldFi, int res)
+{
+ rpmRC (*hookFunc)(rpmts, char*, Header, rpmfi, int);
+ int i;
+ rpmRC rc = RPMRC_OK;
+ const char *name = NULL;
+
+ for (i = 0; i < plugins->count; i++) {
+ name = plugins->names[i];
+ RPMPLUGINS_SET_HOOK_FUNC(PLUGINHOOK_FILE_CONFLICT);
+ if (hookFunc(ts, path, oldHeader, oldFi, res) == RPMRC_FAIL)
+ rc = RPMRC_FAIL;
+ }
+
+ return rc;
+}
diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h
index cff3e6cc1..8e35d81ab 100644
--- a/lib/rpmplugins.h
+++ b/lib/rpmplugins.h
@@ -22,11 +22,16 @@ extern "C" {
#define PLUGINHOOK_PSM_PRE_FUNC pluginhook_psm_pre
#define PLUGINHOOK_PSM_POST_FUNC pluginhook_psm_post
+#define PLUGINHOOK_VERIFY_FUNC pluginhook_verify
#define PLUGINHOOK_SCRIPTLET_PRE_FUNC pluginhook_scriptlet_pre
#define PLUGINHOOK_SCRIPTLET_FORK_POST_FUNC pluginhook_scriptlet_fork_post
#define PLUGINHOOK_SCRIPTLET_POST_FUNC pluginhook_scriptlet_post
+#define PLUGINHOOK_FSM_INIT_FUNC pluginhook_fsm_init
+#define PLUGINHOOK_FSM_COMMIT_FUNC pluginhook_fsm_commit
+#define PLUGINHOOK_FILE_CONFLICT pluginhook_file_conflict
+
enum rpmPluginHook_e {
PLUGINHOOK_NONE = 0,
PLUGINHOOK_INIT = 1 << 0,
@@ -41,9 +46,21 @@ enum rpmPluginHook_e {
PLUGINHOOK_PSM_POST = 1 << 9,
PLUGINHOOK_SCRIPTLET_PRE = 1 << 10,
PLUGINHOOK_SCRIPTLET_FORK_POST = 1 << 11,
- PLUGINHOOK_SCRIPTLET_POST = 1 << 12
+ PLUGINHOOK_SCRIPTLET_POST = 1 << 12,
+ PLUGINHOOK_VERIFY = 1 << 13,
+ PLUGINHOOK_FSM_INIT = 1 << 14,
+ PLUGINHOOK_FSM_COMMIT = 1 << 15,
+ PLUGINHOOK_FILE_CONFLICT = 1 << 16
+
};
+/* indicates if a directory is part of rpm package or created by rpm itself */
+typedef enum rpmPluginDirType_e {
+ DIR_TYPE_NONE = 0,
+ DIR_TYPE_NORMAL = 1 << 0,
+ DIR_TYPE_UNOWNED = 1 << 1
+} rpmPluginDirType;
+
/* indicates the way the scriptlet is executed */
typedef enum rpmScriptletExecutionFlow_e {
RPMSCRIPTLET_NONE = 0,
@@ -207,6 +224,49 @@ rpmRC rpmpluginsCallScriptletForkPost(rpmPlugins plugins, const char *path, int
*/
rpmRC rpmpluginsCallScriptletPost(rpmPlugins plugins, const char *s_name, int type, int res);
+/** \ingroup rpmplugins
+ * Call the verify hook
+ * @param plugins plugins structure
+ * @param keyring RPM keyring
+ * @param sigtd signature tag
+ * @param sig OpenPGP signature parameters
+ * @param res scriptlet execution result code
+ * @return RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+rpmRC rpmpluginsCallVerify(rpmPlugins plugins, rpmKeyring keyring, rpmtd sigtd,
+ pgpDigParams sig, DIGEST_CTX ctx, int res);
+
+/** \ingroup rpmplugins
+ * Call the fsm init hook
+ * @param plugins plugins structure
+ * @param path file full path
+ * @param mode file mode
+ * @return RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+rpmRC rpmpluginsCallFsmInit(rpmPlugins plugins, const char* path, mode_t mode);
+
+/** \ingroup rpmplugins
+ * Call the fsm commit hook
+ * @param plugins plugins structure
+ * @param path file full path
+ * @param mode file mode
+ * @param type file type
+ * @return RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+rpmRC rpmpluginsCallFsmCommit(rpmPlugins plugins, const char* path, mode_t mode, int type);
+
+/** \ingroup rpmplugins
+ * Call the fsm commit hook
+ * @param plugins plugins structure
+ * @param ts transaction set
+ * @param path new file path
+ * @param oldHeader old header
+ * @param oldFi old file
+ * @param res return code
+ * @return RPMRC_OK on success, RPMRC_FAIL otherwise
+ */
+rpmRC rpmpluginsCallFileConflict(rpmPlugins plugins, rpmts ts, char* path, Header oldHeader, rpmfi oldFi, int res);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/transaction.c b/lib/transaction.c
index 3a7cb740c..659a08656 100644
--- a/lib/transaction.c
+++ b/lib/transaction.c
@@ -421,9 +421,16 @@ static void handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi, int fx,
rState = RPMFILE_STATE_WRONGCOLOR;
}
+ if (rConflicts) {
+ char *path = rpmfiFNIndex(fi, fx);
+ /* Call file conflict hook for all plugins */
+ rpmpluginsCallFileConflict(ts->plugins, ts, path, otherHeader, otherFi, rConflicts);
+ }
+
/* Somebody used The Force, lets shut up... */
- if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES)
+ if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
rConflicts = 0;
+ }
if (rConflicts) {
char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA);
diff --git a/macros.in b/macros.in
index 12d6c29e1..1e89aeefa 100644
--- a/macros.in
+++ b/macros.in
@@ -1078,6 +1078,12 @@ PreReq: python >= %minver, python < %maxver
%__collection_sepolicy_flags 1
#------------------------------------------------------------------------------
+# transaction specific macros
+%__transaction_plugins msm
+%__plugindir %{_libdir}/rpm-plugins
+%__transaction_msm %{__plugindir}/msm.so
+
+#------------------------------------------------------------------------------
# Macros for further automated spec %setup and patch application
# default to plain patch
diff --git a/packaging/rpm.spec b/packaging/rpm.spec
index ee3d049a8..7f56c8eee 100644
--- a/packaging/rpm.spec
+++ b/packaging/rpm.spec
@@ -22,12 +22,11 @@ BuildRequires: pkgconfig(popt)
BuildRequires: xz-devel
BuildRequires: pkgconfig(zlib)
BuildRequires: pkgconfig(nss)
-%if 0%{?enable_security}
BuildRequires: uthash-devel
BuildRequires: libxml2-devel
BuildRequires: libattr-devel
-BuildRequires: libsmack-devel
-%endif
+BuildRequires: pkgconfig(libsmack)
+
Provides: rpminst
Provides: rpm-libs
@@ -93,10 +92,8 @@ and requires some packages that are usually required
Summary: MSM security plugin for rpm
Group: Development/Libraries
Requires: rpm = %{version}-%{release}
-Requires: libsmack
+Requires: smack
Requires: libxml2
-Requires: file
-Requires: uthash
Requires: nss
%description security-plugin
@@ -141,7 +138,7 @@ BUILDTARGET="--build=%{_target_cpu}-tizen-linux"
autoreconf -i -f
./configure --disable-dependency-tracking --prefix=%{_prefix} --mandir=%{_mandir} --infodir=%{_infodir} \
--libdir=%{_libdir} --sysconfdir=/etc --localstatedir=/var --with-lua \
---with-acl --with-cap --enable-shared %{?with_python: --enable-python} $BUILDTARGET
+--with-acl --with-cap --enable-shared %{?with_python: --enable-python} --with-msm $BUILDTARGET
make %{?_smp_mflags}
@@ -160,9 +157,7 @@ mkdir -p %{buildroot}%{_sysconfdir}/rpm
cp -a tizen_macros %{buildroot}/usr/lib/rpm
mkdir -p %{buildroot}/usr/lib/rpm/tizen
install -m 755 %{SOURCE13} %{buildroot}/usr/lib/rpm/tizen
-%if 0%{?enable_security}
install -m 644 %{SOURCE22} ${RPM_BUILD_ROOT}%{_sysconfdir}/device-sec-policy
-%endif
ln -s ../tizen_macros %{buildroot}/usr/lib/rpm/tizen/macros
for d in BUILD RPMS SOURCES SPECS SRPMS BUILDROOT ; do
mkdir -p %{buildroot}/usr/src/packages/$d
@@ -291,9 +286,9 @@ rm -f var/lib/rpm/Filemd5s var/lib/rpm/Filedigests var/lib/rpm/Requireversion va
%{_libdir}/librpmio.so
%{_libdir}/librpmsign.so
%{_libdir}/pkgconfig/rpm.pc
-%if 0%{?enable_security}
+
%files security-plugin
%defattr(-,root,root)
%{_libdir}/rpm-plugins/msm.so
%config(noreplace) %{_sysconfdir}/device-sec-policy
-%endif
+
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index a9c962cb7..5fd7662f1 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -24,3 +24,10 @@ sepolicy_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmi
plugins_LTLIBRARIES += sepolicy.la
endif
+
+if MSM
+msm_la_SOURCES = plugin.h msm.h msm-plugin.c msmconfig.c msmmatch.c msmxattr.c msmmanifest.c
+msm_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la @WITH_MSM_LIB@
+
+plugins_LTLIBRARIES += msm.la
+endif
diff --git a/plugins/msm-plugin.c b/plugins/msm-plugin.c
new file mode 100644
index 000000000..b62ab8a8a
--- /dev/null
+++ b/plugins/msm-plugin.c
@@ -0,0 +1,855 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tero Aho <ext-tero.aho@nokia.com>
+ *
+ * Copyright (C) 2011 -2013 Intel Corporation.
+ *
+ * Contact: Elena Reshetova <elena.reshetova@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include "plugin.h"
+#include "debug.h"
+
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <sys/stat.h>
+
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmpgp.h>
+#include <rpm/rpmkeyring.h>
+#include <rpm/rpmdb.h>
+
+#include "rpmio/rpmbase64.h"
+#include "rpmio/rpmlog.h"
+
+#include "msm.h"
+
+/*hooks that are used in msm plugin */
+
+rpmPluginHook PLUGIN_HOOKS = \
+ PLUGINHOOK_INIT | \
+ PLUGINHOOK_CLEANUP | \
+ PLUGINHOOK_TSM_PRE | \
+ PLUGINHOOK_TSM_POST | \
+ PLUGINHOOK_PSM_PRE | \
+ PLUGINHOOK_PSM_POST | \
+ PLUGINHOOK_FSM_COMMIT | \
+ PLUGINHOOK_FSM_INIT | \
+ PLUGINHOOK_FILE_CONFLICT | \
+ PLUGINHOOK_VERIFY;
+
+
+typedef struct fileconflict {
+ const char *path;
+ const char *pkg_name;
+ sw_source_x *sw_source;
+ UT_hash_handle hh;
+} fileconflict;
+
+typedef struct packagecontext {
+ char *data; /*!< base64 manifest data */
+ manifest_x *mfx; /*!< parsed manifest data */
+ rpmte te; /*!< related te */
+ struct packagecontext *next; /*!< next in linked list */
+ struct smack_accesses *smack_accesses; /*!< handle to smack_accesses */
+} packagecontext;
+
+static rpmts ts = NULL;
+static int rootSWSource= 0;
+static manifest_x *root = NULL; /* pointer to device security policy file */
+static packagecontext *context = NULL;
+static sw_source_x *current = NULL;
+static packagecontext *contextsHead = NULL;
+static packagecontext *contextsTail = NULL;
+static fileconflict *allfileconflicts = NULL;
+static char* ownSmackLabel = NULL;
+static int SmackEnabled = 0;
+static magic_t cookie = NULL;
+static int package_created = 0;
+
+rpmRC PLUGINHOOK_INIT_FUNC(rpmts _ts, const char *name, const char *opts)
+{
+ ts = _ts;
+ int res = 0;
+
+ rpmlog(RPMLOG_INFO, "reading device security policy from %s\n", DEVICE_SECURITY_POLICY);
+ root = msmProcessDevSecPolicyXml(DEVICE_SECURITY_POLICY);
+
+ if (root) {
+ if (msmSetupSWSources(NULL, root, NULL)) {
+ rpmlog(RPMLOG_ERR, "Failed to setup device security policy from %s\n",
+ DEVICE_SECURITY_POLICY);
+ return RPMRC_FAIL;
+ }
+ } else {
+ /* Do not allow plug-in to proceed without security policy existing */
+ rpmlog(RPMLOG_ERR, "Failed to process sw sources from %s\n",
+ DEVICE_SECURITY_POLICY);
+ return RPMRC_FAIL;
+ }
+
+ /* check its own security context and store it for the case when packages without manifest will be installed */
+ struct stat buf;
+
+ if (stat(SMACK_LOAD_PATH, &buf) == 0) {
+ res = smack_new_label_from_self(&ownSmackLabel);
+ SmackEnabled = 1;
+ if (res != 0) {
+ rpmlog(RPMLOG_ERR, "Failed to obtain rpm security context\n");
+ return RPMRC_FAIL;
+ }
+ } else {
+ rpmlog(RPMLOG_INFO, "Smack disabled in kernel. Going to the image build mode. \n");
+ ownSmackLabel = strdup("_");
+ SmackEnabled = 0;
+ }
+
+ if (stat(SMACK_RULES_PATH, &buf) != 0) {
+ rpmlog(RPMLOG_INFO, "A directory for writing smack rules is missing. Creating one.\n");
+ mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH; // 644 -rwer--r--
+ if (stat(SMACK_RULES_PATH_BEG, &buf) != 0) {
+ if (mkdir(SMACK_RULES_PATH_BEG, mode) != 0) {
+ rpmlog(RPMLOG_ERR, "Failed to create a sub-directory for smack rules\n");
+ return RPMRC_FAIL;
+ }
+ }
+ if (mkdir(SMACK_RULES_PATH, mode) != 0){
+ rpmlog(RPMLOG_ERR, "Failed to create a directory for smack rules\n");
+ return RPMRC_FAIL;
+ }
+ }
+
+ rpmlog(RPMLOG_DEBUG, "rpm security context: %s\n", ownSmackLabel);
+
+ cookie = magic_open(0);
+ if (!cookie)
+ return RPMRC_FAIL;
+
+ if (magic_load(cookie, NULL) != 0) {
+ rpmlog(RPMLOG_ERR, "cannot load magic database - %s\n", magic_error(cookie));
+ magic_close(cookie);
+ return RPMRC_FAIL;
+ }
+
+ return RPMRC_OK;
+}
+
+static int findSWSourceByName(sw_source_x *sw_source, void *param, void* param2)
+{
+ const char *name = (const char *)param;
+ return strcmp(sw_source->name, name);
+}
+
+rpmRC PLUGINHOOK_FILE_CONFLICT_FUNC(rpmts ts, char* path,
+ Header oldHeader, rpmfi oldFi,
+ int rpmrc)
+{
+ fileconflict *fc;
+ if (!path)
+ return rpmrc;
+ rpmlog(RPMLOG_DEBUG, "FILE_CONFLICT_FUNC hook path %s\n",path);
+
+ const char *name = headerGetString(oldHeader, RPMTAG_SECSWSOURCE);
+ if (!name || !root) {
+ return rpmrc; /* no sw source(s) - abnormal state */
+ }
+ const char *pkg_name = headerGetString(oldHeader, RPMTAG_NAME);
+
+ sw_source_x *sw_source = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)name, NULL);
+ if (!sw_source)
+ return rpmrc; /* no old sw_source - abnormal state */
+
+ HASH_FIND(hh, allfileconflicts, path, strlen(path), fc);
+ if (!fc) {
+ /* Add new file conflict into hash */
+ fc = xcalloc(1, sizeof(*fc));
+ if (!fc) return RPMRC_FAIL;
+ fc->path = path;
+ fc->sw_source = sw_source;
+ fc->pkg_name = pkg_name;
+ HASH_ADD_KEYPTR(hh, allfileconflicts, path, strlen(path), fc);
+ } else {
+ /* Many packages have installed the same file */
+ if (strcmp(sw_source->rankkey, fc->sw_source->rankkey) <= 0) {
+ /* Change sw source to the higher ranked one */
+ fc->sw_source = sw_source;
+ }
+ msmFreePointer((void**)&path);
+ }
+
+ if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES) {
+ /* Conflict has been noted, now return ok. It will be actually */
+ /* resolved later when conflicting package signature is verified */
+ /* and sw_source is known. */
+ return rpmrc;
+ }
+ return rpmrc;
+}
+
+rpmRC PLUGINHOOK_TSM_PRE_FUNC(rpmts ts)
+{
+ return RPMRC_OK;
+}
+
+static int findSWSourceBySignature(sw_source_x *sw_source, void *param, void* param2)
+{
+ origin_x *origin;
+ keyinfo_x *keyinfo;
+ pgpDigParams sig = (pgpDigParams)param;
+ DIGEST_CTX ctx = (DIGEST_CTX)param2;
+ pgpDigParams key = NULL;
+ int res = 0;
+
+ for (origin = sw_source->origins; origin; origin = origin->prev) {
+ for (keyinfo = origin->keyinfos; keyinfo; keyinfo = keyinfo->prev) {
+ if (pgpPrtParams(keyinfo->keydata, keyinfo->keylen, PGPTAG_PUBLIC_KEY, &key)) {
+ rpmlog(RPMLOG_INFO, "invalid sw source key\n");
+ return -1;
+ }
+ if (pgpVerifySignature(key, sig, ctx) == RPMRC_OK) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+rpmRC PLUGINHOOK_VERIFY_FUNC(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, DIGEST_CTX ctx, int rpmrc)
+{
+ current = NULL;
+
+#if 0
+ if (!root) {
+ if (rpmrc == RPMRC_NOKEY) {
+ rpmlog(RPMLOG_INFO, "package verified as root sw source\n");
+ rootSWSource = 1; /* accept any signed package as root */
+ return RPMRC_OK;
+ }
+ rpmlog(RPMLOG_ERR, "No device security policy, cannot verify signature\n");
+ return rpmrc;
+ }
+
+
+// make currently that even non-signed package with root policy will be treated as trusted
+
+ if (!root) {
+ rpmlog(RPMLOG_INFO, "package verified as root sw source\n");
+ rootSWSource = 1; /* accept any signed package as root */
+ return RPMRC_OK;
+ }
+
+//------------------
+#endif
+
+ if (!root) {
+ rpmlog(RPMLOG_INFO, "No device policy found\n");
+ rootSWSource = 1; /* accept any signed package as root */
+ return rpmrc;
+ }
+
+ if (rpmrc == RPMRC_NOKEY) {
+ /* No key, revert to unknown sw source. */
+ rpmlog(RPMLOG_INFO, "no key for signature, cannot search sw source\n");
+ goto exit;
+ }
+ if (rpmrc) {
+ /* RPM failed to verify signature */
+ rpmlog(RPMLOG_ERR, "Invalid signature, cannot search sw source\n");
+ return rpmrc;
+ }
+ if (sigtd->tag != RPMSIGTAG_RSA) {
+ /* Not RSA, revert to unknown sw source. */
+ rpmlog(RPMLOG_INFO, "no RSA signature, cannot search sw source\n");
+ goto exit;
+ }
+ current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceBySignature, sig, ctx);
+ if (current)
+ rpmlog(RPMLOG_INFO, "signature matches sw source %s\n", current->name);
+ else
+ rpmlog(RPMLOG_INFO, "valid signature but no matching sw source\n");
+
+ exit:
+ if (!current) {
+ current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"_default_", NULL);
+ if (current)
+ rpmlog(RPMLOG_INFO, "using _default_ sw source\n");
+ else { // for now in case default sw source isn't there yet, allow to think that it is coming from root
+ current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"root", NULL);
+ if (current)
+ rpmlog(RPMLOG_INFO, "using _root_ sw source now for testing\n");
+ }
+ }
+
+ return rpmrc;
+}
+
+static packagecontext *msmNew(rpmte te)
+{
+ Header h;
+ struct rpmtd_s msm;
+ int count;
+ packagecontext *ctx = NULL;
+ const char *sw_source = NULL;
+
+ rpmtdReset(&msm);
+
+ h = rpmteHeader(te);
+ if (!h) return NULL;
+
+ ctx = xcalloc(1, sizeof(*ctx));
+ if (!ctx) {
+ goto exit1;
+ }
+ ctx->te = te;
+
+ if (!headerIsEntry(h, RPMTAG_SECMANIFEST)) {
+ goto exit1;
+ }
+
+ if (!headerGet(h, RPMTAG_SECMANIFEST, &msm, HEADERGET_MINMEM)) {
+ goto exit1;
+ }
+
+ count = rpmtdCount(&msm);
+ if (count != 1) {
+ goto exit2;
+ }
+
+ ctx->data = xstrdup(rpmtdNextString(&msm));
+ rpmlog(RPMLOG_INFO, "%s manifest b64 data: %.40s...\n",
+ rpmteN(ctx->te), ctx->data);
+
+ exit2:
+ rpmtdFreeData(&msm);
+ exit1:
+ if (rpmteType(ctx->te) == TR_ADDED) {
+ /* Save sw_source name into database, we need it when package */
+ /* is removed because signature verify is not called then. */
+ if (current) sw_source = current->name;
+ else if (rootSWSource) sw_source = rpmteN(ctx->te);
+
+ if (!sw_source || !headerPutString(h, RPMTAG_SECSWSOURCE, sw_source)) {
+ rpmlog(RPMLOG_ERR, "Failed to save sw source for %s, sw_source: %s\n",
+ rpmteN(ctx->te), sw_source);
+ msmFreePointer((void**)&ctx->data);
+ msmFreePointer((void**)&ctx);
+ }
+ }
+ headerFree(h);
+
+ return ctx;
+}
+
+static packagecontext *msmAddTE(rpmte te)
+{
+ packagecontext *ctx = msmNew(te);
+ if (ctx) {
+ /* add the new policy to the list */
+ if (!contextsHead) {
+ contextsHead = ctx;
+ contextsTail = ctx;
+ } else {
+ if (rpmteType(te) == TR_ADDED) {
+ /* add to the end of the list */
+ contextsTail->next = ctx;
+ contextsTail = ctx;
+ } else {
+ /* add to the beginning of the list */
+ ctx->next = contextsHead;
+ contextsHead = ctx;
+ }
+ }
+ }
+ return ctx;
+}
+
+rpmRC PLUGINHOOK_PSM_PRE_FUNC(rpmte te)
+{
+ packagecontext *ctx = NULL;
+ manifest_x *mfx = NULL;
+ char *xml = NULL;
+ size_t xmllen;
+ rpmRC rc = RPMRC_OK;
+ int ret = 0;
+
+ if (!root && !rootSWSource) {
+ /* no sw source config, just exit */
+ goto exit;
+ }
+
+ if (!current) {
+ /* this means that verify hook has not been called */
+ current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)"_default_", NULL);
+ if (current)
+ rpmlog(RPMLOG_INFO, "using _default_ sw source\n");
+ else {
+ rpmlog(RPMLOG_ERR, "Default source isn't availiable. Package source can't be determined. Abort installation\n");
+ goto fail;
+ }
+ }
+
+ ctx = msmAddTE(te);
+ if (!ctx) {
+ rpmlog(RPMLOG_ERR, "Failed to create security context for %s\n",
+ rpmteNEVRA(te));
+ goto fail;
+ }
+
+ if (rpmteType(ctx->te) == TR_REMOVED) {
+
+ /* Verify hook is not called before remove, */
+ /* so get the sw_source name from package header */
+ Header h = rpmteHeader(te);
+ if (h) {
+ const char *name = headerGetString(h, RPMTAG_SECSWSOURCE);
+ if (name) {
+ current = msmSWSourceTreeTraversal(root->sw_sources, findSWSourceByName, (void *)name, NULL);
+ rpmlog(RPMLOG_INFO, "removing %s from sw source %s\n",
+ rpmteN(ctx->te), name);
+ }
+ headerFree(h);
+ }
+ /* if (!current) {
+ rpmlog(RPMLOG_INFO, "no sw source for removing %s\n", rpmteN(ctx->te));
+ goto exit;
+ }*/
+ }
+
+ if (!ctx->data) {
+ rpmlog(RPMLOG_INFO, "No manifest in this package. Creating default one\n");
+
+ /* create default manifest manually. Make the package to belong to the domain where rpm is running */
+
+ mfx = calloc(1, sizeof(manifest_x));
+ if (!mfx) goto fail;
+ mfx->sw_source = current;
+ mfx->name = strdup(rpmteN(ctx->te));
+ mfx->request = calloc(1, sizeof(request_x));
+ if (!mfx->request) {
+ msmFreePointer((void**)&mfx->name);
+ msmFreePointer((void**)&mfx);
+ goto fail;
+ }
+ mfx->request->ac_domain = strdup(ownSmackLabel);
+ rpmlog(RPMLOG_DEBUG, "Done with manifest creation\n");
+
+ } else {
+ if (rpmBase64Decode(ctx->data, (void **) &xml, &xmllen) != 0) {
+ rpmlog(RPMLOG_ERR, "Failed to decode manifest for %s\n",
+ rpmteN(ctx->te));
+ goto fail;
+ }
+
+ rpmlog(RPMLOG_INFO, "parsing %s manifest: \n%s", rpmteN(ctx->te), xml);
+ mfx = msmProcessManifestXml(xml, xmllen, current, rpmteN(ctx->te));
+
+ if (!mfx) {
+ rpmlog(RPMLOG_ERR, "Failed to parse manifest for %s\n",
+ rpmteN(ctx->te));
+ goto fail;
+ }
+ }
+
+
+ ctx->mfx = mfx;
+
+ int res = smack_accesses_new(&(ctx->smack_accesses));
+ if (res != 0) {
+ rpmlog(RPMLOG_ERR, "Failed to create smack access set\n");
+ goto fail;
+ }
+
+ if (rpmteType(ctx->te) == TR_ADDED) {
+
+ rpmlog(RPMLOG_DEBUG, "Installing the package\n");
+
+ package_x *package = NULL;
+
+ if (rootSWSource) {
+ /* this is the first package */
+ package = msmCreatePackage(mfx->name, mfx->sw_sources,
+ mfx->provides, NULL);
+ } else if (mfx->sw_source) {
+ /* all packages must have sw_source */
+ package = msmCreatePackage(mfx->name, mfx->sw_source,
+ mfx->provides, NULL);
+ } else {
+ rpmlog(RPMLOG_ERR, "Package doesn't have a sw source. Abnormal situation. Abort.\n");
+ goto fail;
+ }
+
+ if (!package) {
+ rpmlog(RPMLOG_ERR, "Package could not be created. \n");
+ goto fail;
+ }
+
+ mfx->provides = NULL; /* owned by package now */
+
+ if (!package->sw_source) { /* this must never happen */
+ rpmlog(RPMLOG_ERR, "Install failed. Check that configuration has at least root sw source installed.\n");
+ msmFreePackage(package);
+ package = NULL;
+ goto fail;
+ }
+
+ rpmlog(RPMLOG_INFO, "adding %s manifest data to system, package_name %s\n",
+ rpmteN(ctx->te), package->name);
+
+ if (msmSetupPackages(ctx->smack_accesses, package, package->sw_source)) {
+ rpmlog(RPMLOG_ERR, "Package setup failed for %s\n", rpmteN(ctx->te) );
+ msmFreePackage(package);
+ package = NULL;
+ goto fail;
+ }
+
+ if (rootSWSource) {
+ /* current is root */
+ root = ctx->mfx;
+ }
+
+ rpmlog(RPMLOG_DEBUG, "Starting the security setup...\n");
+ unsigned int smackLabel = 0;
+
+ if (rootSWSource || ctx->mfx->sw_source) {
+ if (ctx->mfx->sw_sources) {
+ smackLabel = 1; /* setting this one on since this manifest doesn't have any define/request section */
+ ret = msmSetupSWSources(ctx->smack_accesses, ctx->mfx, ts);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "SW source setup failed for %s\n",
+ rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+ if (ctx->mfx->define) {
+ if (ctx->mfx->define->name)
+ smackLabel = 1;
+ ret = msmSetupDefine(ctx->smack_accesses, ctx->mfx);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "AC domain setup failed for %s\n",
+ rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+ if (ctx->mfx->request) {
+ if (ctx->mfx->request->ac_domain)
+ smackLabel = 1;
+ ret = msmSetupRequests(ctx->mfx);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "Request setup failed for %s\n",
+ rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+ if (ctx->smack_accesses) {
+ ret = msmSetupSmackRules(ctx->smack_accesses, ctx->mfx->name, 0, SmackEnabled);
+ smack_accesses_free(ctx->smack_accesses);
+ ctx->smack_accesses = NULL;
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "Setting up smack rules for %s failed\n",
+ rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+ if (package->provides) {
+ ret = msmSetupDBusPolicies(package);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "Setting up dbus policies for %s failed\n",
+ rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+
+ /* last check is needed in order to catch in advance
+ the situation when no ac domain defined or requested */
+ if (smackLabel == 0) {
+ rpmlog(RPMLOG_ERR, "No ac domain defined or requested for package %s. Abort.\n", rpmteN(ctx->te));
+ msmCancelPackage(ctx->mfx->name);
+ goto fail;
+ }
+ }
+
+
+ } else if (rpmteDependsOn(ctx->te)) { /* TR_REMOVED */
+ rpmlog(RPMLOG_INFO, "upgrading package %s by %s\n",
+ rpmteNEVR(ctx->te), rpmteNEVR(rpmteDependsOn(ctx->te)));
+ } else if (mfx->sw_sources) {
+ rpmlog(RPMLOG_ERR, "Cannot remove sw source package %s\n",
+ rpmteN(ctx->te));
+ goto fail;
+ }
+
+ rpmlog(RPMLOG_DEBUG, "Finished with pre psm hook \n");
+ package_created = 1;
+
+ goto exit;
+
+ fail: /* error, cancel the rpm operation */
+ rc = RPMRC_FAIL;
+
+ exit: /* success, continue rpm operation */
+ context = ctx;
+ msmFreePointer((void**)&xml);
+
+ return rc;
+}
+
+rpmRC PLUGINHOOK_FSM_INIT_FUNC(const char* path, mode_t mode)
+{
+
+ //check if there any conflicts that prevent file being written to the disk
+
+ fileconflict *fc;
+ packagecontext *ctx = context;
+ char * cleanedPath = NULL, *dupPath = NULL;
+
+ rpmlog(RPMLOG_DEBUG, "Started with FSM_INIT_FUNC hook for file: %s\n", path);
+
+ if (!ctx) return RPMRC_FAIL;
+ if (!path) return RPMRC_FAIL;
+
+ dupPath = strdup(path);
+ cleanedPath = strchr(dupPath, ';');
+ if (cleanedPath)
+ *cleanedPath = '\0';
+
+ //rpmlog(RPMLOG_DEBUG, "dupapth: %s\n", dupPath);
+
+ HASH_FIND(hh, allfileconflicts, dupPath, strlen(dupPath), fc);
+ msmFreePointer((void**)&dupPath);
+
+ if (fc) {
+ //rpmlog(RPMLOG_DEBUG, "rpmteN(ctx->te) %s fc->pkg_name: %s\n", rpmteN(ctx->te), fc->pkg_name);
+ /* There is a conflict, see if we are not allowed to overwrite */
+ if ((!current || (strcmp(current->rankkey, fc->sw_source->rankkey) >= 0)) && (strcmp(rpmteN(ctx->te), fc->pkg_name))){
+ rpmlog(RPMLOG_ERR, "%s has file conflict in %s from sw source %s\n",
+ rpmteN(ctx->te), fc->path, fc->sw_source->name);
+ return RPMRC_FAIL;
+ }
+ rpmlog(RPMLOG_INFO, "%s from %s overwrites %s from %s\n",
+ rpmteN(ctx->te), current->name, fc->path, fc->sw_source->name);
+ }
+
+ rpmlog(RPMLOG_DEBUG, "Finished with FSM_INIT_FUNC hook for file: %s\n", path);
+
+ return RPMRC_OK;
+}
+
+rpmRC PLUGINHOOK_FSM_COMMIT_FUNC(const char* path, mode_t mode, int type)
+{
+ packagecontext *ctx = context;
+ if (!ctx) return RPMRC_FAIL;
+ if (!path) return RPMRC_FAIL;
+
+ /* the type is ignored for now */
+
+ rpmlog(RPMLOG_DEBUG, "Started with FSM_COMMIT_FUNC hook for file: %s\n", path);
+
+ if (ctx->mfx) {
+ file_x *file = xcalloc(1, sizeof(*file));
+ if (file) {
+ file->path = strndup(path, strlen(path) + 1);
+ LISTADD(ctx->mfx->files, file);
+ if (rpmteType(ctx->te) == TR_ADDED) {
+ if (msmSetFileXAttributes(ctx->mfx, file->path, cookie) < 0) {
+ rpmlog(RPMLOG_ERR, "Setting of extended attributes failed for file %s from package %s\n",
+ file->path, rpmteN(ctx->te));
+ return RPMRC_FAIL;
+ }
+ }
+
+ } else
+ return RPMRC_FAIL;
+ } else {
+ rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
+ rpmteN(ctx->te));
+ return RPMRC_FAIL;
+ }
+
+ rpmlog(RPMLOG_DEBUG, "Finished with FSM_COMMIT_FUNC hook for file: %s\n", path);
+ return RPMRC_OK;
+}
+
+rpmRC PLUGINHOOK_PSM_POST_FUNC(rpmte te, int rpmrc)
+{
+
+ int ret = 0;
+ packagecontext *ctx = context;
+ if (!ctx) return RPMRC_FAIL;
+
+ if (!package_created) {
+ /* failure in rpm pre psm hook, rollback */
+ return RPMRC_FAIL;
+ }
+
+ if (rpmrc) {
+ /* failure in rpm psm, rollback */
+ if (rpmteType(ctx->te) == TR_ADDED)
+ msmCancelPackage(ctx->mfx->name);
+ goto exit;
+ }
+
+ if (!ctx->mfx){
+ rpmlog(RPMLOG_ERR, "Manifest is missing while it should be present for the package %s\n",
+ rpmteN(ctx->te));
+ goto exit;
+ }
+
+ if (rootSWSource) {
+ /* current is root */
+ root = context->mfx;
+ }
+
+
+ if (rpmteType(ctx->te) == TR_REMOVED) {
+ if (ctx->mfx->sw_source) {
+ if (rpmteDependsOn(ctx->te)) {
+ rpmlog(RPMLOG_INFO, "upgrading %s manifest data\n",
+ rpmteN(ctx->te));
+ } else {
+ rpmlog(RPMLOG_INFO, "removing %s manifest data\n",
+ rpmteN(ctx->te));
+ if (ctx->mfx->define || ctx->mfx->provides || ctx->mfx->sw_sources) {
+ msmRemoveRules(ctx->smack_accesses, ctx->mfx, SmackEnabled);
+ }
+ msmRemoveConfig(ctx->mfx);
+ }
+ }
+
+ }
+
+ exit:
+ current = NULL;
+
+ if (ret) {
+ return RPMRC_FAIL;
+ }
+ return rpmrc;
+}
+
+rpmRC PLUGINHOOK_TSM_POST_FUNC(rpmts ts, int rpmrc)
+{
+ packagecontext *ctx = context;
+ if (!ctx) return RPMRC_FAIL;
+ return RPMRC_OK;
+}
+
+static packagecontext *msmFree(packagecontext *ctx)
+{
+
+ while (ctx) {
+ packagecontext *next = ctx->next;
+ msmFreePointer((void**)&ctx->data);
+ ctx->mfx = msmFreeManifestXml(ctx->mfx);
+ if (ctx->smack_accesses) smack_accesses_free(ctx->smack_accesses);
+ msmFreePointer((void**)&ctx);
+ ctx = next;
+ }
+
+ return NULL;
+
+}
+
+rpmRC PLUGINHOOK_CLEANUP_FUNC(void)
+{
+
+ msmFreeInternalHashes(); // free hash structures first
+
+ if (root) {
+ msmSaveDeviceSecPolicyXml(root);
+ if (!rootSWSource) root = msmFreeManifestXml(root);
+ }
+
+ ts = NULL;
+
+ contextsHead = contextsTail = msmFree(contextsHead);
+ contextsHead = contextsTail = NULL;
+
+ if (allfileconflicts) {
+ fileconflict *fc, *temp;
+ HASH_ITER(hh, allfileconflicts, fc, temp) {
+ HASH_DELETE(hh, allfileconflicts, fc);
+ msmFreePointer((void**)&fc->path);
+ msmFreePointer((void**)&fc);
+ }
+ }
+
+ msmFreePointer((void**)&ownSmackLabel);
+ if (cookie) magic_close(cookie);
+
+ return RPMRC_OK;
+}
+
+const char *msmQueryPackageFile(const char *rfor,
+ const char **dname, const char **pname)
+{
+ int match = 0;
+ const char *path = NULL;
+
+ if (ts) {
+ char *sep = strchr(rfor, ':');
+ if (sep && sep[1] == ':' && sep[2] == '/')
+ path = &sep[2];
+ if (!path) return NULL;
+
+ rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, path, 0);
+ if (!mi)
+ mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, path, 0);
+ if (mi) {
+ Header h;
+ const char *name, *sw_source;
+ while ((h = rpmdbNextIterator(mi))) {
+ rpmdbCheckSignals();
+ name = headerGetString(h, RPMTAG_NAME);
+ sw_source = headerGetString(h, RPMTAG_SECSWSOURCE);
+ if (name && sw_source) {
+ match = !strncmp(rfor, name, path - rfor - 2);
+ rpmlog(RPMLOG_INFO, "file %s belongs to package %s in sw source %s %s\n", path, name, sw_source, (match ? "(matched request)" : ""));
+ if (match) {
+ *pname = xstrdup(name);
+ *dname = xstrdup(sw_source);
+ break;
+ }
+ }
+ }
+ mi = rpmdbFreeIterator(mi);
+ }
+ }
+ return match ? path : NULL;
+}
+
+void msmFreePointer(void** ptr)
+{
+ if (*ptr)
+ free(*ptr);
+ *ptr = NULL;
+ return;
+}
diff --git a/plugins/msm.h b/plugins/msm.h
new file mode 100644
index 000000000..3a6e75cba
--- /dev/null
+++ b/plugins/msm.h
@@ -0,0 +1,468 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tero Aho <ext-tero.aho@nokia.com>
+ *
+ * Copyright (C) 2011 - 2013 Intel Corporation.
+ *
+ * Contact: Elena Reshetova <elena.reshetova@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#ifndef MSM_H
+#define MSM_H
+
+#define IMA "security.ima"
+#define SMACK64TRANSMUTE "security.SMACK64TRANSMUTE"
+#define SMACK64 "security.SMACK64"
+#define SMACK64EXEC "security.SMACK64EXEC"
+
+#define SMACK_RULES_PATH "/etc/smack/accesses.d/"
+#define SMACK_RULES_PATH_BEG "/etc/smack/"
+#define DEVICE_SECURITY_POLICY "/etc/device-sec-policy"
+#define SMACK_LOAD_PATH "/smack/load"
+
+#define SMACK_ISOLATED_LABEL "Isolated"
+
+#define SMACK_LABEL_LENGTH 255
+#define SMACK_ACCESS_TYPE_LENGHT 5
+#define SMACK_UNINSTALL 1
+#define RANK_LIMIT 10000
+
+#define DBUS_SERVICE 1
+#define DBUS_PATH 2
+#define DBUS_INTERFACE 3
+#define DBUS_METHOD 4
+#define DBUS_SIGNAL 5
+
+#include <uthash.h>
+#include <sys/capability.h>
+#include <sys/smack.h>
+#include <magic.h>
+#include "rpmio/rpmio.h"
+
+/** \ingroup msm
+ * List definitions.
+ * All lists are doubly-linked, the last element is stored to list pointer,
+ * which means that lists must be looped using the prev pointer, or by
+ * calling LISTHEAD first to go to start in order to use the next pointer.
+ */
+#define LISTADD(list, node) \
+ do { \
+ (node)->prev = (list); \
+ if (list) (node)->next = (list)->next; \
+ else (node)->next = NULL; \
+ if (list) (list)->next = (node); \
+ (list) = (node); \
+ } while (0);
+
+#define NODEADD(node1, node2) \
+ do { \
+ (node2)->prev = (node1); \
+ (node2)->next = (node1)->next; \
+ if ((node1)->next) (node1)->next->prev = (node2); \
+ (node1)->next = (node2); \
+ } while (0);
+
+#define LISTCAT(list, first, last) \
+ if ((first) && (last)) { \
+ (first)->prev = (list); \
+ (list) = (last); \
+ }
+
+#define LISTDEL(list, node) \
+ do { \
+ if ((node)->prev) (node)->prev->next = (node)->next; \
+ if ((node)->next) (node)->next->prev = (node)->prev; \
+ if (!((node)->prev) && !((node)->next)) (list) = NULL; \
+ } while (0);
+
+#define LISTHEAD(list, node) \
+ for ((node) = (list); (node)->prev; (node) = (node)->prev);
+
+#define LISTTAIL(list, node) \
+ for ((node) = (list); (node)->next; (node) = (node)->next);
+
+/** \ingroup msm
+ * Structure definitions.
+ * These structures represent the parsed security manifest of a package.
+ */
+
+
+typedef struct file_x {
+ const char *path; /* file path */
+ ino_t ino; /* file inode */
+ struct file_x *prev;
+ struct file_x *next;
+} file_x;
+
+typedef struct filesystem_x {
+ const char *path; /* filesystem object absolute path */
+ const char *label; /* SMACK64 xattr */
+ const char *exec_label; /* SMACK64EXEC xattr */
+ const char *type; /* can be set as TRANSMUTABLE for directory */
+ struct filesystem_x *prev;
+ struct filesystem_x *next;
+ } filesystem_x;
+
+typedef struct ac_domain_x { /* structure for storing ac domain */
+ const char *name; /* ac domain name */
+ const char *match;
+ const char *type; /* ac domain policy type: "shared" or "restricted" or "NULL" (private) */
+ const char *plist; /* list of packages that allowed to request domain, if policy is "restricted" */
+ const char *pkg_name; /* package that defined ac domain */
+ struct ac_domain_x *prev;
+ struct ac_domain_x *next;
+ struct sw_source_x *sw_source; /* sw source of the package that defined the domain */
+ const char *origin;
+ UT_hash_handle hh;
+ int allowed;
+ struct ac_domain_x *older; /* previous version in upgrades */
+ struct ac_domain_x *newer; /* the newer upgraded version */
+} ac_domain_x;
+
+typedef struct annotation_x {
+ const char *name;
+ const char *value;
+} annotation_x;
+
+typedef struct member_x {
+ int type;
+ const char *name;
+ struct annotation_x *annotation;
+ struct member_x *prev;
+ struct member_x *next;
+} member_x;
+
+typedef struct interface_x {
+ const char *name;
+ struct annotation_x *annotation;
+ struct member_x *members;
+ struct interface_x *prev;
+ struct interface_x *next;
+} interface_x;
+
+typedef struct node_x {
+ const char *name;
+ struct annotation_x *annotation;
+ struct member_x *members;
+ struct interface_x *interfaces;
+ struct node_x *prev;
+ struct node_x *next;
+} node_x;
+
+typedef struct dbus_x {
+ const char *name;
+ const char *own;
+ const char *bus;
+ struct annotation_x *annotation;
+ struct node_x *nodes;
+ struct dbus_x *prev;
+ struct dbus_x *next;
+} dbus_x;
+
+typedef struct provide_x {
+ const char *name; /* _system_ or NULL */
+ struct ac_domain_x *ac_domains;
+ struct filesystem_x *filesystems;
+ struct dbus_x *dbuss;
+ const char *origin;
+ struct provide_x *prev;
+ struct provide_x *next;
+} provide_x;
+
+typedef struct request_x {
+ const char *ac_domain;
+} request_x;
+
+typedef struct keyinfo_x {
+ const unsigned char *keydata;
+ size_t keylen;
+ struct keyinfo_x *prev;
+ struct keyinfo_x *next;
+} keyinfo_x;
+
+typedef struct access_x {
+ const char *data;
+ const char *type;
+ struct access_x *prev;
+ struct access_x *next;
+} access_x;
+
+typedef struct origin_x {
+ const char *type;
+ struct keyinfo_x *keyinfos;
+ struct access_x *accesses;
+ struct origin_x *prev;
+ struct origin_x *next;
+} origin_x;
+
+typedef struct constraint_x {
+ const char *name;
+ const char *value;
+ struct constraint_x *prev;
+ struct constraint_x *next;
+} constraint_x;
+
+typedef struct d_request_x {
+ const char *label_name;
+ const char *ac_type;
+ struct d_request_x *prev;
+ struct d_request_x *next;
+} d_request_x;
+
+typedef struct d_permit_x {
+ const char *label_name;
+ const char *to_label_name;
+ const char *ac_type;
+ struct d_permit_x *prev;
+ struct d_permit_x *next;
+} d_permit_x;
+
+typedef struct d_provide_x {
+ const char *label_name;
+ struct d_provide_x *prev;
+ struct d_provide_x *next;
+} d_provide_x;
+
+typedef struct define_x {
+ const char *name; /* ac domain name */
+ const char *policy;
+ const char *plist; /* list of packages that are allowed to request the ac domain */
+ struct d_request_x *d_requests;
+ struct d_permit_x *d_permits;
+ struct d_provide_x *d_provides;
+} define_x;
+
+typedef struct package_x {
+ const char *name; /* package name */
+ struct sw_source_x *sw_source; /* package sw source */
+ struct provide_x *provides;
+ const char *modified; /* internal packages */
+ struct package_x *prev;
+ struct package_x *next;
+ UT_hash_handle hh;
+ struct package_x *older; /* previous version in upgrades */
+ struct package_x *newer; /* the newer upgraded version */
+} package_x;
+
+typedef struct sw_source_x {
+ const char *name;
+ const char *rankkey;
+ struct package_x *packages; /* config processing */
+ struct ac_domain_x *allowmatches; /* list of allow wildcards */
+ struct ac_domain_x *allows; /* hash of allowed ac domains */
+ struct ac_domain_x *denymatches; /* list of deny wildcards */
+ struct ac_domain_x *denys; /* hash of denied ac domains */
+ struct origin_x *origins;
+ struct sw_source_x *prev;
+ struct sw_source_x *next;
+ struct sw_source_x *parent;
+ struct sw_source_x *older; /* previous version in upgrades */
+ struct sw_source_x *newer; /* the newer upgraded version */
+} sw_source_x;
+
+typedef struct manifest_x { /*package manifest */
+ struct sw_source_x *sw_source; /* package sw source */
+ const char *name; /* package name */
+ struct provide_x *provides; /* assign section */
+ struct request_x *request; /* request section */
+ struct sw_source_x *sw_sources; /*defined software sources(non-NULL only for configuration manifests)*/
+ struct define_x *define; /* define section */
+ struct file_x *files; /* installed files */
+} manifest_x;
+
+/** \ingroup msm
+ * Frees the given pointer and sets it to NULL
+ * @param ptr address of pointer to be freed
+ * @return
+ */
+void msmFreePointer(void **ptr);
+
+/** \ingroup msm
+ * Process package security manifest.
+ * @param buffer xml data buffer
+ * @param size buffer length
+ * @param current sw source for package
+ * @param packagename name of the package
+ * @return pointer to structure on success
+ */
+manifest_x *msmProcessManifestXml(const char *buffer, int size, sw_source_x *current, const char *packagename);
+
+/** \ingroup msm
+ * Process device security policy file.
+ * @param filename file name
+ * @return pointer to structure on success
+ */
+manifest_x *msmProcessDevSecPolicyXml(const char *filename);
+
+/** \ingroup msm
+ * Free all structures reserved during manifest processing.
+ * @param mfx pointer to structure
+ */
+manifest_x* msmFreeManifestXml(manifest_x * mfx);
+
+/** \ingroup msm
+ * Go through all sw sources in manifest, import keys to RPM keyring.
+ * @param smack_accesses smack_accesses handle for setting smack rules
+ * @param mfx package manifest
+ * @param ts rpm transaction set
+ * @return 0 on success, else -1
+ */
+int msmSetupSWSources(struct smack_accesses *smack_accesses, manifest_x *mfx, rpmts ts);
+
+
+/** \ingroup msm
+ * Create package structure for package being installed.
+ * @param name package name
+ * @param sw_source package sw source
+ * @param provides provided ac domains
+ * @param modified for internal packages
+ * @return allocated and initialized package struct
+ */
+package_x *msmCreatePackage(const char *name, sw_source_x *sw_source, provide_x *provides, const char *modified);
+
+/** \ingroup msm
+ * Go through all provides in manifest, add provided ac domains to hash.
+ * @param packages pointer to packages list
+ * @param sw_source link to sw source in device security policy
+ * @param rule_set rule set for setting smack rules
+ * @return 0 on success, else -1
+ */
+int msmSetupPackages(struct smack_accesses *smack_accesses, package_x *packages, sw_source_x *sw_source);
+
+/** \ingroup msm
+ * Setup define section of manifest
+ * @param smack_accesses smack_accesses handle for setting smack rules
+ * @param mfx package manifest
+ * @return 0 on success, else -1
+ */
+int msmSetupDefine(struct smack_accesses *smack_accesses, manifest_x *mfx);
+
+/** \ingroup msm
+ * Setup smack rules according to the manifest
+ * @param smack_accesses smack_accesses handle for setting smack rules
+ * @param package_name package name
+ * @param flag flag to indicate installation or uninstallation
+ * @param SmackEnabled flag to indicate Smack presence in the kernel
+ * @return 0 on success, else -1
+ */
+int msmSetupSmackRules(struct smack_accesses *smack_accesses, const char* package_name, int flag, int SmackEnabled);
+
+/** \ingroup msm
+ * Check previous installation of package.
+ * @param name package name
+ * @return package or NULL
+ */
+package_x *msmCheckPackage(const char *name);
+
+/** \ingroup msm
+ * Cancel the installation of package (rules and config data).
+ * @param name package name
+ */
+void msmCancelPackage(const char *name);
+
+/** \ingroup msm
+ * Free package structure.
+ * @param package package
+ * @return next package in list or NULL
+ */
+package_x *msmFreePackage(package_x *package);
+
+/** \ingroup msm
+ * Set extended attributes of the file based on manifest.
+ * @param mfx package manifest
+ * @param filepath path of the file
+ * @param cookie magic cookie
+ * @return 0 on success, else -1
+ */
+int msmSetFileXAttributes(manifest_x *mfx, const char* filepath, magic_t cookie);
+
+/** \ingroup msm
+ * Set setup the request section of manifest.
+ * @param mfx package manifest
+ * @return 0 on success, else -1
+ */
+int msmSetupRequests(manifest_x *mfx);
+
+/** \ingroup msm
+ * Package is removed, remove all related Smack rules.
+ * @param mfx package manifest
+ * @param smack_accesses smack_accesses handle for setting smack rules
+ * @param SmackEnabled flag to indicate Smack presence in the kernel
+ */
+void msmRemoveRules(struct smack_accesses *smack_accesses, manifest_x *mfx, int SmackEnabled);
+
+/** \ingroup msm
+ * Setup DBus policies for package
+ * @param package package
+ */
+int msmSetupDBusPolicies(package_x *package);
+
+
+/** \ingroup msm
+ * Package is removed, remove related data in device security policy.
+ * @param mfx package manifest
+ */
+void msmRemoveConfig(manifest_x *mfx);
+
+/** \ingroup msm
+ * String compare which allows wildcards (* and ?) in s2.
+ * @param s1 string to compare
+ * @param s2 string to compare
+ * @return 0 if s1 matches s2
+ */
+int strwcmp(const char *s1, const char *s2);
+
+/** \ingroup msm
+ * Saves configuration into /etc/dev-sec-policy.
+ * @param mfx data to serialize
+ * @return RPMRC_OK or RPMRC_FAIL
+ */
+rpmRC msmSaveDeviceSecPolicyXml(manifest_x *root);
+
+/** \ingroup msm
+ * Depth first tree traversal for sw source tree.
+ * @param sw_sources sw source tree
+ * @param func function to call for each sw source until 0 is returned
+ * @param param parameter for the function
+ * @param param2 second parameter for the function
+ * @return matching sw source or NULL
+ */
+sw_source_x *msmSWSourceTreeTraversal(sw_source_x *sw_sources, int (func)(sw_source_x *, void *, void *), void *param, void* param2);
+
+/** \ingroup msm
+ * Free internal hashes.
+ */
+void msmFreeInternalHashes(void);
+
+/** \ingroup msm
+ * Query that requested package really owns the file.
+ * @param rfor request for 'package::/file/path'
+ * @param sw_sname sw source name, caller must free
+ * @param pname package name, caller must free
+ * @return pointer to the path part in rfor, or NULL if file
+ * is not owned by the specified package
+ */
+const char *msmQueryPackageFile(const char *rfor,
+ const char **sw_sname, const char **pname);
+
+
+#endif
diff --git a/plugins/msmconfig.c b/plugins/msmconfig.c
new file mode 100644
index 000000000..80f8ec492
--- /dev/null
+++ b/plugins/msmconfig.c
@@ -0,0 +1,265 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Ilhan Gurel <ilhan.gurel@nokia.com>
+ *
+ * Copyright (C) 2011 - 2013 Intel Corporation.
+ *
+ * Contact: Elena Reshetova <elena.reshetova@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <libxml/tree.h>
+
+#include "rpmio/rpmbase64.h"
+#include "rpmio/rpmlog.h"
+
+#include "msm.h"
+
+typedef enum credType_e {
+ CRED_ALLOWMATCHES = 0,
+ CRED_ALLOW = 1,
+ CRED_DENYMATCHES = 2,
+ CRED_DENY = 3,
+ CRED_PROVIDE = 4
+} credType;
+
+/**
+ * Serializes key data
+ * @todo Problem with getting keydata
+ * @param parent XML node
+ * @param keyinfo keyinfo structure
+ * @return none
+ */
+static void msmHandleKeyinfo(xmlNode *parent, keyinfo_x *keyinfo)
+{
+ char *enc = NULL;
+
+ if (!parent)
+ return;
+
+ while (keyinfo) {
+ xmlNode *node = xmlNewNode(NULL, BAD_CAST "keyinfo");
+
+ /* b64 encode keydata first */
+ if ((enc = rpmBase64Encode(keyinfo->keydata, keyinfo->keylen, -1)) != NULL) {
+ xmlAddChild(node, xmlNewText(BAD_CAST "\n"));
+ xmlAddChild(node, xmlNewText(BAD_CAST enc));
+ msmFreePointer((void**)&enc);
+ }
+
+ xmlAddChild(parent, node);
+ keyinfo = keyinfo->prev;
+ }
+}
+
+/**
+ * Serializes ac_domain data
+ * @param parent XML node
+ * @param type Type (allow, deny,..)
+ * @param ac_domain ac_domain structure
+ * @return none
+ */
+static void msmHandleACDomains(xmlNode *parent, credType type,
+ ac_domain_x *ac_domain)
+{
+ if (!ac_domain || !parent)
+ return;
+
+ xmlNode *node = NULL;
+
+ if ((type == CRED_ALLOWMATCHES) || (type == CRED_ALLOW)) {
+ node = xmlNewNode(NULL, BAD_CAST "allow");
+ } else if ((type == CRED_DENYMATCHES) || (type == CRED_DENY)) {
+ node = xmlNewNode(NULL, BAD_CAST "deny");
+ } else if (type == CRED_PROVIDE) {
+ node = parent;
+ } else {
+ return;
+ }
+
+ while (ac_domain) {
+ xmlNode *childnode = xmlNewNode(NULL, BAD_CAST "ac_domain");
+ if ((type == CRED_ALLOWMATCHES) || (type == CRED_DENYMATCHES)) {
+ xmlNewProp(childnode, BAD_CAST "match", BAD_CAST ac_domain->match);
+ } else {
+ xmlNewProp(childnode, BAD_CAST "name", BAD_CAST ac_domain->name);
+ if (ac_domain->type)
+ xmlNewProp(childnode, BAD_CAST "policy", BAD_CAST ac_domain->type);
+ if (ac_domain->plist)
+ xmlNewProp(childnode, BAD_CAST "plist", BAD_CAST ac_domain->plist);
+ }
+ xmlAddChild(node, childnode);
+ if (type == CRED_ALLOW || type == CRED_DENY)
+ ac_domain = ac_domain->hh.next;
+ else
+ ac_domain = ac_domain->prev;
+ }
+
+ if (type != CRED_PROVIDE)
+ xmlAddChild(parent, node);
+}
+
+/**
+ * Serializes origin data
+ * @param parent XML node
+ * @param origin origin structure
+ * @return none
+ */
+static void msmHandleOrigin(xmlNode *parent, origin_x *origin)
+{
+ if (!parent)
+ return;
+
+ while (origin) {
+ xmlNode *node = xmlNewNode(NULL, BAD_CAST "origin");
+ xmlAddChild(parent, node);
+ msmHandleKeyinfo(node, origin->keyinfos);
+ origin = origin->prev;
+ }
+}
+
+/**
+ * Serializes provides data
+ * @param parent XML node
+ * @param provide provide structure
+ * @return none
+ */
+static void msmHandleProvide(xmlNode *parent, provide_x *provide)
+{
+ if (!parent)
+ return;
+
+ while (provide) {
+ if (provide->ac_domains) {
+ xmlNode *node = xmlNewNode(NULL, BAD_CAST "provide");
+ xmlAddChild(parent, node);
+ msmHandleACDomains(node, CRED_PROVIDE, provide->ac_domains);
+ if (provide->origin) {
+ xmlNode *childnode = xmlNewNode(NULL, BAD_CAST "for");
+ xmlNewProp(childnode, BAD_CAST "origin", BAD_CAST provide->origin);
+ xmlAddChild(node, childnode);
+ }
+ }
+ provide = provide->prev;
+ }
+}
+
+/**
+ * Serializes packages data
+ * @param parent XML node
+ * @param package package structure
+ * @return none
+ */
+static void msmHandlePackage(xmlNode *parent, package_x *package)
+{
+ if (!parent)
+ return;
+
+ while (package) {
+ if (!package->newer) {
+ xmlNode *node = xmlNewNode(NULL, BAD_CAST "package");
+ xmlNewProp(node, BAD_CAST "name", BAD_CAST package->name);
+ if (package->modified)
+ xmlNewProp(node, BAD_CAST "modified", BAD_CAST package->modified);
+ xmlAddChild(parent, node);
+ msmHandleProvide(node, package->provides);
+ }
+ package = package->prev;
+ }
+}
+
+/**
+ * Serializes sw source data
+ * @param parent XML node
+ * @param sw_source sw_source structure
+ * @return none
+ */
+static void msmHandleSWSource(xmlNode *parent, sw_source_x *sw_source)
+{
+ #define MAX_DEPTH 10
+ xmlNode *node[MAX_DEPTH];
+ sw_source_x *temp;
+ int depth = 0;
+
+ if (!sw_source || !parent)
+ return;
+
+ node[0] = parent;
+
+ while (sw_source) {
+ depth = 1; /* recalculate depth */
+ for (temp = sw_source->parent; temp; temp = temp->parent) depth++;
+ if (!sw_source->newer && depth < MAX_DEPTH) {
+ node[depth] = xmlNewNode(NULL, BAD_CAST "sw_source");
+ xmlNewProp(node[depth], BAD_CAST "name", BAD_CAST sw_source->name);
+ xmlNewProp(node[depth], BAD_CAST "rankkey", BAD_CAST sw_source->rankkey);
+ xmlAddChild(node[depth-1], node[depth]);
+ msmHandleOrigin(node[depth], sw_source->origins);
+ msmHandleACDomains(node[depth], CRED_ALLOWMATCHES, sw_source->allowmatches);
+ msmHandleACDomains(node[depth], CRED_ALLOW, sw_source->allows);
+ msmHandleACDomains(node[depth], CRED_DENYMATCHES, sw_source->denymatches);
+ msmHandleACDomains(node[depth], CRED_DENY, sw_source->denys);
+ msmHandlePackage(node[depth], sw_source->packages);
+ if (sw_source->older) {
+ /* packages still belong to this sw_source */
+ msmHandlePackage(node[depth], sw_source->older->packages);
+ }
+ }
+ sw_source = sw_source->next;
+ }
+}
+
+/**
+ * Saves sw_source configuration into /etc/dev-sec-policy.
+ * @param mfx data to serialize
+ * @return RPMRC_OK or RPMRC_FAIL
+ */
+rpmRC msmSaveDeviceSecPolicyXml(manifest_x *mfx)
+{
+ FILE *outFile;
+ rpmRC rc = RPMRC_OK;
+
+ /* if data doesn't have sw_source information, no need to do anything */
+ if (mfx && mfx->sw_sources) {
+ sw_source_x *sw_source;
+ xmlDoc *doc = xmlNewDoc( BAD_CAST "1.0");
+ xmlNode *rootnode = xmlNewNode(NULL, BAD_CAST "config");
+ xmlDocSetRootElement(doc, rootnode);
+
+ LISTHEAD(mfx->sw_sources, sw_source);
+ msmHandleSWSource(rootnode, sw_source);
+
+ outFile = fopen(DEVICE_SECURITY_POLICY, "w");
+ if (outFile) {
+ xmlElemDump(outFile, doc, rootnode);
+ fclose(outFile);
+ } else {
+ rpmlog(RPMLOG_ERR, "Unable to write device security policy%s\n",
+ DEVICE_SECURITY_POLICY);
+ rc = RPMRC_FAIL;
+ }
+ xmlFreeDoc(doc);
+ xmlCleanupParser();
+ }
+
+ return rc;
+}
+
diff --git a/plugins/msmmanifest.c b/plugins/msmmanifest.c
new file mode 100644
index 000000000..ddba7daff
--- /dev/null
+++ b/plugins/msmmanifest.c
@@ -0,0 +1,1501 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tero Aho <ext-tero.aho@nokia.com>
+ *
+ * Copyright (C) 2011 -2013 Intel Corporation.
+ *
+ * Contact: Elena Reshetova <elena.reshetova@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <libxml/xmlreader.h>
+#include <sys/capability.h>
+
+#include "msm.h"
+
+#include "rpmio/rpmbase64.h"
+#include "rpmio/rpmlog.h"
+
+/* We'll support only the basic set of characters */
+#define ASCII(s) (const char *)s
+#define XMLCHAR(s) (const xmlChar *)s
+
+
+static int msmVerifyAccessType(const char* type)
+{
+ int res = 0, idx = 0;
+
+ if (type) {
+ if (strlen(type) > SMACK_ACCESS_TYPE_LENGHT) {
+ rpmlog(RPMLOG_ERR, "Lenght of the access type is bigger than allowed value: %s\n", type);
+ return -1;
+ }
+ while ( type[idx] != '\0' ){
+ if ((type[idx] !='a') && (type[idx]!='r') && (type[idx]!='w') &&
+ (type[idx]!='x') && (type[idx]!='t') && (type[idx] !='-')) {
+ rpmlog(RPMLOG_ERR, "Not allowed character in access type: %s\n", type);
+ res = -1;
+ break;
+ }
+ idx++;
+ }
+ } else return -1;
+ return res;
+}
+
+static int msmVerifySmackLabel(const char* label)
+{
+ int res = 0, idx = 0;
+
+ if (label) {
+ if (strlen(ASCII(label)) > SMACK_LABEL_LENGTH) { //smack limitation on lenght
+ rpmlog(RPMLOG_ERR, "Domain or label name %s lenght is longer than defined SMACK_LABEL_LENGTH\n", label);
+ return -1;
+ }
+ if (strlen(ASCII(label)) == 0){
+ rpmlog(RPMLOG_ERR, "An attempt to define an empty domain or label name\n");
+ return -1;
+ }
+ if (label[0] == '-') {
+ rpmlog(RPMLOG_ERR, "Dash is not allowed as first character in smack label: %s\n", label);
+ return -1;
+ }
+ while ( label[idx] != '\0' ){
+ if ((label[idx] =='\"') || (label[idx] =='\'') || (label[idx] =='/') ||
+ (label[idx] =='\\') || (label[idx] > '~') || (label[idx] <= ' ')) {
+ rpmlog(RPMLOG_ERR, "Not allowed character in smack label: %s, position: %d \n", label, idx);
+ res = -1;
+ break;
+ }
+ idx++;
+ }
+ } else return -1;
+
+ return res;
+}
+
+static int msmVerifyLabelPrefix(const char* sub_label, const char* domain_name)
+{
+ char *tmp = NULL;
+ char sep[]= "::";
+
+ tmp = calloc(strlen(domain_name) + 3, sizeof (const char));
+ if (!tmp)
+ return -1;
+
+ strncpy(tmp, domain_name, strlen(domain_name));
+ strncpy(tmp + strlen(domain_name), sep, 2);
+
+ if (strstr(ASCII(sub_label), tmp) != ASCII(sub_label)) { //sub label name should be prefixed by domain name and "::"
+ rpmlog(RPMLOG_ERR, "Label name %s isn't prefixed by domain name %s\n", ASCII(sub_label), domain_name);
+ msmFreePointer((void**)&tmp);
+ return -1;
+ }
+
+ msmFreePointer((void**)&tmp);
+ return 0;
+
+}
+
+static int msmNextChildElement(xmlTextReaderPtr reader, int depth)
+{
+ int ret = xmlTextReaderRead(reader);
+ int cur = xmlTextReaderDepth(reader);
+ while (ret == 1) {
+ /*
+ rpmlog(RPMLOG_DEBUG, "node %s %d\n",
+ ASCII(xmlTextReaderConstName(reader)),
+ xmlTextReaderDepth(reader));
+ */
+ switch (xmlTextReaderNodeType(reader)) {
+ case XML_READER_TYPE_ELEMENT:
+ case XML_READER_TYPE_TEXT:
+ if (cur == depth+1)
+ return 1;
+ break;
+ case XML_READER_TYPE_END_ELEMENT:
+ if (cur == depth)
+ return 0;
+ break;
+ default:
+ if (cur <= depth)
+ return 0;
+ break;
+ }
+ ret = xmlTextReaderRead(reader);
+ cur = xmlTextReaderDepth(reader);
+ }
+ return ret;
+}
+
+static ac_domain_x *msmFreeACDomain(ac_domain_x *ac_domain)
+{
+ if (ac_domain) {
+ ac_domain_x *prev = ac_domain->prev;
+ msmFreePointer((void**)&ac_domain->name);
+ msmFreePointer((void**)&ac_domain->type);
+ msmFreePointer((void**)&ac_domain->match);
+ msmFreePointer((void**)&ac_domain->plist);
+ msmFreePointer((void**)&ac_domain);
+ return prev;
+ } else return NULL;
+}
+
+static annotation_x *msmProcessAnnotation(xmlTextReaderPtr reader)
+{
+ const xmlChar *name, *value;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ value = xmlTextReaderGetAttribute(reader, XMLCHAR("value"));
+ rpmlog(RPMLOG_DEBUG, "annotation %s %s\n", ASCII(name), ASCII(value));
+
+ if (name && value) {
+ annotation_x *annotation = calloc(1, sizeof(annotation_x));
+ if (annotation) {
+ annotation->name = ASCII(name);
+ annotation->value = ASCII(value);
+ return annotation;
+ }
+ }
+ msmFreePointer((void**)&name);
+ msmFreePointer((void**)&value);
+ return NULL;
+}
+
+static int msmProcessMember(xmlTextReaderPtr reader, member_x *member)
+{
+ const xmlChar *node, *name;
+ int ret, depth;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "member %s\n", ASCII(name));
+ member->name = ASCII(name);
+
+ if (!name) return -1;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "annotation")) {
+ annotation_x *annotation = msmProcessAnnotation(reader);
+ if (annotation) {
+ member->annotation = annotation;
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return -1;
+ }
+ return ret;
+}
+
+static int msmProcessInterface(xmlTextReaderPtr reader, interface_x *interface)
+{
+ const xmlChar *node, *name;
+ int ret, depth;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "interface %s\n", ASCII(name));
+ interface->name = ASCII(name);
+
+ if (!name) return -1;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "method")) {
+ member_x *member = calloc(1, sizeof(member_x));
+ if (member) {
+ member->type = DBUS_METHOD;
+ ret = msmProcessMember(reader, member);
+ LISTADD(interface->members, member);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "signal")) {
+ member_x *member = calloc(1, sizeof(member_x));
+ if (member) {
+ member->type = DBUS_SIGNAL;
+ ret = msmProcessMember(reader, member);
+ LISTADD(interface->members, member);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "annotation")) {
+ annotation_x *annotation = msmProcessAnnotation(reader);
+ if (annotation) {
+ interface->annotation = annotation;
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return -1;
+ }
+ return ret;
+}
+
+static int msmProcessNode(xmlTextReaderPtr reader, node_x *nodex)
+{
+ const xmlChar *node, *name;
+ int ret, depth;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "node %s\n", ASCII(name));
+ nodex->name = ASCII(name);
+
+ if (!name) return -1;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "interface")) {
+ interface_x *interface = calloc(1, sizeof(interface_x));
+ if (interface) {
+ ret = msmProcessInterface(reader, interface);
+ LISTADD(nodex->interfaces, interface);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "method")) {
+ member_x *member = calloc(1, sizeof(member_x));
+ if (member) {
+ member->type = DBUS_METHOD;
+ ret = msmProcessMember(reader, member);
+ LISTADD(nodex->members, member);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "signal")) {
+ member_x *member = calloc(1, sizeof(member_x));
+ if (member) {
+ member->type = DBUS_SIGNAL;
+ ret = msmProcessMember(reader, member);
+ LISTADD(nodex->members, member);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "annotation")) {
+ annotation_x *annotation = msmProcessAnnotation(reader);
+ if (annotation) {
+ nodex->annotation = annotation;
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return -1;
+ }
+ return ret;
+}
+
+static int msmProcessDBus(xmlTextReaderPtr reader, dbus_x *dbus)
+{
+ const xmlChar *node, *name, *own, *bus;
+ int ret, depth;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ own = xmlTextReaderGetAttribute(reader, XMLCHAR("own"));
+ bus = xmlTextReaderGetAttribute(reader, XMLCHAR("bus"));
+ rpmlog(RPMLOG_DEBUG, "dbus %s %s %s\n", ASCII(name), ASCII(own), ASCII(bus));
+ dbus->name = ASCII(name);
+ dbus->own = ASCII(own);
+ dbus->bus = ASCII(bus);
+
+ if (!name || !bus) return -1;
+ if (strcmp(dbus->bus, "session") && strcmp(dbus->bus, "system"))
+ return -1;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "node")) {
+ node_x *nodex = calloc(1, sizeof(node_x));
+ if (nodex) {
+ ret = msmProcessNode(reader, nodex);
+ LISTADD(dbus->nodes, nodex);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "annotation")) {
+ annotation_x *annotation = msmProcessAnnotation(reader);
+ if (annotation) {
+ dbus->annotation = annotation;
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return -1;
+ }
+ return ret;
+}
+
+static ac_domain_x *msmProcessACDomain(xmlTextReaderPtr reader, sw_source_x *sw_source, const char* pkg_name)
+{
+ const xmlChar *name, *match, *policy, *plist;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ match = xmlTextReaderGetAttribute(reader, XMLCHAR("match"));
+ policy = xmlTextReaderGetAttribute(reader, XMLCHAR("policy"));
+ plist = xmlTextReaderGetAttribute(reader, XMLCHAR("plist"));
+ rpmlog(RPMLOG_DEBUG, "ac_domain %s match %s policy %s plist %s\n", ASCII(name), ASCII(match), ASCII(policy), ASCII(plist));
+
+ if (!((!name && !match) || (name && match))) {
+ ac_domain_x *ac_domain = calloc(1, sizeof(ac_domain_x));
+ if (ac_domain) {
+ ac_domain->name = ASCII(name);
+ ac_domain->match = ASCII(match);
+ ac_domain->type = ASCII(policy);
+ ac_domain->plist = ASCII(plist);
+ ac_domain->sw_source = sw_source;
+ ac_domain->pkg_name = pkg_name;
+ return ac_domain;
+ }
+ }
+ rpmlog(RPMLOG_ERR, "Mandatory argument is missing for ac domain definition\n");
+ rpmlog(RPMLOG_ERR, "ac_domain %s match %s policy %s plist %s\n", ASCII(name), ASCII(match), ASCII(policy), ASCII(plist));
+ msmFreePointer((void**)&name);
+ msmFreePointer((void**)&match);
+ msmFreePointer((void**)&policy);
+ msmFreePointer((void**)&plist);
+ return NULL;
+}
+
+static filesystem_x *msmProcessFilesystem(xmlTextReaderPtr reader)
+{
+ const xmlChar *path, *label, *type, *exec_label;
+
+ path = xmlTextReaderGetAttribute(reader, XMLCHAR("path"));
+ label = xmlTextReaderGetAttribute(reader, XMLCHAR("label"));
+ exec_label = xmlTextReaderGetAttribute(reader, XMLCHAR("exec_label"));
+ type = xmlTextReaderGetAttribute(reader, XMLCHAR("type"));
+
+ rpmlog(RPMLOG_DEBUG, "filesystem path %s label %s exec label %s type %s\n",
+ ASCII(path), ASCII(label), ASCII(exec_label), ASCII(type));
+
+ if (path && (label || exec_label)) {
+ if ((label) && (msmVerifySmackLabel(ASCII(label)) < 0)) {
+ goto fail;
+ }
+ if ((exec_label) && (msmVerifySmackLabel(ASCII(exec_label)) < 0)) {
+ goto fail;
+ }
+
+ filesystem_x *filesystem = calloc(1, sizeof(filesystem_x));
+ if (filesystem) {
+ filesystem->path = ASCII(path);
+ filesystem->label = ASCII(label);
+ filesystem->exec_label = ASCII(exec_label);
+ filesystem->type = ASCII(type);
+ return filesystem;
+ }
+
+ } else {
+ rpmlog(RPMLOG_ERR, "Mandatory argument is missing for filesystem assign request\n");
+ rpmlog(RPMLOG_ERR, "filesystem path %s label %s exec label %s\n",
+ ASCII(path), ASCII(label), ASCII(exec_label));
+ }
+
+fail:
+ msmFreePointer((void**)&path);
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&exec_label);
+ msmFreePointer((void**)&type);
+ return NULL;
+}
+
+static int msmProcessProvide(xmlTextReaderPtr reader, provide_x *provide, sw_source_x *current, manifest_x *mfx, const char* pkg_name)
+{
+ const xmlChar *node, *name, *origin;
+ int ret, depth;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "assign %s\n", ASCII(name));
+ provide->name = ASCII(name);
+
+ if (provide->name &&
+ (strcmp(provide->name, "_system_") || mfx->sw_source->parent))
+ return -1; /* only _system_ is accepted from root sw source */
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "dbus")) {
+ dbus_x *dbus = calloc(1, sizeof(dbus_x));
+ if (dbus) {
+ ret = msmProcessDBus(reader, dbus);
+ LISTADD(provide->dbuss, dbus);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "ac_domain")) {
+ ac_domain_x *ac_domain = msmProcessACDomain(reader, current, pkg_name);
+ if (ac_domain) {
+ const char *name = ac_domain->name;
+ LISTADD(provide->ac_domains, ac_domain);
+ if (!name) return -1;
+ if (mfx && !provide->name) {
+ ac_domain->name = malloc(strlen(mfx->name) + 2 +
+ strlen(name) + 1);
+ sprintf((char *)ac_domain->name, "%s::%s", mfx->name, name);
+ msmFreePointer((void**)&name);
+ }
+ } else return -1;
+
+ } else if (!strcmp(ASCII(node), "for")) {
+ origin = xmlTextReaderGetAttribute(reader, XMLCHAR("origin"));
+ rpmlog(RPMLOG_DEBUG, "for %s\n", ASCII(origin));
+ if (!origin) return -1;
+ if (provide->origin) {
+ msmFreePointer((void**)&origin);
+ return -1;
+ }
+ provide->origin = ASCII(origin);
+ if (strcmp(ASCII(origin), "trusted") &&
+ strcmp(ASCII(origin), "current") &&
+ strcmp(ASCII(origin), "all"))
+ return -1;
+
+ } else if (!strcmp(ASCII(node), "filesystem")) {
+ filesystem_x *filesystem = msmProcessFilesystem(reader);
+ if (filesystem) {
+ LISTADD(provide->filesystems, filesystem);
+ } else return -1;
+
+ } else {
+ rpmlog(RPMLOG_ERR, "No allowed element in assign section: %s\n", ASCII(node));
+ return -1;
+ }
+
+ if (ret < 0) return ret;
+ }
+
+ return ret;
+}
+
+static int msmProcessPackage(xmlTextReaderPtr reader, package_x *package, sw_source_x *current)
+{
+ const xmlChar *node, *name, *modified;
+ int ret, depth;
+
+ /* config processing */
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ modified = xmlTextReaderGetAttribute(reader, XMLCHAR("modified"));
+ rpmlog(RPMLOG_DEBUG, "package %s %s\n", name, modified);
+
+ package->name = ASCII(name);
+ package->modified = ASCII(modified);
+ package->sw_source = current;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "provide")) {
+ provide_x *provide = calloc(1, sizeof(provide_x));
+ if (provide) {
+ LISTADD(package->provides, provide);
+ ret = msmProcessProvide(reader, provide, current, NULL, package->name);
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmProcessRequest(xmlTextReaderPtr reader, request_x *request)
+{
+ const xmlChar *node, *name;
+ int ret, depth, requestPresent = 0;
+
+ rpmlog(RPMLOG_DEBUG, "request \n");
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "domain")) {
+ if (requestPresent) {
+ rpmlog(RPMLOG_ERR, "A second domain defined inside a request section. Abort package installation\n");
+ return -1;
+ }
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "ac domain name %s\n", ASCII(name));
+ if (name) {
+ request->ac_domain = ASCII(name);
+ requestPresent = 1;
+ } else {
+ rpmlog(RPMLOG_ERR, "No ac domain name defined in request.\n");
+ return -1;
+ }
+ } else {
+ rpmlog(RPMLOG_ERR, "Not allowed element in request section: %s\n", ASCII(node));
+ return -1;
+ }
+ }
+
+ return ret;
+}
+
+static int msmProcessDRequest(xmlTextReaderPtr reader, define_x *define)
+{
+ const xmlChar *node = NULL, *label = NULL, *type = NULL;
+ int ret, depth;
+
+ rpmlog(RPMLOG_DEBUG, "request\n");
+
+ if (!define->name) {
+ rpmlog(RPMLOG_ERR, "An attempt to define a domain without a name. Abort.\n");
+ return -1;
+ }
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "smack")) {
+ label = xmlTextReaderGetAttribute(reader, XMLCHAR("request"));
+ type = xmlTextReaderGetAttribute(reader, XMLCHAR("type"));
+ rpmlog(RPMLOG_DEBUG, "request label %s type %s\n", ASCII(label), ASCII(type));
+ if (label && type) {
+ if (msmVerifyAccessType(ASCII(type)) < 0) {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ if (msmVerifySmackLabel(ASCII(label)) < 0) {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ d_request_x *request = calloc(1, sizeof(d_request_x));
+ if (request) {
+ request->label_name = ASCII(label);
+ request->ac_type = ASCII(type);
+ LISTADD(define->d_requests, request);
+ } else {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+
+ } else {
+ rpmlog(RPMLOG_ERR, "One of the mandatory arguments for domain request is missing. Abort installation\n");
+ rpmlog(RPMLOG_ERR, "smack request label %s type %s\n", ASCII(label), ASCII(type));
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ } else {
+ rpmlog(RPMLOG_ERR, "Not allowed element in domain request section: %s\n", ASCII(node));
+ return -1;
+ }
+ if (ret < 0) return ret;
+ }
+
+ return ret;
+}
+
+static int msmProcessDPermit(xmlTextReaderPtr reader, define_x *define)
+{
+ const xmlChar *node, *label, *type, *to_label;
+ int ret, depth;
+
+ rpmlog(RPMLOG_DEBUG, "permit\n");
+
+ if (!define->name) {
+ rpmlog(RPMLOG_ERR, "An attempt to define a domain without a name. Abort.\n");
+ return -1;
+ }
+
+ depth = xmlTextReaderDepth(reader);
+
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "smack")) {
+ label = xmlTextReaderGetAttribute(reader, XMLCHAR("permit"));
+ to_label = xmlTextReaderGetAttribute(reader, XMLCHAR("to"));
+ type = xmlTextReaderGetAttribute(reader, XMLCHAR("type"));
+ rpmlog(RPMLOG_DEBUG, "permit %s to %s type %s\n", ASCII(label), ASCII(to_label), ASCII(type));
+
+ if (label && type) {
+ if (msmVerifyAccessType(ASCII(type)) < 0) {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&to_label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ if (msmVerifySmackLabel(ASCII(label)) < 0) {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&to_label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ if ((to_label) && (msmVerifyLabelPrefix(ASCII(to_label), define->name) < 0)) {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&to_label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ d_permit_x *permit = calloc(1, sizeof(d_permit_x));
+ if (permit) {
+ permit->label_name = ASCII(label);
+ permit->to_label_name = ASCII(to_label);
+ permit->ac_type = ASCII(type);
+ LISTADD(define->d_permits, permit);
+ } else {
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&to_label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+
+ } else {
+ rpmlog(RPMLOG_ERR, "One of the mandatory arguments for domain permit is missing. Abort installation\n");
+ rpmlog(RPMLOG_ERR, "smack permit label %s type %s\n", ASCII(label), ASCII(type));
+ msmFreePointer((void**)&label);
+ msmFreePointer((void**)&to_label);
+ msmFreePointer((void**)&type);
+ return -1;
+ }
+ } else {
+ rpmlog(RPMLOG_ERR, "Not allowed element in domain permit section: %s\n", ASCII(node));
+ return -1;
+ }
+ if (ret < 0) return ret;
+ }
+
+ return ret;
+}
+
+static int msmProcessDProvide(xmlTextReaderPtr reader, define_x *define)
+{
+ const xmlChar *node, *label;
+ int ret = 0, depth;
+
+ rpmlog(RPMLOG_DEBUG, "provide\n");
+
+ if (!define->name) {
+ rpmlog(RPMLOG_ERR, "An attempt to define a domain without a name. Abort.\n");
+ return -1;
+ }
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "label")) {
+ label = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rpmlog(RPMLOG_DEBUG, "label %s \n", ASCII(label));
+
+ if (label) {
+ if (msmVerifySmackLabel(ASCII(label)) < 0) {
+ msmFreePointer((void**)&label);
+ return -1;
+ }
+
+ if (msmVerifyLabelPrefix(ASCII(label), define->name) < 0) {
+ msmFreePointer((void**)&label);
+ return -1;
+ }
+
+ d_provide_x *provide = calloc(1, sizeof(d_provide_x));
+ if (provide) {
+ provide->label_name = ASCII(label);
+ LISTADD(define->d_provides, provide);
+ } else {
+ msmFreePointer((void**)&label);
+ return -1;
+ }
+
+ } else {
+ rpmlog(RPMLOG_INFO, "Label name is empty. Label provide is ignored\n");
+ continue;
+ }
+ } else {
+ rpmlog(RPMLOG_ERR, "Not allowed element in domain provide section: %s\n", ASCII(node));
+ return -1;
+ }
+ if (ret < 0) return ret;
+ }
+
+ return ret;
+}
+
+static int msmProcessDefine(xmlTextReaderPtr reader, define_x *define, manifest_x *mfx, sw_source_x *current)
+{
+ const xmlChar *node, *name, *policy, *plist;
+ int ret, depth, domainPresent = 0;
+
+ rpmlog(RPMLOG_DEBUG, "define\n");
+
+ depth = xmlTextReaderDepth(reader);
+
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "domain")) {
+ if (domainPresent) {
+ rpmlog(RPMLOG_ERR, "Only one domain is allowed per define section. Abort installation\n");
+ return -1;
+ }
+ domainPresent = 1;
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ policy = xmlTextReaderGetAttribute(reader, XMLCHAR("policy"));
+ plist = xmlTextReaderGetAttribute(reader, XMLCHAR("plist"));
+ rpmlog(RPMLOG_DEBUG, "domain %s policy %s plist %s\n",
+ ASCII(name), ASCII(policy), ASCII(plist));
+
+ if (name) {
+
+ if (msmVerifySmackLabel(ASCII(name)) < 0){
+ msmFreePointer((void**)&name);
+ msmFreePointer((void**)&policy);
+ msmFreePointer((void**)&plist);
+ return -1;
+ }
+
+ define->name = ASCII(name);
+ define->policy = ASCII(policy);
+ define->plist = ASCII(plist);
+
+ // store defined ac domain name
+ ac_domain_x *ac_domain = calloc(1, sizeof(ac_domain_x));
+ if (ac_domain) {
+ if (define->name) {
+ ac_domain->name = strdup(define->name);
+ }
+ ac_domain->match = strdup("trusted"); // hardcode trusted policy for ac domain definition
+ if (define->policy) {
+ ac_domain->type = strdup(define->policy);
+ }
+ if (define->plist) {
+ ac_domain->plist = strdup(define->plist);
+ }
+ ac_domain->sw_source = current;
+ ac_domain->pkg_name = mfx->name;
+ if (!mfx->provides){
+ provide_x *provide = calloc(1, sizeof(provide_x));
+ if (provide) {
+ LISTADD(mfx->provides, provide);
+ } else {
+ if (ac_domain) {
+ msmFreeACDomain(ac_domain);
+ return -1;
+ }
+ }
+ }
+ LISTADD(mfx->provides->ac_domains, ac_domain);
+ } else return -1;
+
+ } else {
+ rpmlog(RPMLOG_ERR, "Domain name must be defined. Abort installation\n");
+ msmFreePointer((void**)&policy);
+ msmFreePointer((void**)&plist);
+ return -1;
+ }
+ } else if (!strcmp(ASCII(node), "request")) {
+ int res = msmProcessDRequest(reader, define);
+ if (res < 0) return res;
+
+ } else if (!strcmp(ASCII(node), "permit")) {
+ int res = msmProcessDPermit(reader, define);
+ if (res < 0) return res;
+
+ } else if (!strcmp(ASCII(node), "provide")) {
+ int res = msmProcessDProvide(reader, define);
+ if (res < 0) return res;
+ } else {
+ rpmlog(RPMLOG_ERR, "Not allowed element in domain define section: %s\n", ASCII(node));
+ return -1;
+ }
+
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmProcessKeyinfo(xmlTextReaderPtr reader, origin_x *origin)
+{
+ const xmlChar *keydata;
+ keyinfo_x *keyinfo;
+ int ret, depth;
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ keydata = xmlTextReaderConstValue(reader);
+ rpmlog(RPMLOG_DEBUG, "keyinfo %.40s...\n", ASCII(keydata));
+ if (!keydata) return -1;
+ keyinfo = calloc(1, sizeof(keyinfo_x));
+ if (keyinfo) {
+ if ((ret = rpmBase64Decode(ASCII(keydata), (void **)&keyinfo->keydata, &keyinfo->keylen))) {
+ rpmlog(RPMLOG_ERR, "Failed to decode keyinfo %s, %d\n", keydata, ret);
+ ret = -1;
+ }
+ LISTADD(origin->keyinfos, keyinfo);
+ } else return -1;
+
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static access_x *msmProcessAccess(xmlTextReaderPtr reader, origin_x *origin)
+{
+ const xmlChar *data, *type;
+
+ data = xmlTextReaderGetAttribute(reader, XMLCHAR("data"));
+ type = xmlTextReaderGetAttribute(reader, XMLCHAR("type"));
+ rpmlog(RPMLOG_DEBUG, "access %s %s\n", ASCII(data), ASCII(type));
+
+ if (data) {
+ access_x *access = calloc(1, sizeof(access_x));
+ if (access) {
+ access->data = ASCII(data);
+ access->type = ASCII(type);
+ return access;
+ }
+ }
+ msmFreePointer((void**)&data);
+ msmFreePointer((void**)&type);
+ return NULL;
+}
+
+static int msmProcessOrigin(xmlTextReaderPtr reader, origin_x *origin)
+{
+ const xmlChar *node, *type;
+ int ret, depth;
+
+ type = xmlTextReaderGetAttribute(reader, XMLCHAR("type"));
+ rpmlog(RPMLOG_DEBUG, "origin %s\n", ASCII(type));
+ origin->type = ASCII(type);
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "keyinfo")) {
+ ret = msmProcessKeyinfo(reader, origin);
+ } else if (!strcmp(ASCII(node), "access")) {
+ access_x *access = msmProcessAccess(reader, origin);
+ if (access) {
+ LISTADD(origin->accesses, access);
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmProcessDeny(xmlTextReaderPtr reader, sw_source_x *sw_source)
+{
+ const xmlChar *node;
+ int ret, depth;
+
+ rpmlog(RPMLOG_DEBUG, "deny\n");
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "ac_domain")) {
+ ac_domain_x *ac_domain = msmProcessACDomain(reader, sw_source, NULL);
+ if (ac_domain) {
+ if (ac_domain->name) {
+ HASH_ADD_KEYPTR(hh, sw_source->denys, ac_domain->name,
+ strlen(ac_domain->name), ac_domain);
+ } else {
+ LISTADD(sw_source->denymatches, ac_domain);
+ }
+ } else return -1;
+ } else return -1;
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmProcessAllow(xmlTextReaderPtr reader, sw_source_x *sw_source)
+{
+ const xmlChar *node;
+ int ret, depth;
+
+ rpmlog(RPMLOG_DEBUG, "allow\n");
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "deny")) {
+ ret = msmProcessDeny(reader, sw_source);
+ } else if (!strcmp(ASCII(node), "ac_domain")) {
+ ac_domain_x *ac_domain = msmProcessACDomain(reader, sw_source, NULL);
+ if (ac_domain) {
+ if (ac_domain->name) {
+ HASH_ADD_KEYPTR(hh, sw_source->allows, ac_domain->name,
+ strlen(ac_domain->name), ac_domain);
+ } else {
+ LISTADD(sw_source->allowmatches, ac_domain);
+ }
+ } else return -1;
+ } else return -1;
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmFindSWSourceByName(sw_source_x *sw_source, void *param)
+{
+ const char *name = (const char *)param;
+ return strcmp(sw_source->name, name);
+}
+
+static int msmProcessSWSource(xmlTextReaderPtr reader, sw_source_x *sw_source, const char *parentkey, manifest_x *mfx)
+{
+ const xmlChar *name, *node, *rank, *rankkey;
+ sw_source_x *current;
+ int ret, depth, len;
+ int rankval = 0;
+
+ /* config processing */
+ current = sw_source;
+
+ name = xmlTextReaderGetAttribute(reader, XMLCHAR("name"));
+ rank = xmlTextReaderGetAttribute(reader, XMLCHAR("rank"));
+ rankkey = xmlTextReaderGetAttribute(reader, XMLCHAR("rankkey"));
+ rpmlog(RPMLOG_DEBUG, "sw source %s rank %s key %s\n",
+ ASCII(name), ASCII(rank), ASCII(rankkey));
+
+ sw_source->name = ASCII(name);
+
+ if (rankkey) {
+ /* config processing */
+ sw_source->rankkey = ASCII(rankkey);
+ } else {
+ if (rank) {
+ rankval = atoi(ASCII(rank));
+ msmFreePointer((void**)&rank); /* rankkey is used from now on */
+ }
+ }
+ if (!sw_source->name) return -1; /* sw source must have name */
+ if (!mfx && rankkey) return -1; /* manifest cannot set rankkey itself */
+
+ if (!mfx) {
+ sw_source_x *old = msmSWSourceTreeTraversal(sw_source->parent, msmFindSWSourceByName, (void *)sw_source->name, NULL);
+ if (old && old->parent != sw_source->parent) {
+ if (!old->parent && old == sw_source->parent) {
+ /* root sw source upgrade (it's signed by root) */
+ parentkey = "";
+ } else {
+ rpmlog(RPMLOG_ERR, "SW source called %s has already been installed\n",
+ sw_source->name);
+ return -1; /* sw_source names are unique (allow upgrade though) */
+ }
+ }
+ /* rank algorithm is copied from harmattan dpkg wrapper */
+ if (rankval > RANK_LIMIT) rankval = RANK_LIMIT;
+ if (rankval < -RANK_LIMIT) rankval = -RANK_LIMIT;
+ rankval += RANK_LIMIT;
+
+ len = strlen(parentkey) + 1 + 5 + 1 + 5 + 1 + strlen(sw_source->name) + 1;
+ if (!(sw_source->rankkey = malloc(len))) return -1;
+ sprintf((char *)sw_source->rankkey, "%s/%05d/%05d.%s",
+ parentkey, rankval, RANK_LIMIT, sw_source->name);
+ }
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "allow")) {
+ ret = msmProcessAllow(reader, sw_source);
+ } else if (!strcmp(ASCII(node), "deny")) {
+ ret = msmProcessDeny(reader, sw_source);
+ } else if (!strcmp(ASCII(node), "origin")) {
+ origin_x *origin = calloc(1, sizeof(origin_x));
+ if (origin) {
+ LISTADD(sw_source->origins, origin);
+ ret = msmProcessOrigin(reader, origin);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "package")) {
+ /* config processing */
+ if (!mfx) return -1;
+ package_x *package = calloc(1, sizeof(package_x));
+ if (package) {
+ LISTADD(sw_source->packages, package);
+ ret = msmProcessPackage(reader, package, current);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "sw_source")) {
+ /* config processing */
+ if (!mfx) return -1;
+ sw_source_x *sw_source = calloc(1, sizeof(sw_source_x));
+ if (sw_source) {
+ sw_source->parent = current;
+ LISTADD(mfx->sw_sources, sw_source);
+ } else return -1;
+ ret = msmProcessSWSource(reader, sw_source, "", mfx);
+ } else return -1;
+
+ if (ret < 0) return ret;
+ }
+ return ret;
+}
+
+static int msmProcessMsm(xmlTextReaderPtr reader, manifest_x *mfx, sw_source_x *current)
+{
+ const xmlChar *node;
+ int ret, depth;
+ int assignPresent = 0, requestPresent = 0, definePresent = 0; /* there must be only one section per manifest */
+ mfx->sw_source = current;
+
+ rpmlog(RPMLOG_DEBUG, "manifest\n");
+
+ depth = xmlTextReaderDepth(reader);
+ while ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "assign")) {
+ if (assignPresent) {
+ rpmlog(RPMLOG_ERR, "A second assign section in manifest isn't allowed. Abort installation.\n");
+ return -1;
+ }
+ assignPresent = 1;
+ provide_x *provide = calloc(1, sizeof(provide_x));
+ if (provide) {
+ LISTADD(mfx->provides, provide);
+ ret = msmProcessProvide(reader, provide, current, mfx, NULL);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "define")) {
+ if (definePresent) {
+ rpmlog(RPMLOG_ERR, "A second request section in manifest isn't allowed. Abort installation.\n");
+ return -1;
+ }
+ definePresent = 1;
+ mfx->define = calloc(1, sizeof(define_x));
+ if (mfx->define) {
+ ret = msmProcessDefine(reader, mfx->define, mfx, current);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "request")) {
+ if (requestPresent) {
+ rpmlog(RPMLOG_ERR, "A second request section in manifest isn't allowed. Abort installation.\n");
+ return -1;
+ }
+ requestPresent = 1;
+ mfx->request = calloc(1, sizeof(request_x));
+ if (mfx->request) {
+ ret = msmProcessRequest(reader, mfx->request);
+ } else return -1;
+ } else if (!strcmp(ASCII(node), "sw_source")) {
+ sw_source_x *sw_source = calloc(1, sizeof(sw_source_x));
+ if (sw_source) {
+ char parentkey[256] = { 0 };
+ sw_source->parent = current;
+ if (sw_source->parent) {
+ snprintf(parentkey, sizeof(parentkey),
+ "%s", sw_source->parent->rankkey);
+ char *sep = strrchr(parentkey, '/');
+ if (sep) *sep = '\0';
+ }
+ LISTADD(mfx->sw_sources, sw_source);
+ ret = msmProcessSWSource(reader, sw_source, parentkey, NULL);
+ } else return -1;
+ } else return -1;
+
+ if (ret < 0) return ret;
+ }
+
+ return ret;
+}
+
+static int msmProcessConfig(xmlTextReaderPtr reader, manifest_x *mfx)
+{
+ const xmlChar *node;
+ int ret, depth;
+
+ rpmlog(RPMLOG_DEBUG, "config\n");
+
+ depth = xmlTextReaderDepth(reader);
+ if ((ret = msmNextChildElement(reader, depth))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "sw_source")) {
+ mfx->sw_sources = calloc(1, sizeof(sw_source_x));
+ if (!mfx->sw_sources) return -1;
+ ret = msmProcessSWSource(reader, mfx->sw_sources, "", mfx);
+ } else return -1;
+ }
+ return ret;
+}
+
+static int msmProcessManifest(xmlTextReaderPtr reader, manifest_x *mfx, sw_source_x *current)
+{
+ const xmlChar *node;
+ int ret;
+
+ if ((ret = msmNextChildElement(reader, -1))) {
+ node = xmlTextReaderConstName(reader);
+ if (!node) return -1;
+
+ if (!strcmp(ASCII(node), "manifest")) {
+ ret = msmProcessMsm(reader, mfx, current);
+ } else if (!strcmp(ASCII(node), "config")) {
+ ret = msmProcessConfig(reader, mfx);
+ } else return -1;
+ }
+ return ret;
+}
+
+static filesystem_x *msmFreeFilesystem(filesystem_x *filesystem)
+{
+ if (filesystem) {
+ filesystem_x *prev = filesystem->prev;
+ msmFreePointer((void**)&filesystem->path);
+ msmFreePointer((void**)&filesystem->label);
+ msmFreePointer((void**)&filesystem->exec_label);
+ msmFreePointer((void**)&filesystem->type);
+ msmFreePointer((void**)&filesystem);
+ return prev;
+ } else
+ return NULL;
+
+}
+
+static member_x *msmFreeMember(member_x *member)
+{
+
+ if (member) {
+ member_x *prev = member->prev;
+ msmFreePointer((void**)&member->name);
+ if (member->annotation) {
+ msmFreePointer((void**)&member->annotation->name);
+ msmFreePointer((void**)&member->annotation->value);
+ msmFreePointer((void**)&member->annotation);
+ }
+ msmFreePointer((void**)&member);
+ return prev;
+ } else
+ return NULL;
+
+}
+
+static interface_x *msmFreeInterface(interface_x *interface)
+{
+
+ member_x *member;
+
+ if (interface) {
+ interface_x *prev = interface->prev;
+ msmFreePointer((void**)&interface->name);
+ if (interface->annotation) {
+ msmFreePointer((void**)&interface->annotation->name);
+ msmFreePointer((void**)&interface->annotation->value);
+ msmFreePointer((void**)&interface->annotation);
+ }
+ for (member = interface->members; member; member = msmFreeMember(member));
+ msmFreePointer((void**)&interface);
+ return prev;
+ } else
+ return NULL;
+
+}
+
+static node_x *msmFreeNode(node_x *node)
+{
+ member_x *member;
+ interface_x *interface;
+
+ if (node) {
+ node_x *prev = node->prev;
+ msmFreePointer((void**)&node->name);
+ if (node->annotation) {
+ msmFreePointer((void**)&node->annotation->name);
+ msmFreePointer((void**)&node->annotation->value);
+ msmFreePointer((void**)&node->annotation);
+ }
+ for (member = node->members; member; member = msmFreeMember(member));
+ for (interface = node->interfaces; interface; interface = msmFreeInterface(interface));
+ msmFreePointer((void**)&node);
+ return prev;
+ } else
+ return NULL;
+
+}
+
+static dbus_x *msmFreeDBus(dbus_x *dbus)
+{
+ node_x *node;
+
+ if (dbus) {
+ dbus_x *prev = dbus->prev;
+ msmFreePointer((void**)&dbus->name);
+ msmFreePointer((void**)&dbus->own);
+ msmFreePointer((void**)&dbus->bus);
+ if (dbus->annotation) {
+ msmFreePointer((void**)&dbus->annotation->name);
+ msmFreePointer((void**)&dbus->annotation->value);
+ msmFreePointer((void**)&dbus->annotation);
+ }
+ for (node = dbus->nodes; node; node = msmFreeNode(node));
+ msmFreePointer((void**)&dbus);
+ return prev;
+ } else return NULL;
+}
+
+static provide_x *msmFreeProvide(provide_x *provide)
+{
+ ac_domain_x *ac_domain;
+ filesystem_x *filesystem;
+ provide_x *prev = provide->prev;
+ dbus_x *dbus;
+
+ if (provide) {
+ for (ac_domain = provide->ac_domains; ac_domain; ac_domain = msmFreeACDomain(ac_domain));
+ if (provide->filesystems)
+ for (filesystem = provide->filesystems; filesystem; filesystem = msmFreeFilesystem(filesystem));
+ msmFreePointer((void**)&provide->name);
+ msmFreePointer((void**)&provide->origin);
+ for (dbus = provide->dbuss; dbus; dbus = msmFreeDBus(dbus));
+ msmFreePointer((void**)&provide);
+ }
+ return prev;
+}
+
+static file_x *msmFreeFile(file_x *file)
+{
+ file_x *prev = file->prev;
+ msmFreePointer((void**)&file->path);
+ msmFreePointer((void**)&file);
+ return prev;
+}
+
+package_x *msmFreePackage(package_x *package)
+{
+ provide_x *provide;
+ package_x *prev = package->prev;
+ for (provide = package->provides; provide; provide = msmFreeProvide(provide));
+ msmFreePointer((void**)&package->name);
+ msmFreePointer((void**)&package->modified);
+ msmFreePointer((void**)&package);
+ return prev;
+}
+
+static keyinfo_x *msmFreeKeyinfo(keyinfo_x *keyinfo)
+{
+ keyinfo_x *prev = keyinfo->prev;
+ msmFreePointer((void**)&keyinfo->keydata);
+ msmFreePointer((void**)&keyinfo);
+ return prev;
+}
+
+static access_x *msmFreeAccess(access_x *access)
+{
+ access_x *prev = access->prev;
+ msmFreePointer((void**)&access->data);
+ msmFreePointer((void**)&access->type);
+ msmFreePointer((void**)&access);
+ return prev;
+}
+
+static origin_x *msmFreeOrigin(origin_x *origin)
+{
+ keyinfo_x *keyinfo;
+ access_x *access;
+ origin_x *prev = origin->prev;
+ for (keyinfo = origin->keyinfos; keyinfo; keyinfo = msmFreeKeyinfo(keyinfo));
+ for (access = origin->accesses; access; access = msmFreeAccess(access));
+ msmFreePointer((void**)&origin->type);
+ msmFreePointer((void**)&origin);
+ return prev;
+}
+
+static sw_source_x *msmFreeSWSource(sw_source_x *sw_source)
+{
+ package_x *package;
+ ac_domain_x *ac_domain, *temp;
+ origin_x *origin;
+ sw_source_x *next = sw_source->next;
+
+ rpmlog(RPMLOG_DEBUG, "freeing sw source %s\n", sw_source->name);
+
+ for (package = sw_source->packages; package; package = msmFreePackage(package));
+ for (ac_domain = sw_source->allowmatches; ac_domain; ac_domain = msmFreeACDomain(ac_domain));
+ if (sw_source->allows) {
+ HASH_ITER(hh, sw_source->allows, ac_domain, temp) {
+ HASH_DELETE(hh, sw_source->allows, ac_domain);
+ msmFreeACDomain(ac_domain);
+ }
+ }
+
+ for (ac_domain = sw_source->denymatches; ac_domain; ac_domain = msmFreeACDomain(ac_domain));
+ if (sw_source->denys) {
+ HASH_ITER(hh, sw_source->denys, ac_domain, temp) {
+ HASH_DELETE(hh, sw_source->denys, ac_domain);
+ msmFreeACDomain(ac_domain);
+ }
+ }
+ for (origin = sw_source->origins; origin; origin = msmFreeOrigin(origin));
+ msmFreePointer((void**)&sw_source->name);
+ msmFreePointer((void**)&sw_source->rankkey);
+ msmFreePointer((void**)&sw_source);
+ return next;
+}
+
+static d_request_x *msmFreeDRequest(d_request_x *d_request)
+{
+ d_request_x *next = d_request->next;
+ rpmlog(RPMLOG_DEBUG, "freeing domain request %s\n", d_request->label_name);
+ msmFreePointer((void**)&d_request->label_name);
+ msmFreePointer((void**)&d_request->ac_type);
+ msmFreePointer((void**)&d_request);
+ return next;
+}
+
+static d_permit_x *msmFreeDPermit(d_permit_x *d_permit)
+{
+ d_permit_x *next = d_permit->next;
+ rpmlog(RPMLOG_DEBUG, "freeing domain permit %s\n", d_permit->label_name);
+ msmFreePointer((void**)&d_permit->label_name);
+ msmFreePointer((void**)&d_permit->to_label_name);
+ msmFreePointer((void**)&d_permit->ac_type);
+ msmFreePointer((void**)&d_permit);
+ return next;
+}
+
+static d_provide_x *msmFreeDProvide(d_provide_x *d_provide)
+{
+ d_provide_x *next = d_provide->next;
+ rpmlog(RPMLOG_DEBUG, "freeing domain provide %s\n", d_provide->label_name);
+ msmFreePointer((void**)&d_provide->label_name);
+ msmFreePointer((void**)&d_provide);
+ return next;
+}
+
+manifest_x* msmFreeManifestXml(manifest_x* mfx)
+{
+ provide_x *provide;
+ file_x *file;
+ sw_source_x *sw_source;
+ d_request_x *d_request;
+ d_permit_x *d_permit;
+ d_provide_x *d_provide;
+
+ rpmlog(RPMLOG_DEBUG, "in msmFreeManifestXml\n");
+ if (mfx) {
+ if (mfx->provides)
+ for (provide = mfx->provides; provide; provide = msmFreeProvide(provide));
+ rpmlog(RPMLOG_DEBUG, "after freeing provides\n");
+ if (mfx->request) {
+ msmFreePointer((void**)&mfx->request->ac_domain);
+ msmFreePointer((void**)&mfx->request);
+ }
+ rpmlog(RPMLOG_DEBUG, "after freeing requests\n");
+ for (file = mfx->files; file; file = msmFreeFile(file));
+ rpmlog(RPMLOG_DEBUG, "after freeing files\n");
+ if (mfx->sw_sources) {
+ LISTHEAD(mfx->sw_sources, sw_source);
+ for (; sw_source; sw_source = msmFreeSWSource(sw_source));
+ }
+ msmFreePointer((void**)&mfx->name);
+ rpmlog(RPMLOG_DEBUG, "after freeing name\n");
+ if (mfx->define) {
+ msmFreePointer((void**)&mfx->define->name);
+ msmFreePointer((void**)&mfx->define->policy);
+ msmFreePointer((void**)&mfx->define->plist);
+ if (mfx->define->d_requests) {
+ LISTHEAD(mfx->define->d_requests, d_request);
+ for (; d_request; d_request = msmFreeDRequest(d_request));
+ }
+ rpmlog(RPMLOG_DEBUG, "after freeing define requests\n");
+ if (mfx->define->d_permits) {
+ LISTHEAD(mfx->define->d_permits, d_permit);
+ for (; d_permit; d_permit = msmFreeDPermit(d_permit));
+ }
+ rpmlog(RPMLOG_DEBUG, "after freeing define permits\n");
+ if (mfx->define->d_provides) {
+ LISTHEAD(mfx->define->d_provides, d_provide);
+ for (; d_provide; d_provide = msmFreeDProvide(d_provide));
+ }
+ rpmlog(RPMLOG_DEBUG, "after freeing provides\n");
+ msmFreePointer((void**)&mfx->define);
+ }
+
+ rpmlog(RPMLOG_DEBUG, "after freeing defines \n");
+ msmFreePointer((void**)&mfx);
+ }
+ return mfx;
+}
+
+manifest_x *msmProcessManifestXml(const char *buffer, int size, sw_source_x *current, const char *packagename)
+{
+ xmlTextReaderPtr reader;
+ manifest_x *mfx = NULL;
+
+ reader = xmlReaderForMemory(buffer, size, NULL, NULL, 0);
+
+ if (reader) {
+ mfx = calloc(1, sizeof(manifest_x));
+ if (mfx) {
+ mfx->name = strdup(packagename);
+ if (msmProcessManifest(reader, mfx, current) < 0) {
+ /* error in parcing. Let's display some hint where we failed */
+ rpmlog(RPMLOG_DEBUG, "Syntax error in processing manifest in the above line\n");
+ mfx = msmFreeManifestXml(mfx);
+ }
+ }
+ xmlFreeTextReader(reader);
+ } else {
+ rpmlog(RPMLOG_ERR, "Unable to create xml reader\n");
+ }
+ return mfx;
+}
+
+manifest_x *msmProcessDevSecPolicyXml(const char *filename)
+{
+ xmlTextReaderPtr reader;
+ manifest_x *mfx = NULL;
+
+ reader = xmlReaderForFile(filename, NULL, 0);
+
+ if (reader) {
+ mfx = calloc(1, sizeof(manifest_x));
+ if (mfx) {
+ if (msmProcessManifest(reader, mfx, NULL) < 0) {
+ mfx = msmFreeManifestXml(mfx);
+ }
+ }
+ xmlFreeTextReader(reader);
+ } else {
+ rpmlog(RPMLOG_ERR, "Unable to open device security policy %s\n", filename);
+ }
+ return mfx;
+}
diff --git a/plugins/msmmatch.c b/plugins/msmmatch.c
new file mode 100644
index 000000000..0fcf8b939
--- /dev/null
+++ b/plugins/msmmatch.c
@@ -0,0 +1,71 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tero Aho <ext-tero.aho@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "msm.h"
+
+/* Wild card strcmp, wild cards * and ? allowed in s1 */
+int strwcmp(const char *s1, const char *s2)
+{
+ char *c1 = (char *)s1;
+ char *c2 = (char *)s2;
+ char *star = NULL;
+ int ok = 0;
+
+ if (!s1 || !s2) return 1;
+
+ while (*c2) {
+ if (*c1 == '*') {
+ if (star && (c1 - star) != ok)
+ goto fail;
+ c1++;
+ star = c1;
+ ok = 0;
+ }
+ if (*c1 == '?') {
+ c1++;
+ c2++;
+ continue;
+ }
+ if (*c1 == *c2) {
+ c1++;
+ c2++;
+ ok++;
+ } else if (star) {
+ c1 = star;
+ c2++;
+ ok = 0;
+ } else goto fail;
+ }
+ if (*c1 == '\0' && *c2 == '\0' && (!star || (c1 - star) == ok))
+ return 0;
+ fail:
+ return (*c1 < *c2 ? -1 : 1);
+}
+
diff --git a/plugins/msmxattr.c b/plugins/msmxattr.c
new file mode 100644
index 000000000..4db937ef1
--- /dev/null
+++ b/plugins/msmxattr.c
@@ -0,0 +1,1327 @@
+/*
+ * This file is part of MSM security plugin
+ * Greatly based on the code of MSSF security plugin
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tero Aho <ext-tero.aho@nokia.com>
+ *
+ * Copyright (C) 2011 - 2013 Intel Corporation.
+ *
+ * Contact: Elena Reshetova <elena.reshetova@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/capability.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <attr/xattr.h>
+#include <uthash.h>
+#include <magic.h>
+#include "rpmio/rpmlog.h"
+#include "rpm/rpmlib.h"
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmts.h>
+
+#include "msm.h"
+
+static ac_domain_x *all_ac_domains = NULL; /* hash of all provided ac domains */
+static package_x *allpackages = NULL; /* hash of all installed packages */
+
+void msmFreeInternalHashes(void)
+{
+ if (all_ac_domains) {
+ HASH_CLEAR(hh,all_ac_domains);
+ }
+
+ if (allpackages) {
+ HASH_CLEAR(hh,allpackages);
+ }
+}
+
+static int msmCheckACDomainRules(ac_domain_x *ac_domain,
+ sw_source_x *requested, sw_source_x *provided)
+{
+ sw_source_x *sw_source;
+
+ /* go through sw source and its parents: ac domains must not match */
+ /* deny or deny wildcards and must match allow or allow wildcards */
+ /* in the whole path up to the level of the providing sw source */
+
+ for (sw_source = requested; sw_source->parent && sw_source->parent != sw_source; sw_source = sw_source->parent) {
+ ac_domain_x *denied;
+ ac_domain_x *allowed;
+ /* check first if requested ac domain is denied */
+ HASH_FIND(hh, sw_source->denys, ac_domain->name, strlen(ac_domain->name), denied);
+ if (denied) return 0; /* matched deny */
+ for (denied = sw_source->denymatches; denied; denied = denied->prev)
+ if (!strwcmp(denied->match, ac_domain->name))
+ return 0; /* matched deny wildcard */
+
+ /* not denied, now check if it's in allows or allowmatches */
+ HASH_FIND(hh, sw_source->allows, ac_domain->name, strlen(ac_domain->name), allowed);
+ if (allowed) continue; /* matched allow */
+ for (allowed = sw_source->allowmatches; allowed; allowed = allowed->prev)
+ if (!strwcmp(allowed->match, ac_domain->name))
+ break; /* matched allow wildcard */
+ if (allowed) continue; /* matched allow wildcard */
+
+ if (strcmp(sw_source->rankkey, provided->rankkey) <= 0)
+ return 1; /* ranked higher (or same sw source), allow */
+ return 0; /* not mentioned, deny */
+ }
+ return 1; /* still here, allow for root sw source */
+}
+
+static int msmCheckLabelProvisioning(manifest_x *mfx, const char* label)
+{
+
+ d_provide_x *provide = NULL;
+
+ if ((mfx) && (label) && (mfx->define) && (mfx->define->d_provides)) {
+ for (provide = mfx->define->d_provides; provide; provide = provide->prev) {
+ if ( strcmp(provide->label_name, label) == 0 )
+ return 0;
+ }
+ }
+ rpmlog(RPMLOG_ERR, "Label %s hasn't been provided in the manifest\n", label);
+ return -1;
+}
+
+static int msmSetSmackRules(struct smack_accesses *smack_accesses, ac_domain_x *ac_domains, const char *aid)
+{
+ ac_domain_x *ac_domain;
+ int ret = 0;
+
+ if (!smack_accesses) return ret;
+
+ for (ac_domain = ac_domains; ac_domain; ac_domain = ac_domain->prev) {
+ if (ac_domain->allowed) {
+ ret = smack_accesses_add(smack_accesses, aid, ac_domain->name, "rw");
+ if (ret < 0) {
+ rpmlog(RPMLOG_ERR, "smack_add failed for %s %s\n",
+ aid, ac_domain->name);
+ return ret;
+ }
+ }/* else if (!ac_domain->allowed && !ac_domain->newer) {
+ // remove not allowed rule in case something has changed
+ smack_rule_set_remove(rule_set, aid, ac_domain->name, NULL);
+ }*/
+ }
+ return ret;
+
+}
+
+static int msmIsProvideAllowed(ac_domain_x *provided, sw_source_x *sw_source, const char *origin)
+{
+
+ /* first check provided ac_domain attributes */
+ if (provided->sw_source == sw_source) {
+ /* allowed always if ac_domain is provided in the same sw source */
+ return 1;
+ } else if (origin && !strcmp(origin, "current")) {
+ /* denied if ac_domain is only meant for current sw source */
+ return 0;
+ }
+ if (origin && !strcmp(origin, "all")) {
+ /* ac_domain is allowed for all sw sources */
+ return 1;
+ }
+ if (!origin || !strcmp(origin, "trusted")) {
+ if (strcmp(sw_source->rankkey, provided->sw_source->rankkey) < 0) {
+ /* higher ranked sw sources are allowed if ac_domain is trusted */
+ return 1;
+ } /* else flow through to check denys and allows below */
+ } else return 0;
+
+ return msmCheckACDomainRules(provided, sw_source, provided->sw_source);
+}
+
+static int msmSetSmackProvide(struct smack_accesses *smack_accesses, provide_x *provide, sw_source_x *sw_source)
+{
+ ac_domain_x *ac_domain;
+ sw_source_x *current = sw_source;
+ int ret = -1;
+
+ if (!provide || (!provide->ac_domains)) return 0;
+
+ /* set smack rules for all sw sources */
+ LISTHEAD(current, sw_source);
+ for (; sw_source; sw_source = sw_source->next) {
+ if (!sw_source->newer) {
+ for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev) {
+ ac_domain->allowed = msmIsProvideAllowed(ac_domain, sw_source, ac_domain->origin);
+ rpmlog(RPMLOG_INFO, "%s ac_domain %s provided in %s for %s\n", (ac_domain->allowed ? "allowing" : "not allowing"),
+ ac_domain->name, ac_domain->sw_source->name, sw_source->name);
+ }
+ if (smack_accesses)
+ ret = msmSetSmackRules(smack_accesses, provide->ac_domains, sw_source->name);
+ else
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int msmSetupZypperRepo(access_x *access, sw_source_x *sw_source)
+{
+ struct stat sb;
+ char path[FILENAME_MAX+1];
+ FILE *file = NULL;
+ char data[512];
+ int ret = -1;
+
+ /* NOTE: Creating zypper repos manually here! */
+ /* A library call would be the correct way, but calling c++ from c */
+ /* is not nice. On the other hand, now there is no libzypp dependency. */
+
+ char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
+ if (!sysconfdir || !strcmp(sysconfdir, "")) {
+ rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
+ goto exit;
+ }
+ snprintf(path, sizeof(path), "%s/zypp", sysconfdir);
+ if (stat(path, &sb) == -1) {
+ rpmlog(RPMLOG_ERR, "Failed to stat %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ snprintf(path, sizeof(path), "%s/zypp/repos.d", sysconfdir);
+ if (stat(path, &sb) == -1) {
+ if (mkdir(path, 0755) == -1) {
+ rpmlog(RPMLOG_ERR, "Failed to create %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ }
+ snprintf(path, sizeof(path), "%s/zypp/repos.d/%s.repo",
+ sysconfdir, sw_source->name);
+ file = fopen(path, "w");
+ if (!file) {
+ rpmlog(RPMLOG_ERR, "Failed to open %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ snprintf(data, sizeof(data),
+ "[%s]\n"
+ "name=%s\n"
+ "enabled=1\n"
+ "autorefresh=0\n"
+ "baseurl=%s\n"
+ "type=%s\n"
+ "keeppackages=0\n",
+ sw_source->name, sw_source->name, access->data,
+ (access->type ? access->type : "NONE"));
+
+ if (fputs(data, file) == EOF) {
+ rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ rpmlog(RPMLOG_INFO, "added zypper repository %s for sw source %s\n",
+ path, sw_source->name);
+
+ ret = 0;
+ exit:
+ if (file) fclose(file);
+ msmFreePointer((void**)&sysconfdir);
+
+ return ret;
+}
+
+static int msmSetSmackSWSource(struct smack_accesses *smack_accesses, sw_source_x *sw_source)
+{
+ package_x *package, *temp;
+ provide_x *provide;
+
+ if (!allpackages) return 0;
+
+ if (sw_source->older) {
+ ac_domain_x *ac_domain, *temp;
+ /* remove old domain rules in case of upgrade */
+ //smack_rule_set_remove_by_subject(rule_set, sw_source->name, NULL);
+ /* make sure domain's credentials point to upgraded domain */
+ HASH_ITER(hh, all_ac_domains, ac_domain, temp) {
+ if (ac_domain->sw_source == sw_source->older)
+ ac_domain->sw_source = sw_source;
+ }
+ }
+
+ /* iterate through all packages to create smack rules for the domain */
+ HASH_ITER(hh, allpackages, package, temp) {
+ if (sw_source->older) {
+ /* make sure domain's packages point to upgraded domain */
+ if (package->sw_source == sw_source->older)
+ package->sw_source = sw_source;
+ }
+ if (!package->newer) {
+ for (provide = package->provides; provide; provide = provide->prev) {
+ if (msmSetSmackProvide(smack_accesses, provide, package->sw_source))
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+int msmSetupSWSources(struct smack_accesses *smack_accesses, manifest_x *mfx, rpmts ts)
+{
+ sw_source_x *sw_source;
+ origin_x *origin;
+ keyinfo_x *keyinfo;
+ access_x *access;
+ ac_domain_x *allow;
+ ac_domain_x *deny;
+ ac_domain_x *ac_domain;
+ int ret;
+ rpmRC rc;
+
+ LISTHEAD(mfx->sw_sources, sw_source);
+
+ while (sw_source) {
+ sw_source_x *next = sw_source->next;
+ sw_source_x *parent = sw_source->parent;
+ if (ts) {
+ for (origin = sw_source->origins; origin; origin = origin->prev) {
+ for (keyinfo = origin->keyinfos; keyinfo; keyinfo = keyinfo->prev) {
+ rpmlog(RPMLOG_INFO, "setting keyinfo for sw source %s\n",
+ sw_source->name);
+ rc = rpmtsImportPubkey(ts, keyinfo->keydata, keyinfo->keylen);
+ if (rc != RPMRC_OK) {
+ rpmlog(RPMLOG_ERR, "Key import failed for sw source %s\n",
+ sw_source->name);
+ return rc;
+ }
+ }
+ for (access = origin->accesses; access; access = access->prev) {
+ rpmlog(RPMLOG_INFO, "setting access %s for sw source %s\n",
+ access->data, sw_source->name);
+ if (origin->type && !strcmp(origin->type, "ZYPPER")) {
+ ret = msmSetupZypperRepo(access, sw_source);
+ if (ret) {
+ rpmlog(RPMLOG_ERR,
+ "Failed to set access %s for sw source %s\n",
+ access->data, sw_source->name);
+ return ret;
+ }
+ }
+ }
+ }
+ } else {
+
+ /* config processing */
+ ret = msmSetupPackages(NULL, sw_source->packages, NULL);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "Setup packages failed for sw source %s\n",
+ sw_source->name);
+ return ret;
+ }
+ }
+ if (ts) {
+ for (allow = sw_source->allows; allow; allow = allow->hh.next) {
+ HASH_FIND(hh, all_ac_domains, allow->name, strlen(allow->name), ac_domain);
+ if (ac_domain) {
+ rpmlog(RPMLOG_INFO, "sw source %s allows access to ac domain %s\n",
+ sw_source->name, allow->name);
+ } else {
+ rpmlog(RPMLOG_WARNING, "sw source %s allows access to ac domain %s which doesn't exist\n",
+ sw_source->name, allow->name);
+ }
+ }
+ for (allow = sw_source->allowmatches; allow; allow = allow->prev)
+ rpmlog(RPMLOG_INFO, "sw source %s allows access to ac domain match %s\n",
+ sw_source->name, allow->match);
+
+ for (deny = sw_source->denys; deny; deny = deny->hh.next) {
+ HASH_FIND(hh, all_ac_domains, deny->name, strlen(deny->name), ac_domain);
+ if (ac_domain) {
+ rpmlog(RPMLOG_INFO, "sw source %s denies access to ac domain %s\n",
+ sw_source->name, deny->name);
+ } else {
+ rpmlog(RPMLOG_WARNING, "sw source %s denies access to ac domain %s which doesn't exist\n",
+ sw_source->name, deny->name);
+ }
+ }
+ for (deny = sw_source->denymatches; deny; deny = deny->prev)
+ rpmlog(RPMLOG_INFO, "sw source %s denies access to ac domain match %s\n",
+ sw_source->name, deny->match);
+
+ if (parent) {
+ if (strcmp(parent->name, sw_source->name)) {
+ sw_source_x *older;
+ for (older = parent; older; older = older->next) {
+ if (!strcmp(sw_source->name, older->name)) {
+ sw_source->older = older;
+ older->newer = sw_source;
+ break;
+ }
+ }
+ } else if (!parent->parent) {
+ /* root sw_source upgrade */
+ sw_source->older = parent;
+ parent->newer = sw_source;
+ sw_source->parent = NULL;
+ } else return -1;
+
+ LISTDEL(mfx->sw_sources, sw_source); /* take out from sw sources list */
+ NODEADD(parent, sw_source); /* add to sw source tree */
+ }
+
+ /* set smack rules for the new/upgraded sw source */
+ ret = msmSetSmackSWSource(smack_accesses, sw_source);
+ if (ret) {
+ rpmlog(RPMLOG_ERR, "Setting smack rules failed for sw source %s\n",
+ sw_source->name);
+ return ret;
+ }
+
+ }
+ sw_source = next;
+ }
+ return 0;
+}
+
+static void msmRemoveDBusConfig(package_x *package, dbus_x *dbuss)
+{
+ dbus_x *dbus;
+
+ for (dbus = dbuss; dbus; dbus = dbus->prev) {
+ char path[FILENAME_MAX+1];
+ snprintf(path, sizeof(path), "/etc/dbus-1/%s.d/manifest.%s.conf",
+ dbus->bus, package->name);
+ unlink(path);
+ }
+}
+
+static int msmSetupDBusRule(FILE *file, const char *creds, int type, const char *service, const char *name, const char *parentType, const char *parentValue)
+{
+ char data[1024];
+
+ if (creds && *creds) {
+ switch (type) {
+ case DBUS_SERVICE:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny send_destination=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow send_destination=\"%s\"/>\n"
+ " </policy>\n",
+ name, creds, name);
+ break;
+ case DBUS_PATH:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny send_destination=\"%s\" send_path=\"%s\"/>\n"
+ " <deny receive_sender=\"%s\" receive_path=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
+ " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
+ " </policy>\n",
+ service, name, service, name, creds,
+ service, name, service, name);
+ break;
+ case DBUS_INTERFACE:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny send_destination=\"%s\" send_interface=\"%s\"/>\n"
+ " <deny receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
+ " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
+ " </policy>\n",
+ service, name, service, name, creds,
+ service, name, service, name);
+ break;
+ case DBUS_METHOD:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
+ " </policy>\n",
+ service, parentType, parentValue, name, creds,
+ service, parentType, parentValue, name);
+ break;
+ case DBUS_SIGNAL:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
+ " </policy>\n",
+ service, parentType, parentValue, name, creds,
+ service, parentType, parentValue, name);
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ switch (type) {
+ case DBUS_SERVICE:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <allow send_destination=\"%s\"/>\n"
+ " </policy>\n",
+ name);
+ break;
+ case DBUS_PATH:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <allow send_destination=\"%s\" send_path=\"%s\"/>\n"
+ " <allow receive_sender=\"%s\" receive_path=\"%s\"/>\n"
+ " </policy>\n",
+ service, name, service, name);
+ break;
+ case DBUS_INTERFACE:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <allow send_destination=\"%s\" send_interface=\"%s\"/>\n"
+ " <allow receive_sender=\"%s\" receive_interface=\"%s\"/>\n"
+ " </policy>\n",
+ service, name, service, name);
+ break;
+ case DBUS_METHOD:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <allow send_destination=\"%s\" send_%s=\"%s\" send_member=\"%s\"/>\n"
+ " </policy>\n",
+ service, parentType, parentValue, name);
+ break;
+ case DBUS_SIGNAL:
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <allow receive_sender=\"%s\" receive_%s=\"%s\" receive_member=\"%s\"/>\n"
+ " </policy>\n",
+ service, parentType, parentValue, name);
+ break;
+ default:
+ return -1;
+ }
+ }
+ if (fputs(data, file) == EOF) {
+ rpmlog(RPMLOG_ERR, "Failed to write DBus rule %s: %s\n",
+ data, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int msmSetupDBusConfig(package_x *package, dbus_x *dbus, int phase)
+{
+ char path[FILENAME_MAX+1];
+ FILE *file = NULL;
+ char data[512];
+ node_x *node;
+ interface_x *interface;
+ member_x *member;
+ int ret = -1;
+
+ char *sysconfdir = rpmExpand("%{?_sysconfdir}", NULL);
+ if (!sysconfdir || !strcmp(sysconfdir, "")) {
+ rpmlog(RPMLOG_ERR, "Failed to expand %%_sysconfdir macro\n");
+ goto exit;
+ }
+ snprintf(path, sizeof(path), "%s/dbus-1/%s.d/manifest.%s.conf",
+ sysconfdir, dbus->bus, package->name);
+
+ file = fopen(path, phase ? "a" : "w");
+ if (!file) {
+ rpmlog(RPMLOG_ERR, "Cannot open %s: %s\n", path, strerror(errno));
+ goto exit;
+ }
+
+ if (phase == 0) {
+ snprintf(data, sizeof(data),
+ "<!-- This configuration is automatically generated from Manifest by RPM %s security plugin -->\n"
+ "<!DOCTYPE busconfig PUBLIC \"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd\">\n"
+ "<busconfig>\n",
+ rpmEVR);
+ if (fputs(data, file) == EOF) {
+ rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ }
+
+ if (phase >= 0) {
+ if (dbus->own) {
+ snprintf(data, sizeof(data),
+ " <policy context=\"default\">\n"
+ " <deny own=\"%s\"/>\n"
+ " </policy>\n"
+ " <policy smack=\"%s\">\n"
+ " <allow own=\"%s\"/>\n"
+ " </policy>\n",
+ dbus->name, dbus->own, dbus->name);
+ if (fputs(data, file) == EOF) {
+ rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ }
+ if (dbus->annotation) {
+ msmSetupDBusRule(file, dbus->annotation->value, DBUS_SERVICE,
+ NULL, dbus->name, NULL, NULL);
+ }
+ for (node = dbus->nodes; node; node = node->prev) {
+ if (node->annotation) {
+ msmSetupDBusRule(file, node->annotation->value, DBUS_PATH,
+ dbus->name, node->name, NULL, NULL);
+ }
+ for (member = node->members; member; member = member->prev) {
+ if (member->annotation) {
+ msmSetupDBusRule(file, member->annotation->value, member->type,
+ dbus->name, member->name,
+ "path", node->name);
+ }
+ }
+ for (interface = node->interfaces; interface; interface = interface->prev) {
+ if (interface->annotation) {
+ msmSetupDBusRule(file, interface->annotation->value, DBUS_INTERFACE,
+ dbus->name, interface->name, NULL, NULL);
+ }
+ for (member = interface->members; member; member = member->prev) {
+ if (member->annotation) {
+ msmSetupDBusRule(file, member->annotation->value, member->type,
+ dbus->name, member->name,
+ "interface", interface->name);
+ }
+ }
+ }
+ }
+ }
+
+ if (phase < 0) {
+ snprintf(data, sizeof(data), "</busconfig>\n");
+ if (fputs(data, file) == EOF) {
+ rpmlog(RPMLOG_ERR, "Failed to write %s: %s\n",
+ path, strerror(errno));
+ goto exit;
+ }
+ rpmlog(RPMLOG_INFO, "wrote dbus config %s\n", path);
+ }
+ ret = 0;
+
+ exit:
+ if (file) fclose(file);
+ if (ret) unlink(path);
+ msmFreePointer((void**)&sysconfdir);
+
+ return ret;
+}
+
+static int msmIsRequestAllowed(manifest_x *mfx, ac_domain_x *provided)
+{
+
+ if (mfx->sw_source == provided->sw_source) {
+ /* allowed always if ac domain is provided in the same sw source */
+ return 1;
+ } else if (provided->origin && !strcmp(provided->origin, "current")) {
+ /* denied if ac domain is only meant for current sw source */
+ return 0;
+ }
+ if (provided->origin && !strcmp(provided->origin, "all")) {
+ /* ac_domain is allowed for all sw sources */
+ return 1;
+ }
+ if (!provided->origin || !strcmp(provided->origin, "trusted")) {
+ if (strcmp(mfx->sw_source->rankkey, provided->sw_source->rankkey) < 0) {
+ /* higher ranked sw sources are allowed if ac domain is trusted */
+ return 1;
+ } /* else flow through to check denys and allows below */
+ } else return 0;
+
+ return msmCheckACDomainRules(provided, mfx->sw_source, provided->sw_source);
+}
+
+static int msmCheckDomainJoinPossibility(manifest_x *mfx, ac_domain_x *defined_ac_domain)
+{
+
+ char *tmp = NULL, *pch = NULL;
+ unsigned int found = 0;
+
+ if ((!mfx) || (!defined_ac_domain))
+ return -1;
+
+ if (defined_ac_domain->type) {
+ if (strcmp(defined_ac_domain->type, "restricted") == 0) {
+ if (defined_ac_domain->plist) {
+ tmp = calloc(strlen(defined_ac_domain->plist) + 1, sizeof(char));
+ if (!tmp) return -1;
+ strncpy(tmp, defined_ac_domain->plist, strlen(defined_ac_domain->plist));
+ pch = strtok (tmp, ", ");
+ while (pch != NULL)
+ {
+ if (strcmp(pch, mfx->name) == 0) {
+ found = 1;
+ break;
+ }
+ pch = strtok(NULL, ", ");
+ }
+ msmFreePointer((void**)&tmp);
+ }
+ if (found != 1) {
+ rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
+ rpmlog(RPMLOG_ERR, "because ac domain is marked as restricted\n");
+ return -1;
+ }
+ } else if (strcmp(defined_ac_domain->type, "shared") == 0) {
+ return 0;
+ } else {
+ // domain hasn't been marked as shared
+ rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
+ rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
+ return -1;
+ }
+ } else {
+ // by default ac domains are private
+ rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed ", mfx->request->ac_domain);
+ rpmlog(RPMLOG_ERR, "because ac domain is marked as private\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int msmSetupRequests(manifest_x *mfx)
+{
+
+ ac_domain_x *defined_ac_domain = NULL;
+
+ if ((!mfx) || (!mfx->request) || (!mfx->request->ac_domain))
+ return -1;
+
+ HASH_FIND(hh, all_ac_domains, mfx->request->ac_domain, strlen(mfx->request->ac_domain), defined_ac_domain);
+ if (!defined_ac_domain){ // request for a undefined domain.
+ rpmlog(RPMLOG_ERR, "Request for a domain name %s that hasn't been yet defined by any package\n", mfx->request->ac_domain);
+ return -1;
+ }
+
+ //now check that the package can join the requested AC domain
+
+ if (mfx->define){
+ rpmlog(RPMLOG_DEBUG, "mfx->define->name %s mfx->request->ac_domain %s\n", mfx->define->name, mfx->request->ac_domain);
+ if (strcmp(mfx->define->name, mfx->request->ac_domain) == 0)
+ //ac domain is requested from the same package where it was define. This case is always allowed
+ return 0;
+ }
+
+ //need to check if developer allowed other packages to join this domain
+ if (msmCheckDomainJoinPossibility(mfx, defined_ac_domain) < 0 )
+ return -1;
+
+ // now checking if security policy allows to join this domain
+ if (msmIsRequestAllowed(mfx, defined_ac_domain)) {
+ rpmlog(RPMLOG_INFO, "Request for a domain name %s is allowed based on package sw source\n", mfx->request->ac_domain);
+ return 0;
+
+ } else {
+ rpmlog(RPMLOG_ERR, "Request for a domain name %s isn't allowed based on package sw source\n", mfx->request->ac_domain);
+ return -1;
+ }
+}
+
+static int msmSetupProvides(struct smack_accesses *smack_accesses, package_x *package)
+{
+ provide_x *provide;
+ ac_domain_x *ac_domain;
+
+ for (provide = package->provides; provide; provide = provide->prev) {
+ for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev) {
+ ac_domain_x *current_d = NULL;
+ ac_domain->origin = provide->origin;
+
+ HASH_FIND(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), current_d);
+
+ if (current_d) { /* ac domain has been previously defined */
+
+ if (strcmp(ac_domain->pkg_name, current_d->pkg_name) == 0) { /* check that it was provided by same package */
+ HASH_DELETE(hh, all_ac_domains, current_d);
+ HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
+ current_d->newer = ac_domain;
+ ac_domain->older = current_d;
+ rpmlog(RPMLOG_INFO, "package %s upgraded ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
+
+ } else {
+ rpmlog(RPMLOG_ERR, "package %s can't upgrade ac domain %s previously defined in package %s\n",
+ ac_domain->pkg_name, ac_domain->name, current_d->pkg_name);
+ return -1;
+ }
+ } else {
+ HASH_ADD_KEYPTR(hh, all_ac_domains, ac_domain->name, strlen(ac_domain->name), ac_domain);
+ rpmlog(RPMLOG_INFO, "package %s defined ac domain %s\n", ac_domain->pkg_name, ac_domain->name);
+ }
+ }
+ int ret = msmSetSmackProvide(smack_accesses, provide, package->sw_source);
+
+ if (ret < 0) {
+ rpmlog(RPMLOG_ERR, "Failed to set smack rules for provide\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int msmSetupDBusPolicies(package_x *package)
+{
+
+ dbus_x *session = NULL;
+ dbus_x *system = NULL;
+ provide_x *provide;
+ dbus_x *dbus;
+
+ for (provide = package->provides; provide; provide = provide->prev) {
+ for (dbus = provide->dbuss; dbus; dbus = dbus->prev) {
+ if (!strcmp(dbus->bus, "session")) {
+ msmSetupDBusConfig(package, dbus, session ? 1 : 0);
+ session = dbus;
+ } else if (!strcmp(dbus->bus, "system")) {
+ msmSetupDBusConfig(package, dbus, system ? 1 : 0);
+ system = dbus;
+ } else return -1;
+ }
+ if (session) msmSetupDBusConfig(package, session, -1);
+ if (system) msmSetupDBusConfig(package, system, -1);
+ session = system = NULL;
+ }
+ return 0;
+
+}
+
+static int msmCheckDomainRequestOrPermit(manifest_x *mfx, const char* domain)
+{
+
+ ac_domain_x *defined_ac_domain = NULL;
+ char* name = NULL;
+
+ if ((!mfx) || (!domain))
+ return -1;
+
+ name = calloc(strlen(domain) + 1, sizeof(char));
+ if (!name) return -1;
+ strncpy(name, domain, strlen(domain));
+ strtok(name, ":"); // remove label name if present
+ rpmlog(RPMLOG_DEBUG, "label name %s domain name %s \n", domain, name);
+
+ HASH_FIND(hh, all_ac_domains, name, strlen(name), defined_ac_domain);
+ if (!defined_ac_domain) { // request or permit for an undefined domain.
+ rpmlog(RPMLOG_ERR, "A domain name %s hasn't been yet defined by any package. Can't verify if it is allowed\n", name);
+ msmFreePointer((void**)&name);
+ return -1;
+ }
+
+ //now check that this ac_domain can be requested
+
+ if ((mfx->define) && (mfx->define->name)) {
+ rpmlog(RPMLOG_DEBUG, "mfx->define->name %s domain %s\n", mfx->define->name, name);
+ if (strcmp(mfx->define->name, name) == 0) {
+ // AC domain access is requested or permitted from the same package where it was defined.
+ // This case is always allowed
+ msmFreePointer((void**)&name);
+ return 0;
+ }
+ }
+
+ // no need to check if developer allowed other packages to request/permit this domain
+ // because this isn't a request to belong to a domain, but request/permit for domain access
+
+ if (msmIsRequestAllowed(mfx, defined_ac_domain)) {
+ // request or permit is allowed by domain policy
+ rpmlog(RPMLOG_DEBUG, "Request/Permit to access a domain name %s is allowed based on package sw source\n", name);
+ msmFreePointer((void**)&name);
+ return 0;
+
+ } else {
+ rpmlog(RPMLOG_ERR, "Request/Permit to access a domain name %s isn't allowed based on package sw source\n", name);
+ msmFreePointer((void**)&name);
+ return -1;
+ }
+}
+
+int msmSetupDefine(struct smack_accesses *smack_accesses, manifest_x *mfx)
+{
+ d_request_x *d_request;
+ d_permit_x *d_permit;
+ ac_domain_x * defined_ac_domain = NULL;
+ int ret;
+
+ if ( (!mfx) || (!mfx->define) || (!mfx->define->name)) {
+ rpmlog(RPMLOG_ERR, "Failed to setup define with empty name\n");
+ return -1;
+ }
+
+ /* need to check if domain hasn't been already defined by other package */
+
+ HASH_FIND(hh, all_ac_domains, mfx->define->name, strlen(mfx->define->name), defined_ac_domain);
+ if ((defined_ac_domain) && (defined_ac_domain->pkg_name)) { // this domain has been previously defined
+ if (strcmp(defined_ac_domain->pkg_name, mfx->name) != 0) {
+ rpmlog(RPMLOG_ERR, "Attempt to define a domain name %s that has been already defined by package %s\n",
+ mfx->define->name, defined_ac_domain->pkg_name);
+ return -1;
+ }
+
+ }
+
+ if (mfx->define->d_requests) {
+ for (d_request = mfx->define->d_requests; d_request; d_request = d_request->prev) {
+ // first check if the current's package sw source can grant access to requested domain
+ if ( msmCheckDomainRequestOrPermit(mfx, d_request->label_name) < 0 )
+ return -1;
+ if ( smack_accesses_add(smack_accesses, mfx->define->name, d_request->label_name, d_request->ac_type) < 0 ) {
+ rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain requests\n");
+ return -1;
+ }
+
+ }
+ }
+
+ if (mfx->define->d_permits) {
+ for (d_permit = mfx->define->d_permits; d_permit; d_permit = d_permit->prev) {
+ // first check if the current's package sw source can grant access to permited domain
+ if ( msmCheckDomainRequestOrPermit(mfx, d_permit->label_name) < 0 )
+ return -1;
+ if (!d_permit->to_label_name)
+ ret = smack_accesses_add(smack_accesses, d_permit->label_name, mfx->define->name, d_permit->ac_type);
+ else {
+ if ( msmCheckLabelProvisioning(mfx, d_permit->to_label_name) < 0 )
+ return -1;
+ ret = smack_accesses_add(smack_accesses, d_permit->label_name, d_permit->to_label_name, d_permit->ac_type);
+ }
+ if (ret < 0) {
+ rpmlog(RPMLOG_ERR, "Failed to set smack rules for domain permits\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+package_x *msmCreatePackage(const char *name, sw_source_x *sw_source, provide_x *provides, const char *modified)
+{
+ if (!name) return NULL;
+
+ package_x *package = calloc(1, sizeof(package_x));
+ if (package) {
+ package->name = strdup(name);
+ if (!package->name) goto exit;
+ package->sw_source = sw_source;
+ package->provides = provides;
+ if (modified) {
+ package->modified = strdup(modified);
+ if (!package->modified) goto exit;
+ }
+ }
+ return package;
+
+ exit:
+ msmFreePointer((void**)&package->name);
+ msmFreePointer((void**)&package->modified);
+ msmFreePointer((void**)&package);
+
+ return NULL;
+}
+
+int msmSetupSmackRules(struct smack_accesses *smack_accesses, const char* package_name, int flag, int SmackEnabled)
+{
+ int ret = 0;
+ char * buffer = calloc(strlen(SMACK_RULES_PATH) + strlen(package_name) + 1, sizeof(char));
+ if (!buffer) return -1;
+ strncpy(buffer, SMACK_RULES_PATH, strlen(SMACK_RULES_PATH));
+ strncpy(buffer + strlen(SMACK_RULES_PATH), package_name, strlen(package_name));
+ rpmlog(RPMLOG_DEBUG, "smack rule file path %s, SmackEnabled %d\n", buffer, SmackEnabled);
+
+ if (flag == SMACK_UNINSTALL) { /* uninstallation case */
+ FILE* fd = fopen(buffer, "r");
+ if (fd) {
+ rpmlog(RPMLOG_DEBUG, "uninstall case \n");
+ struct smack_accesses *old_rule_set = NULL;
+ ret = smack_accesses_new(&old_rule_set);
+ if (ret != 0) return -1;
+ ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
+ if (ret == 0) {
+ if (SmackEnabled == 1)
+ ret = smack_accesses_clear(old_rule_set); /* deletes rules from kernel */
+
+ }
+ smack_accesses_free(old_rule_set);
+ fclose(fd);
+ remove(buffer); /* delete rules file from system */
+ }
+ } else { /*installation case */
+ /* first attempt to clean previous version of rules, if exists */
+ FILE* fd = fopen(buffer, "r");
+ if (fd) {
+ struct smack_accesses *old_rule_set = NULL;
+ ret = smack_accesses_new(&old_rule_set);
+ if (ret != 0) return -1;
+ ret = smack_accesses_add_from_file(old_rule_set, fileno(fd));
+ if (ret == 0) {
+ if (SmackEnabled == 1)
+ ret = smack_accesses_clear(old_rule_set); /* deletes old rules from kernel */
+ }
+ fclose(fd);
+ smack_accesses_free(old_rule_set);
+ }
+ /* now write new rules to the system */
+ fd = fopen(buffer, "w");
+ if (!fd) {
+ rpmlog(RPMLOG_ERR, "Can't write smack rules\n");
+ return -1;
+ }
+ ret = smack_accesses_save(smack_accesses, fileno(fd));
+ rpmlog(RPMLOG_DEBUG, "ret in installation %d\n", ret);
+ if (!ret) {
+ if (SmackEnabled == 1)
+ ret = smack_accesses_apply(smack_accesses);
+ }
+ fclose(fd);
+ }
+
+ free(buffer);
+ if (ret)
+ return -1;
+ return 0;
+
+}
+
+int msmSetupPackages(struct smack_accesses *smack_accesses, package_x *packages, sw_source_x *sw_source)
+{
+ package_x *package, *first = NULL;
+ char *p_rankkey, *c_rankkey;
+ for (package = packages; package; package = package->prev) {
+ package_x *current_p;
+ rpmlog(RPMLOG_DEBUG, "before HASH_FIND, package->name %s\n", package->name);
+ HASH_FIND(hh, allpackages, package->name, strlen(package->name), current_p);
+ rpmlog(RPMLOG_DEBUG, "after HASH_FIND\n");
+ if (current_p) {
+ if (!current_p->sw_source) {
+ return -1;
+ }
+ p_rankkey = strdup(package->sw_source->rankkey);
+ c_rankkey = strdup(current_p->sw_source->rankkey);
+ p_rankkey = strtok(p_rankkey, ".");
+ c_rankkey = strtok(c_rankkey, ".");
+ /* this is an upgrade, remove old one from config */
+ if ((strcmp(p_rankkey, c_rankkey) < 0) ||
+ (strcmp(package->sw_source->name, current_p->sw_source->name) == 0)) {
+ HASH_DELETE(hh, allpackages, current_p);
+ rpmlog(RPMLOG_INFO, "sw source %s upgraded package %s previously provided in sw source %s\n",
+ package->sw_source->name, package->name, current_p->sw_source->name);
+ current_p->newer = package;
+ package->older = current_p;
+ } else {
+ /* upgrade from lower or similary ranked sw source is not allowed */
+ rpmlog(RPMLOG_ERR, "sw source %s tried to upgrade package %s previously provided in sw source %s\n",
+ package->sw_source->name, package->name, current_p->sw_source->name);
+ return -1;
+ }
+ msmFreePointer((void**)&p_rankkey);
+ msmFreePointer((void**)&c_rankkey);
+ } else {
+ if (sw_source) {
+ rpmlog(RPMLOG_INFO, "sw source %s provided package %s\n", package->sw_source->name, package->name);
+ }
+ }
+ rpmlog(RPMLOG_DEBUG, "before HASH_ADD_KEYPTR\n");
+ HASH_ADD_KEYPTR(hh, allpackages, package->name, strlen(package->name), package);
+ /* set sw source smack rules*/
+ if ((msmSetupProvides(smack_accesses, package)) < 0 ) {
+ msmCancelPackage(package->name);
+ return -1;
+ }
+ first = package;
+ }
+ if (sw_source && packages) {
+ /* catenate list to sw_source config */
+ LISTCAT(sw_source->packages, first, packages);
+ }
+ return 0;
+}
+
+package_x *msmCheckPackage(const char *name)
+{
+ package_x *package = NULL;
+
+ if (name)
+ HASH_FIND(hh, allpackages, name, strlen(name), package);
+
+ return package;
+}
+
+static void msmCancelACDomain(const char *name)
+{
+ if (name) {
+ ac_domain_x *domain;
+ HASH_FIND(hh, all_ac_domains, name, strlen(name), domain);
+ if (domain) {
+ HASH_DELETE(hh, all_ac_domains, domain);
+ if (domain->older) {
+ /* resume previous version */
+ HASH_ADD_KEYPTR(hh, all_ac_domains, domain->older->name, strlen(domain->older->name), domain->older);
+ domain->older->older = domain->older->newer;
+ domain->older->newer = NULL;
+ domain->newer = domain->older;
+ domain->older = NULL;
+ } else {
+ /* no previous, just take this one out */
+ domain->newer = domain;
+ }
+ }
+ }
+}
+
+void msmCancelPackage(const char *name)
+{
+ provide_x *provide;
+ ac_domain_x *ac_domain;
+
+ if (name) {
+ package_x *package;
+ HASH_FIND(hh, allpackages, name, strlen(name), package);
+ if (package) {
+ HASH_DELETE(hh, allpackages, package);
+ if (package->older) {
+ /* resume previous version */
+ HASH_ADD_KEYPTR(hh, allpackages, package->older->name, strlen(package->older->name), package->older);
+ package->older->older = package->older->newer;
+ package->older->newer = NULL;
+ package->newer = package->older;
+ package->older = NULL;
+ } else {
+ /* no previous, just take this one out */
+ package->newer = package;
+ }
+ /* need to clean up the all_ac_domain list, too */
+ for (provide = package->provides; provide; provide = provide->prev) {
+ for (ac_domain = provide->ac_domains; ac_domain; ac_domain = ac_domain->prev)
+ msmCancelACDomain(ac_domain->name);
+ }
+ }
+ }
+}
+
+static int is_executable(const char* path, magic_t cookie)
+{
+ const char* buffer = NULL;
+ int result = -1;
+ char* ptr = NULL;
+
+ if ((!path) || (!cookie))
+ return result;
+
+ buffer = magic_file(cookie, path);
+
+ rpmlog(RPMLOG_DEBUG, "buffer: %s\n", buffer);
+
+ if (buffer != NULL) {
+ ptr = strstr(buffer,"executable");
+ if (ptr) result = 0;
+ ptr = strstr(buffer,"ELF");
+ if (ptr) result = 0;
+ }
+
+ return result;
+}
+
+int msmSetFileXAttributes(manifest_x *mfx, const char* filepath, magic_t cookie)
+{
+ provide_x *provide = NULL;
+ filesystem_x *filesystem = NULL;
+ size_t len = 0, match = 0;
+ const char *label = NULL;
+ const char *exec_label = NULL;
+ const char *type = NULL;
+ const char isolatedLabel[] = SMACK_ISOLATED_LABEL;
+ struct stat st;
+
+ if (!filepath) return -1;
+ if (mfx->name) {
+ package_x *package = msmCheckPackage(mfx->name);
+ if (!package)
+ return -1;
+ for (provide = package->provides; provide; provide = provide->prev) {
+ for (filesystem = provide->filesystems; filesystem; filesystem = filesystem->prev) {
+ if (!strcmp(filepath, filesystem->path)) {
+ /* exact match */
+ label = filesystem->label;
+ exec_label = filesystem->exec_label;
+ if (filesystem->type) type = filesystem->type;
+ goto found;
+ }
+
+ len = strlen(filesystem->path);
+ rpmlog(RPMLOG_DEBUG, "filesystem->path: %s, length %d\n", filesystem->path, len);
+ rpmlog(RPMLOG_DEBUG, "filesystem->path + len - 1: %s\n", filesystem->path + len - 1);
+ if (len > match) {
+ if ((!strncmp(filepath, filesystem->path, len)) && (filesystem->type)) {
+ /* partial match and the directory marked as transmutable*/
+ label = filesystem->label;
+ exec_label = filesystem->exec_label;
+ match = len;
+ }
+ if (!strncmp(filesystem->path + len - 1, "*", 1)) {
+ if (!strncmp(filepath, filesystem->path, len - 1)) {
+ /* partial match and the path is marked with wildcard*/
+ label = filesystem->label;
+ exec_label = filesystem->exec_label;
+ match = len - 1;
+ }
+ }
+ }
+ }
+ }
+ } else
+ return -1;
+
+ found:
+ if ((!label) || (!exec_label)) {
+ /* no match, use default label of AC domain */
+ if (mfx->request) { //AC domain is requested in manifest
+ if (mfx->request->ac_domain) {
+ if (!label) label = mfx->request->ac_domain;
+ if (!exec_label) exec_label = mfx->request->ac_domain;
+ } else {
+ rpmlog(RPMLOG_INFO, "Request for AC domain is empty. Can't identify default file label\n");
+ rpmlog(RPMLOG_INFO, "File will be labelled with the label \"Isolated\"\n");
+ if (!label) label = isolatedLabel;
+ if (!exec_label) exec_label = isolatedLabel;
+ }
+ } else if (mfx->define) { // AC domain defined in manifest
+ if (mfx->define->name) {
+ if (!label) label = mfx->define->name;
+ if (!exec_label) exec_label = mfx->define->name;
+ } else {
+ rpmlog(RPMLOG_INFO, "Define for AC domain is empty. Can't identify default file label\n");
+ rpmlog(RPMLOG_INFO, "File will be labelled with the label \"Isolated\"\n");
+ if (!label) label = isolatedLabel;
+ if (!exec_label) exec_label = isolatedLabel;
+ }
+ } else { // no request or definition of domain
+ rpmlog(RPMLOG_INFO, "Both define and request sections are empty. Can't identify default file label\n");
+ rpmlog(RPMLOG_INFO, "File will be labelled with the label \"Isolated\"\n");
+ if (!label) label = isolatedLabel;
+ if (!exec_label) exec_label = isolatedLabel;
+ }
+ }
+
+
+ rpmlog(RPMLOG_INFO, "setting SMACK64 %s for %s\n", label, filepath);
+
+ if (lsetxattr(filepath, SMACK64, label, strlen(label), 0) < 0 ) {
+ rpmlog(RPMLOG_ERR, "Failed to set SMACK64 %s for %s: %s\n",
+ label, filepath, strerror(errno));
+ }
+
+ if ((is_executable(filepath, cookie)) == 0) {
+ if ((exec_label) && (strcmp(exec_label, "none") == 0)) {
+ // do not set SMACK64EXEC
+ rpmlog(RPMLOG_INFO, "not setting SMACK64EXEC for %s as requested in manifest\n", filepath);
+ } else {
+ rpmlog(RPMLOG_INFO, "setting SMACK64EXEC %s for %s\n", exec_label, filepath);
+ if (lsetxattr(filepath, SMACK64EXEC, exec_label, strlen(exec_label), 0) < 0 ) {
+ rpmlog(RPMLOG_ERR, "Failed to set SMACK64EXEC %s for %s: %s\n",
+ exec_label, filepath, strerror(errno));
+ }
+ }
+ }
+
+ if (type) { //marked as transmutable
+ if ((lstat(filepath, &st) != -1) && (S_ISDIR(st.st_mode))) { //check that it is a directory
+ char at_true[] = "TRUE";
+ rpmlog(RPMLOG_INFO, "setting SMACK64TRANSMUTE %s for %s\n", at_true, filepath);
+ if ( lsetxattr(filepath, SMACK64TRANSMUTE, at_true, strlen(at_true), 0) < 0 ) {
+ rpmlog(RPMLOG_ERR, "Failed to set SMACK64TRANSMUTE %s for %s: %s\n",
+ at_true, filepath, strerror(errno));
+ }
+ } else {
+ rpmlog(RPMLOG_DEBUG, "No setting up of transmute attr for a non-directory, path %s\n",
+ filepath);
+ }
+
+ }
+
+
+
+ return 0;
+
+}
+
+void msmRemoveRules(struct smack_accesses *smack_accesses, manifest_x *mfx, int SmackEnabled)
+{
+ provide_x *provide;
+ package_x *package;
+
+ HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
+ if (!package)
+ return;
+
+ if ((mfx->define) || (mfx->sw_sources)) {
+ /* remove smack rule file and rule set from kernel */
+ rpmlog(RPMLOG_DEBUG, "removing smack rules for %s\n", mfx->name);
+ msmSetupSmackRules(smack_accesses, mfx->name, SMACK_UNINSTALL, SmackEnabled);
+ }
+
+ for (provide = mfx->provides; provide; provide = provide->prev) {
+ if (provide->dbuss && !package->older)
+ msmRemoveDBusConfig(package, provide->dbuss);
+
+ }
+
+}
+
+void msmRemoveConfig(manifest_x *mfx)
+{
+ package_x *package;
+
+ HASH_FIND(hh, allpackages, mfx->name, strlen(mfx->name), package);
+ if (package) {
+ if (!package->older) {
+ /* set newer to remove from config list */
+ package->newer = package;
+ rpmlog(RPMLOG_INFO, "removing package for %s\n", mfx->name);
+ }
+ }
+}
+
+sw_source_x *msmSWSourceTreeTraversal(sw_source_x *sw_sources, int (func)(sw_source_x *, void *, void*), void *param, void* param2)
+{
+ sw_source_x *sw_source;
+
+ if (sw_sources) {
+ LISTHEAD(sw_sources, sw_source);
+ /* sw source tree is actually a list ordered into tree traversal path */
+ for (; sw_source; sw_source = sw_source->next)
+ if (!sw_source->newer)
+ if (!(func)(sw_source, param, param2)) return sw_source;
+ }
+ return NULL;
+}
+
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 87b469b13..5962b5a35 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -24,8 +24,14 @@ rpmRC PLUGINHOOK_TSM_POST_FUNC(rpmts ts, int res);
/* per transaction element plugin hooks */
rpmRC PLUGINHOOK_PSM_PRE_FUNC(rpmte te);
rpmRC PLUGINHOOK_PSM_POST_FUNC(rpmte te, int res);
+rpmRC PLUGINHOOK_VERIFY_FUNC(rpmKeyring keyring, rpmtd sigtd, pgpDigParams sig, DIGEST_CTX ctx, int res);
/*per scriptlet plugin hooks */
rpmRC PLUGINHOOK_SCRIPTLET_PRE_FUNC(const char *s_name, int type);
rpmRC PLUGINHOOK_SCRIPTLET_FORK_POST_FUNC(const char *path, int type);
rpmRC PLUGINHOOK_SCRIPTLET_POST_FUNC(const char *s_name, int type, int res);
+
+/*per file plugin hooks */
+rpmRC PLUGINHOOK_FSM_INIT_FUNC(const char* path, mode_t mode);
+rpmRC PLUGINHOOK_FSM_COMMIT_FUNC(const char* path, mode_t mode, int type);
+rpmRC PLUGINHOOK_FILE_CONFLICT_FUNC(rpmts ts, char* path, Header oldHeader, rpmfi oldFi, int rpmrc);