diff options
author | Elena Reshetova <elena.reshetova@intel.com> | 2013-01-04 12:46:42 +0200 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2013-02-02 16:44:45 -0800 |
commit | 61613c459b741f11d26531c0795a432c11fbf4e7 (patch) | |
tree | b3867bd575902efb6388d7fb69c37ac151db3361 | |
parent | 86e7c4ecfd94737f5ede5cadcc2600ea654c399f (diff) | |
download | rpm-61613c459b741f11d26531c0795a432c11fbf4e7.tar.gz rpm-61613c459b741f11d26531c0795a432c11fbf4e7.tar.bz2 rpm-61613c459b741f11d26531c0795a432c11fbf4e7.zip |
Adding security msm plugin
-rw-r--r-- | configure.ac | 55 | ||||
-rw-r--r-- | lib/fsm.c | 26 | ||||
-rw-r--r-- | lib/package.c | 9 | ||||
-rw-r--r-- | lib/rpmplugins.c | 72 | ||||
-rw-r--r-- | lib/rpmplugins.h | 62 | ||||
-rw-r--r-- | lib/transaction.c | 9 | ||||
-rw-r--r-- | macros.in | 6 | ||||
-rw-r--r-- | packaging/rpm.spec | 17 | ||||
-rw-r--r-- | plugins/Makefile.am | 7 | ||||
-rw-r--r-- | plugins/msm-plugin.c | 855 | ||||
-rw-r--r-- | plugins/msm.h | 468 | ||||
-rw-r--r-- | plugins/msmconfig.c | 265 | ||||
-rw-r--r-- | plugins/msmmanifest.c | 1501 | ||||
-rw-r--r-- | plugins/msmmatch.c | 71 | ||||
-rw-r--r-- | plugins/msmxattr.c | 1327 | ||||
-rw-r--r-- | plugins/plugin.h | 6 |
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])], @@ -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); @@ -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); |