From 80070b4a6ca1205c8bc00926fa23c997797f053c Mon Sep 17 00:00:00 2001 From: "smh22@firebug.cl.cam.ac.uk" Date: Mon, 20 Jun 2005 22:28:08 +0000 Subject: bitkeeper revision 1.1718.1.7 (42b742f8NxTuN2pqCHFAWI78dbEYKw) Initial MAC (sHype) support from IBM. Defaults to NULL policy for now. Signed-off-by: Reiner Sailer Signed-off-by: Stefan Berger Signed-off-by: Steven Hand --- .rootkeys | 15 + tools/Makefile | 1 + tools/libxc/xc.h | 2 + tools/libxc/xc_domain.c | 3 + tools/policy/Makefile | 36 ++ tools/policy/policy_tool.c | 557 +++++++++++++++++++++++ tools/python/xen/lowlevel/xc/xc.c | 10 +- tools/python/xen/xend/XendDomainInfo.py | 16 +- tools/python/xen/xend/image.py | 6 +- tools/python/xen/xend/server/SrvDomainDir.py | 1 + tools/python/xen/xm/create.py | 7 +- tools/python/xen/xm/main.py | 10 +- tools/python/xen/xm/opts.py | 7 + xen/Makefile | 4 +- xen/Rules.mk | 1 + xen/acm/Makefile | 15 + xen/acm/acm_chinesewall_hooks.c | 503 +++++++++++++++++++++ xen/acm/acm_core.c | 205 +++++++++ xen/acm/acm_null_hooks.c | 76 ++++ xen/acm/acm_policy.c | 197 +++++++++ xen/acm/acm_simple_type_enforcement_hooks.c | 638 +++++++++++++++++++++++++++ xen/arch/x86/setup.c | 6 + xen/arch/x86/x86_32/entry.S | 1 + xen/common/dom0_ops.c | 15 +- xen/common/event_channel.c | 4 + xen/common/grant_table.c | 6 + xen/common/policy_ops.c | 117 +++++ xen/include/acm/acm_core.h | 117 +++++ xen/include/acm/acm_endian.h | 88 ++++ xen/include/acm/acm_hooks.h | 337 ++++++++++++++ xen/include/public/acm.h | 161 +++++++ xen/include/public/acm_dom0_setup.h | 34 ++ xen/include/public/dom0_ops.h | 3 + xen/include/public/policy_ops.h | 74 ++++ xen/include/public/xen.h | 1 + xen/include/xen/sched.h | 2 + 36 files changed, 3259 insertions(+), 17 deletions(-) create mode 100644 tools/policy/Makefile create mode 100644 tools/policy/policy_tool.c create mode 100644 xen/acm/Makefile create mode 100644 xen/acm/acm_chinesewall_hooks.c create mode 100644 xen/acm/acm_core.c create mode 100644 xen/acm/acm_null_hooks.c create mode 100644 xen/acm/acm_policy.c create mode 100644 xen/acm/acm_simple_type_enforcement_hooks.c create mode 100644 xen/common/policy_ops.c create mode 100644 xen/include/acm/acm_core.h create mode 100644 xen/include/acm/acm_endian.h create mode 100644 xen/include/acm/acm_hooks.h create mode 100644 xen/include/public/acm.h create mode 100644 xen/include/public/acm_dom0_setup.h create mode 100644 xen/include/public/policy_ops.h diff --git a/.rootkeys b/.rootkeys index 338b1be862..b04c5ff74b 100644 --- a/.rootkeys +++ b/.rootkeys @@ -785,6 +785,8 @@ 41adc641dV-0cDLSyzMs5BT8nL7v3Q tools/misc/xenperf.c 4056f5155QYZdsk-1fLdjsZPFTnlhg tools/misc/xensymoops 40cf2937dqM1jWW87O5OoOYND8leuA tools/misc/xm +42b742f6JFcp6LFpYu-B4AEsfQwSFw tools/policy/Makefile +42b742f66XOdRMrwaHvbCdSSQyCrFw tools/policy/policy_tool.c 4270cc81g3nSNYCZ1ryCMDEbLtMtbQ tools/pygrub/Makefile 4270deeccyRsJn6jLnRh9odRtMW9SA tools/pygrub/README 4270cc81EIl7NyaS3Av6IPRk2c2a6Q tools/pygrub/setup.py @@ -1101,6 +1103,12 @@ 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk +42b742f6XHTfIEm_hUPtzjKr37LVhw xen/acm/Makefile +42b742f6tHzn0fZWH3TjPva8gbqpow xen/acm/acm_chinesewall_hooks.c +42b742f6bM8kZwuIUbepHZ8SQQkjJA xen/acm/acm_core.c +42b742f6cwfrPubqH47gQpke8xkYSA xen/acm/acm_null_hooks.c +42b742f69qSxm5MM-wtPaWtCqyI3KA xen/acm/acm_policy.c +42b742f6VbmdlwekQRMhXugjcu9QXg xen/acm/acm_simple_type_enforcement_hooks.c 421098b25A0RvuYN3rP28ga3_FN3_Q xen/arch/ia64/Makefile 421098b2okIeYXS9w9avmSozls61xA xen/arch/ia64/Rules.mk 421098b21p12UcKjHBrLh_LjlvNEwA xen/arch/ia64/acpi.c @@ -1324,6 +1332,7 @@ 41a61536SZbR6cj1ukWTb0DYU-vz9w xen/common/multicall.c 3ddb79bdD4SLmmdMD7yLW5HcUWucXw xen/common/page_alloc.c 3e54c38dkHAev597bPr71-hGzTdocg xen/common/perfc.c +42b742f6mgq9puEr7lUrLST0VEpsig xen/common/policy_ops.c 40589968dD2D1aejwSOvrROg7fOvGQ xen/common/sched_bvt.c 41ebbfe9oF1BF3cH5v7yE3eOL9uPbA xen/common/sched_sedf.c 3e397e6619PgAfBbw2XFbXkewvUWgw xen/common/schedule.c @@ -1339,6 +1348,9 @@ 4049e6bfNSIq7s7OV-Bd69QD0RpR2Q xen/drivers/char/console.c 4298e018XQtZkCdufpyFimOGZqqsFA xen/drivers/char/ns16550.c 3e4a8cb7nMChlro4wvOBo76n__iCFA xen/drivers/char/serial.c +42b742f6OteAMPWnoqxqfRX3yxD0yw xen/include/acm/acm_core.h +42b742f6XfIijctEwA0YWL2BoWtDNg xen/include/acm/acm_endian.h +42b742f6jXvp1vdbU2v2WJjTPku65A xen/include/acm/acm_hooks.h 40715b2cFpte_UNWnBZW0Du7z9AhTQ xen/include/acpi/acconfig.h 40715b2ctNvVZ058w8eM8DR9hOat_A xen/include/acpi/acexcep.h 40715b2com8I01qcHcAw47e93XsCqQ xen/include/acpi/acglobal.h @@ -1489,6 +1501,8 @@ 404f1bc4tWkB9Qr8RkKtZGW5eMQzhw xen/include/asm-x86/x86_64/uaccess.h 422f27c8RHFkePhD34VIEpMMqofZcA xen/include/asm-x86/x86_emulate.h 400304fcmRQmDdFYEzDh0wcBba9alg xen/include/public/COPYING +42b742f6duiOTlZvysQkRYZHYBXqvg xen/include/public/acm.h +42b742f7TIMsQgUaNDJXp3QlBve2SQ xen/include/public/acm_dom0_setup.h 421098b7OKb9YH_EUA_UpCxBjaqtgA xen/include/public/arch-ia64.h 404f1bc68SXxmv0zQpXBWGrCzSyp8w xen/include/public/arch-x86_32.h 404f1bc7IwU-qnH8mJeVu0YsNGMrcw xen/include/public/arch-x86_64.h @@ -1502,6 +1516,7 @@ 41d40e9b8zCk5VDqhVbuQyhc7G3lqA xen/include/public/io/ring.h 41ee5e8c6mLxIx82KPsbpt_uts_vSA xen/include/public/io/usbif.h 4051db79512nOCGweabrFWO2M2h5ng xen/include/public/physdev.h +42b742f7Lzy8SKKG25L_-fgk5FHA2Q xen/include/public/policy_ops.h 40589968wmhPmV5-ENbBYmMjnedgKw xen/include/public/sched_ctl.h 404f3d2eR2Owk-ZcGOx9ULGHg3nrww xen/include/public/trace.h 42b5a5f2QC1IxeuwCwwsOEhvcJ2BJg xen/include/public/version.h diff --git a/tools/Makefile b/tools/Makefile index 3a38e899de..00eb4991cc 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,6 +12,7 @@ SUBDIRS += xcs SUBDIRS += xcutils SUBDIRS += pygrub SUBDIRS += firmware +SUBDIRS += policy .PHONY: all install clean check check_clean ioemu eioemuinstall ioemuclean diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h index 27e7845798..e54f9b198d 100644 --- a/tools/libxc/xc.h +++ b/tools/libxc/xc.h @@ -110,6 +110,7 @@ int xc_waitdomain_core(int domain, typedef struct { u32 domid; + u32 ssidref; unsigned int dying:1, crashed:1, shutdown:1, paused:1, blocked:1, running:1; unsigned int shutdown_reason; /* only meaningful if shutdown==1 */ @@ -124,6 +125,7 @@ typedef struct { typedef dom0_getdomaininfo_t xc_domaininfo_t; int xc_domain_create(int xc_handle, + u32 ssidref, u32 *pdomid); diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c index 8f0bba3216..2edf11c39d 100644 --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -9,6 +9,7 @@ #include "xc_private.h" int xc_domain_create(int xc_handle, + u32 ssidref, u32 *pdomid) { int err; @@ -16,6 +17,7 @@ int xc_domain_create(int xc_handle, op.cmd = DOM0_CREATEDOMAIN; op.u.createdomain.domain = (domid_t)*pdomid; + op.u.createdomain.ssidref = ssidref; if ( (err = do_dom0_op(xc_handle, &op)) != 0 ) return err; @@ -101,6 +103,7 @@ int xc_domain_getinfo(int xc_handle, info->crashed = 1; } + info->ssidref = op.u.getdomaininfo.ssidref; info->nr_pages = op.u.getdomaininfo.tot_pages; info->max_memkb = op.u.getdomaininfo.max_pages<<(PAGE_SHIFT); info->shared_info_frame = op.u.getdomaininfo.shared_info_frame; diff --git a/tools/policy/Makefile b/tools/policy/Makefile new file mode 100644 index 0000000000..b8d67471ae --- /dev/null +++ b/tools/policy/Makefile @@ -0,0 +1,36 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +SRCS = policy_tool.c +CFLAGS += -static +CFLAGS += -Wall +CFLAGS += -Werror +CFLAGS += -O3 +CFLAGS += -fno-strict-aliasing +CFLAGS += -I. + +all: build +build: mk-symlinks + $(MAKE) policy_tool + +default: all + +install: all + +policy_tool : policy_tool.c + $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< + +clean: + rm -rf policy_tool xen + + +LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen-sparse) +mk-symlinks: + [ -e xen/linux ] || mkdir -p xen/linux + [ -e xen/io ] || mkdir -p xen/io + ( cd xen >/dev/null ; \ + ln -sf ../$(XEN_ROOT)/xen/include/public/*.h . ) + ( cd xen/io >/dev/null ; \ + ln -sf ../../$(XEN_ROOT)/xen/include/public/io/*.h . ) + ( cd xen/linux >/dev/null ; \ + ln -sf ../../$(LINUX_ROOT)/include/asm-xen/linux-public/*.h . ) diff --git a/tools/policy/policy_tool.c b/tools/policy/policy_tool.c new file mode 100644 index 0000000000..696a70c282 --- /dev/null +++ b/tools/policy/policy_tool.c @@ -0,0 +1,557 @@ +/**************************************************************** + * policy_tool.c + * + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Reiner Sailer + * Stefan Berger + * + * 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, version 2 of the + * License. + * + * sHype policy management tool. This code runs in a domain and + * manages the Xen security policy by interacting with the + * Xen access control module via a /proc/xen/policycmd proc-ioctl, + * which is translated into a policy_op hypercall into Xen. + * + * todo: implement setpolicy to dynamically set a policy cache. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +#include + +#include + +#include + +#define ERROR(_m, _a...) \ + fprintf(stderr, "ERROR: " _m "\n" , ## _a ) + +#define PERROR(_m, _a...) \ + fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \ + errno, strerror(errno)) + +static inline int do_policycmd(int xc_handle, + unsigned int cmd, + unsigned long data) +{ + return ioctl(xc_handle, cmd, data); +} + +static inline int do_xen_hypercall(int xc_handle, + privcmd_hypercall_t *hypercall) +{ + return do_policycmd(xc_handle, + IOCTL_PRIVCMD_HYPERCALL, + (unsigned long)hypercall); +} + +static inline int do_policy_op(int xc_handle, policy_op_t *op) +{ + int ret = -1; + privcmd_hypercall_t hypercall; + + op->interface_version = POLICY_INTERFACE_VERSION; + + hypercall.op = __HYPERVISOR_policy_op; + hypercall.arg[0] = (unsigned long)op; + + if ( mlock(op, sizeof(*op)) != 0 ) + { + PERROR("Could not lock memory for Xen policy hypercall"); + goto out1; + } + + if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 ) + { + if ( errno == EACCES ) + fprintf(stderr, "POLICY operation failed -- need to" + " rebuild the user-space tool set?\n"); + goto out2; + } + + out2: (void)munlock(op, sizeof(*op)); + out1: return ret; +} + +/*************************** DUMPS *******************************/ + +void acm_dump_chinesewall_buffer(void *buf, int buflen) { + + struct acm_chwall_policy_buffer *cwbuf = (struct acm_chwall_policy_buffer *)buf; + domaintype_t *ssids, *conflicts, *running_types, *conflict_aggregate; + int i,j; + + + if (htons(cwbuf->policy_code) != ACM_CHINESE_WALL_POLICY) { + printf("CHINESE WALL POLICY CODE not found ERROR!!\n"); + return; + } + printf("\n\nChinese Wall policy:\n"); + printf("====================\n"); + printf("Max Types = %x.\n", ntohs(cwbuf->chwall_max_types)); + printf("Max Ssidrefs = %x.\n", ntohs(cwbuf->chwall_max_ssidrefs)); + printf("Max ConfSets = %x.\n", ntohs(cwbuf->chwall_max_conflictsets)); + printf("Ssidrefs Off = %x.\n", ntohs(cwbuf->chwall_ssid_offset)); + printf("Conflicts Off = %x.\n", ntohs(cwbuf->chwall_conflict_sets_offset)); + printf("Runing T. Off = %x.\n", ntohs(cwbuf->chwall_running_types_offset)); + printf("C. Agg. Off = %x.\n", ntohs(cwbuf->chwall_conflict_aggregate_offset)); + printf("\nSSID To CHWALL-Type matrix:\n"); + + ssids = (domaintype_t *)(buf + ntohs(cwbuf->chwall_ssid_offset)); + for(i=0; i< ntohs(cwbuf->chwall_max_ssidrefs); i++) { + printf("\n ssidref%2x: ", i); + for(j=0; j< ntohs(cwbuf->chwall_max_types); j++) + printf("%02x ", ntohs(ssids[i*ntohs(cwbuf->chwall_max_types) + j])); + } + printf("\n\nConfict Sets:\n"); + conflicts = (domaintype_t *)(buf + ntohs(cwbuf->chwall_conflict_sets_offset)); + for(i=0; i< ntohs(cwbuf->chwall_max_conflictsets); i++) { + printf("\n c-set%2x: ", i); + for(j=0; j< ntohs(cwbuf->chwall_max_types); j++) + printf("%02x ", ntohs(conflicts[i*ntohs(cwbuf->chwall_max_types) +j])); + } + printf("\n"); + + printf("\nRunning\nTypes: "); + if (ntohs(cwbuf->chwall_running_types_offset)) { + running_types = (domaintype_t *)(buf + ntohs(cwbuf->chwall_running_types_offset)); + for(i=0; i< ntohs(cwbuf->chwall_max_types); i++) { + printf("%02x ", ntohs(running_types[i])); + } + printf("\n"); + } else { + printf("Not Reported!\n"); + } + printf("\nConflict\nAggregate Set: "); + if (ntohs(cwbuf->chwall_conflict_aggregate_offset)) { + conflict_aggregate = (domaintype_t *)(buf + ntohs(cwbuf->chwall_conflict_aggregate_offset)); + for(i=0; i< ntohs(cwbuf->chwall_max_types); i++) { + printf("%02x ", ntohs(conflict_aggregate[i])); + } + printf("\n\n"); + } else { + printf("Not Reported!\n"); + } +} + +void acm_dump_ste_buffer(void *buf, int buflen) { + + struct acm_ste_policy_buffer *stebuf = (struct acm_ste_policy_buffer *)buf; + domaintype_t *ssids; + int i,j; + + + if (ntohs(stebuf->policy_code) != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) { + printf("SIMPLE TYPE ENFORCEMENT POLICY CODE not found ERROR!!\n"); + return; + } + printf("\nSimple Type Enforcement policy:\n"); + printf("===============================\n"); + printf("Max Types = %x.\n", ntohs(stebuf->ste_max_types)); + printf("Max Ssidrefs = %x.\n", ntohs(stebuf->ste_max_ssidrefs)); + printf("Ssidrefs Off = %x.\n", ntohs(stebuf->ste_ssid_offset)); + printf("\nSSID To STE-Type matrix:\n"); + + ssids = (domaintype_t *)(buf + ntohs(stebuf->ste_ssid_offset)); + for(i=0; i< ntohs(stebuf->ste_max_ssidrefs); i++) { + printf("\n ssidref%2x: ", i); + for(j=0; j< ntohs(stebuf->ste_max_types); j++) + printf("%02x ", ntohs(ssids[i*ntohs(stebuf->ste_max_types) +j])); + } + printf("\n\n"); +} + +void acm_dump_policy_buffer(void *buf, int buflen) { + struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf; + + printf("\nPolicy dump:\n"); + printf("============\n"); + printf("Magic = %x.\n", ntohl(pol->magic)); + printf("PolVer = %x.\n", ntohl(pol->policyversion)); + printf("Len = %x.\n", ntohl(pol->len)); + printf("Primary = %s (c=%x, off=%x).\n", + ACM_POLICY_NAME(ntohs(pol->primary_policy_code)), + ntohs(pol->primary_policy_code), ntohs(pol->primary_buffer_offset)); + printf("Secondary = %s (c=%x, off=%x).\n", + ACM_POLICY_NAME(ntohs(pol->secondary_policy_code)), + ntohs(pol->secondary_policy_code), ntohs(pol->secondary_buffer_offset)); + switch (ntohs(pol->primary_policy_code)) { + case ACM_CHINESE_WALL_POLICY: + acm_dump_chinesewall_buffer(buf+ntohs(pol->primary_buffer_offset), + ntohl(pol->len) - ntohs(pol->primary_buffer_offset)); + break; + case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY: + acm_dump_ste_buffer(buf+ntohs(pol->primary_buffer_offset), + ntohl(pol->len) - ntohs(pol->primary_buffer_offset)); + break; + case ACM_NULL_POLICY: + printf("Primary policy is NULL Policy (n/a).\n"); + break; + default: + printf("UNKNOWN POLICY!\n"); + } + switch (ntohs(pol->secondary_policy_code)) { + case ACM_CHINESE_WALL_POLICY: + acm_dump_chinesewall_buffer(buf+ntohs(pol->secondary_buffer_offset), + ntohl(pol->len) - ntohs(pol->secondary_buffer_offset)); + break; + case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY: + acm_dump_ste_buffer(buf+ntohs(pol->secondary_buffer_offset), + ntohl(pol->len) - ntohs(pol->secondary_buffer_offset)); + break; + case ACM_NULL_POLICY: + printf("Secondary policy is NULL Policy (n/a).\n"); + break; + default: + printf("UNKNOWN POLICY!\n"); + } + printf("\nPolicy dump End.\n\n"); +} + +/*************************** set policy ****************************/ + +int acm_domain_set_chwallpolicy(void *bufstart, int buflen) { +#define CWALL_MAX_SSIDREFS 5 +#define CWALL_MAX_TYPES 10 +#define CWALL_MAX_CONFLICTSETS 2 + + struct acm_chwall_policy_buffer *chwall_bin_pol = (struct acm_chwall_policy_buffer *)bufstart; + domaintype_t *ssidrefs, *conflicts; + int ret = 0; + int i,j; + + chwall_bin_pol->chwall_max_types = htons(CWALL_MAX_TYPES); + chwall_bin_pol->chwall_max_ssidrefs = htons(CWALL_MAX_SSIDREFS); + chwall_bin_pol->policy_code = htons(ACM_CHINESE_WALL_POLICY); + chwall_bin_pol->chwall_ssid_offset = htons(sizeof(struct acm_chwall_policy_buffer)); + chwall_bin_pol->chwall_max_conflictsets = htons(CWALL_MAX_CONFLICTSETS); + chwall_bin_pol->chwall_conflict_sets_offset = + htons( + ntohs(chwall_bin_pol->chwall_ssid_offset) + + sizeof(domaintype_t)*CWALL_MAX_SSIDREFS*CWALL_MAX_TYPES); + chwall_bin_pol->chwall_running_types_offset = 0; /* not set */ + chwall_bin_pol->chwall_conflict_aggregate_offset = 0; /* not set */ + ret += sizeof(struct acm_chwall_policy_buffer); + /* now push example ssids into the buffer (max_ssidrefs x max_types entries) */ + /* check buffer size */ + if ((buflen - ret) < (CWALL_MAX_TYPES*CWALL_MAX_SSIDREFS*sizeof(domaintype_t))) + return -1; /* not enough space */ + + ssidrefs = (domaintype_t *)(bufstart+ntohs(chwall_bin_pol->chwall_ssid_offset)); + for(i=0; i< CWALL_MAX_SSIDREFS; i++) { + for (j=0; j< CWALL_MAX_TYPES; j++) + ssidrefs[i*CWALL_MAX_TYPES + j] = htons(0); + /* here, set type i for ssidref i; generally, a ssidref can have multiple chwall types */ + if (i < CWALL_MAX_SSIDREFS) + ssidrefs[i*CWALL_MAX_TYPES + i] = htons(1); + } + ret += CWALL_MAX_TYPES*CWALL_MAX_SSIDREFS*sizeof(domaintype_t); + if ((buflen - ret) < (CWALL_MAX_CONFLICTSETS*CWALL_MAX_TYPES*sizeof(domaintype_t))) + return -1; /* not enough space */ + + /* now the chinese wall policy conflict sets*/ + conflicts = (domaintype_t *)(bufstart + + ntohs(chwall_bin_pol->chwall_conflict_sets_offset)); + memset((void *)conflicts, 0, CWALL_MAX_CONFLICTSETS*CWALL_MAX_TYPES*sizeof(domaintype_t)); + /* just 1 conflict set [0]={2,3}, [1]={0,5,6} */ + if (CWALL_MAX_TYPES > 3) { + conflicts[2] = htons(1); conflicts[3] = htons(1); /* {2,3} */ + conflicts[CWALL_MAX_TYPES] = htons(1); conflicts[CWALL_MAX_TYPES+5] = htons(1); + conflicts[CWALL_MAX_TYPES+6] = htons(1);/* {0,5,6} */ + } + ret += sizeof(domaintype_t)*CWALL_MAX_CONFLICTSETS*CWALL_MAX_TYPES; + return ret; +} + +int acm_domain_set_stepolicy(void *bufstart, int buflen) { +#define STE_MAX_SSIDREFS 5 +#define STE_MAX_TYPES 5 + + struct acm_ste_policy_buffer *ste_bin_pol = (struct acm_ste_policy_buffer *)bufstart; + domaintype_t *ssidrefs; + int i,j, ret = 0; + + ste_bin_pol->ste_max_types = htons(STE_MAX_TYPES); + ste_bin_pol->ste_max_ssidrefs = htons(STE_MAX_SSIDREFS); + ste_bin_pol->policy_code = htons(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); + ste_bin_pol->ste_ssid_offset = htons(sizeof(struct acm_ste_policy_buffer)); + ret += sizeof(struct acm_ste_policy_buffer); + /* check buffer size */ + if ((buflen - ret) < (STE_MAX_TYPES*STE_MAX_SSIDREFS*sizeof(domaintype_t))) + return -1; /* not enough space */ + + ssidrefs = (domaintype_t *)(bufstart+ntohs(ste_bin_pol->ste_ssid_offset)); + for(i=0; i< STE_MAX_SSIDREFS; i++) { + for (j=0; j< STE_MAX_TYPES; j++) + ssidrefs[i*STE_MAX_TYPES + j] = htons(0); + /* set type i in ssidref 0 and ssidref i */ + ssidrefs[i] = htons(1); /* ssidref 0 has all types set */ + if (i < STE_MAX_SSIDREFS) + ssidrefs[i*STE_MAX_TYPES + i] = htons(1); + } + ret += STE_MAX_TYPES*STE_MAX_SSIDREFS*sizeof(domaintype_t); + return ret; +} + +#define MAX_PUSH_BUFFER 16384 +u8 push_buffer[MAX_PUSH_BUFFER]; + +int acm_domain_setpolicy(int xc_handle) +{ + int ret; + struct acm_policy_buffer *bin_pol; + policy_op_t op; + + /* future: read policy from file and set it */ + bin_pol = (struct acm_policy_buffer *)push_buffer; + bin_pol->magic = htonl(ACM_MAGIC); + bin_pol->policyversion = htonl(POLICY_INTERFACE_VERSION); + bin_pol->primary_policy_code = htons(ACM_CHINESE_WALL_POLICY); + bin_pol->secondary_policy_code = htons(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); + + bin_pol->len = htonl(sizeof(struct acm_policy_buffer)); + bin_pol->primary_buffer_offset = htons(ntohl(bin_pol->len)); + ret = acm_domain_set_chwallpolicy(push_buffer + ntohs(bin_pol->primary_buffer_offset), + MAX_PUSH_BUFFER - ntohs(bin_pol->primary_buffer_offset)); + if (ret < 0) { + printf("ERROR creating chwallpolicy buffer.\n"); + return -1; + } + bin_pol->len = htonl(ntohl(bin_pol->len) + ret); + bin_pol->secondary_buffer_offset = htons(ntohl(bin_pol->len)); + ret = acm_domain_set_stepolicy(push_buffer + ntohs(bin_pol->secondary_buffer_offset), + MAX_PUSH_BUFFER - ntohs(bin_pol->secondary_buffer_offset)); + if (ret < 0) { + printf("ERROR creating chwallpolicy buffer.\n"); + return -1; + } + bin_pol->len = htonl(ntohl(bin_pol->len) + ret); + + /* dump it and then push it down into xen/acm */ + acm_dump_policy_buffer(push_buffer, ntohl(bin_pol->len)); + op.cmd = POLICY_SETPOLICY; + op.u.setpolicy.pushcache = (void *)push_buffer; + op.u.setpolicy.pushcache_size = ntohl(bin_pol->len); + op.u.setpolicy.policy_type = ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY; + ret = do_policy_op(xc_handle, &op); + + if (ret) + printf("ERROR setting policy. Use 'xm dmesg' to see details.\n"); + else + printf("Successfully changed policy.\n"); + return ret; +} + +/******************************* get policy ******************************/ + +#define PULL_CACHE_SIZE 8192 +u8 pull_buffer[PULL_CACHE_SIZE]; +int acm_domain_getpolicy(int xc_handle) +{ + policy_op_t op; + int ret; + + memset(pull_buffer, 0x00, sizeof(pull_buffer)); + op.cmd = POLICY_GETPOLICY; + op.u.getpolicy.pullcache = (void *)pull_buffer; + op.u.getpolicy.pullcache_size = sizeof(pull_buffer); + ret = do_policy_op(xc_handle, &op); + /* dump policy */ + acm_dump_policy_buffer(pull_buffer, sizeof(pull_buffer)); + return ret; +} + +/************************ load binary policy ******************************/ + +int acm_domain_loadpolicy(int xc_handle, + const char *filename) +{ + struct stat mystat; + int ret, fd; + off_t len; + u8 *buffer; + + if ((ret = stat(filename, &mystat))) { + printf("File %s not found.\n",filename); + goto out; + } + + len = mystat.st_size; + if ((buffer = malloc(len)) == NULL) { + ret = -ENOMEM; + goto out; + } + if ((fd = open(filename, O_RDONLY)) <= 0) { + ret = -ENOENT; + printf("File %s not found.\n",filename); + goto free_out; + } + if (len == read(fd, buffer, len)) { + policy_op_t op; + /* dump it and then push it down into xen/acm */ + acm_dump_policy_buffer(buffer, len); + op.cmd = POLICY_SETPOLICY; + op.u.setpolicy.pushcache = (void *)buffer; + op.u.setpolicy.pushcache_size = len; + op.u.setpolicy.policy_type = ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY; + ret = do_policy_op(xc_handle, &op); + + if (ret) + printf("ERROR setting policy. Use 'xm dmesg' to see details.\n"); + else + printf("Successfully changed policy.\n"); + + } else { + ret = -1; + } + close(fd); + free_out: + free(buffer); + out: + return ret; +} + +/************************ dump hook statistics ******************************/ +void +dump_ste_stats(struct acm_ste_stats_buffer *ste_stats) +{ + printf("STE-Policy Security Hook Statistics:\n"); + printf("ste: event_channel eval_count = %d\n", ntohl(ste_stats->ec_eval_count)); + printf("ste: event_channel denied_count = %d\n", ntohl(ste_stats->ec_denied_count)); + printf("ste: event_channel cache_hit_count = %d\n", ntohl(ste_stats->ec_cachehit_count)); + printf("ste:\n"); + printf("ste: grant_table eval_count = %d\n", ntohl(ste_stats->gt_eval_count)); + printf("ste: grant_table denied_count = %d\n", ntohl(ste_stats->gt_denied_count)); + printf("ste: grant_table cache_hit_count = %d\n", ntohl(ste_stats->gt_cachehit_count)); +} + +#define PULL_STATS_SIZE 8192 +int acm_domain_dumpstats(int xc_handle) +{ + u8 stats_buffer[PULL_STATS_SIZE]; + policy_op_t op; + int ret; + struct acm_stats_buffer *stats; + + memset(stats_buffer, 0x00, sizeof(stats_buffer)); + op.cmd = POLICY_DUMPSTATS; + op.u.dumpstats.pullcache = (void *)stats_buffer; + op.u.dumpstats.pullcache_size = sizeof(stats_buffer); + ret = do_policy_op(xc_handle, &op); + + if (ret < 0) { + printf("ERROR dumping policy stats. Use 'xm dmesg' to see details.\n"); + return ret; + } + stats = (struct acm_stats_buffer *)stats_buffer; + + printf("\nPolicy dump:\n"); + printf("============\n"); + printf("Magic = %x.\n", ntohl(stats->magic)); + printf("PolVer = %x.\n", ntohl(stats->policyversion)); + printf("Len = %x.\n", ntohl(stats->len)); + + switch(ntohs(stats->primary_policy_code)) { + case ACM_NULL_POLICY: + printf("NULL Policy: No statistics apply.\n"); + break; + case ACM_CHINESE_WALL_POLICY: + printf("Chinese Wall Policy: No statistics apply.\n"); + break; + case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY: + dump_ste_stats((struct acm_ste_stats_buffer *)(stats_buffer + ntohs(stats->primary_stats_offset))); + break; + default: + printf("UNKNOWN PRIMARY POLICY ERROR!\n"); + } + switch(ntohs(stats->secondary_policy_code)) { + case ACM_NULL_POLICY: + printf("NULL Policy: No statistics apply.\n"); + break; + case ACM_CHINESE_WALL_POLICY: + printf("Chinese Wall Policy: No statistics apply.\n"); + break; + case ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY: + dump_ste_stats((struct acm_ste_stats_buffer *)(stats_buffer + ntohs(stats->secondary_stats_offset))); + break; + default: + printf("UNKNOWN SECONDARY POLICY ERROR!\n"); + } + return ret; +} + +/***************************** main **************************************/ + +void +usage(char *progname){ + printf("Use: %s \n" + "\t setpolicy\n" + "\t getpolicy\n" + "\t dumpstats\n" + "\t loadpolicy \n", progname); + exit(-1); +} + +int +main(int argc, char **argv) { + + int policycmd_fd; + + if (argc < 2) + usage(argv[0]); + + if ((policycmd_fd = open("/proc/xen/privcmd", O_RDONLY)) <= 0) { + printf("ERROR: Could not open xen policycmd device!\n"); + exit(-1); + } + + if (!strcmp(argv[1], "setpolicy")) { + if (argc != 2) + usage(argv[0]); + acm_domain_setpolicy(policycmd_fd); + + } else if (!strcmp(argv[1], "getpolicy")) { + if (argc != 2) + usage(argv[0]); + acm_domain_getpolicy(policycmd_fd); + + } else if (!strcmp(argv[1], "loadpolicy")) { + if (argc != 3) + usage(argv[0]); + acm_domain_loadpolicy(policycmd_fd, argv[2]); + + } else if (!strcmp(argv[1], "dumpstats")) { + if (argc != 2) + usage(argv[0]); + acm_domain_dumpstats(policycmd_fd); + + } else + usage(argv[0]); + + close(policycmd_fd); + return 0; +} diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c index 13d60be08e..81721d961e 100644 --- a/tools/python/xen/lowlevel/xc/xc.c +++ b/tools/python/xen/lowlevel/xc/xc.c @@ -78,13 +78,14 @@ static PyObject *pyxc_domain_create(PyObject *self, u32 dom = 0; int ret; + u32 ssidref = 0xFFFFFFFF; - static char *kwd_list[] = { "dom", NULL }; + static char *kwd_list[] = { "dom", "ssidref", NULL }; - if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwd_list, &dom)) + if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list, &dom, &ssidref)) return NULL; - if ( (ret = xc_domain_create(xc->xc_handle, &dom)) < 0 ) + if ( (ret = xc_domain_create(xc->xc_handle, ssidref, &dom)) < 0 ) return PyErr_SetFromErrno(xc_error); return PyInt_FromLong(dom); @@ -230,7 +231,7 @@ static PyObject *pyxc_domain_getinfo(PyObject *self, } info_dict = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i" - ",s:l,s:L,s:l,s:i}", + ",s:l,s:L,s:l,s:i,s:i}", "dom", info[i].domid, "vcpus", info[i].vcpus, "dying", info[i].dying, @@ -242,6 +243,7 @@ static PyObject *pyxc_domain_getinfo(PyObject *self, "mem_kb", info[i].nr_pages*4, "cpu_time", info[i].cpu_time, "maxmem_kb", info[i].max_memkb, + "ssidref", info[i].ssidref, "shutdown_reason", info[i].shutdown_reason); PyDict_SetItemString( info_dict, "vcpu_to_cpu", vcpu_list ); PyDict_SetItemString( info_dict, "cpumap", cpumap_list ); diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index a47709a369..0383b9c981 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -202,7 +202,9 @@ class XendDomainInfo: """ db = parentdb.addChild(uuid) vm = cls(db) - id = xc.domain_create() + ssidref = int(sxp.child_value(config, 'ssidref')) + log.debug('restoring with ssidref='+str(ssidref)) + id = xc.domain_create(ssidref = ssidref) vm.setdom(id) try: vm.restore = True @@ -241,6 +243,7 @@ class XendDomainInfo: self.start_time = None self.name = None self.memory = None + self.ssidref = None self.image = None self.channel = None @@ -316,6 +319,7 @@ class XendDomainInfo: """ self.info = info self.memory = self.info['mem_kb'] / 1024 + self.ssidref = self.info['ssidref'] def state_set(self, state): self.state_updated.acquire() @@ -336,6 +340,7 @@ class XendDomainInfo: s += " id=" + str(self.id) s += " name=" + self.name s += " memory=" + str(self.memory) + s += " ssidref=" + str(self.ssidref) console = self.getConsole() if console: s += " console=" + str(console.console_port) @@ -398,7 +403,8 @@ class XendDomainInfo: sxpr = ['domain', ['id', self.id], ['name', self.name], - ['memory', self.memory] ] + ['memory', self.memory], + ['ssidref', self.ssidref] ] if self.uuid: sxpr.append(['uuid', self.uuid]) if self.info: @@ -533,6 +539,7 @@ class XendDomainInfo: self.memory = int(sxp.child_value(config, 'memory')) if self.memory is None: raise VmError('missing memory size') + self.ssidref = int(sxp.child_value(config, 'ssidref')) cpu = sxp.child_value(config, 'cpu') if self.recreate and self.id and cpu is not None and int(cpu) >= 0: xc.domain_pincpu(self.id, 0, 1< Created domain=%d name=%s memory=%d', id, self.name, self.memory) self.setdom(id) @@ -1011,6 +1018,7 @@ addImageHandlerClass(VmxImageHandler) # Ignore the fields we already handle. add_config_handler('name', vm_field_ignore) add_config_handler('memory', vm_field_ignore) +add_config_handler('ssidref', vm_field_ignore) add_config_handler('cpu', vm_field_ignore) add_config_handler('cpu_weight', vm_field_ignore) add_config_handler('console', vm_field_ignore) diff --git a/tools/python/xen/xend/image.py b/tools/python/xen/xend/image.py index f3b8642a5f..5abc121e86 100644 --- a/tools/python/xen/xend/image.py +++ b/tools/python/xen/xend/image.py @@ -111,7 +111,7 @@ class ImageHandler: except OSError, ex: log.warning("error removing bootloader file '%s': %s", f, ex) - def initDomain(self, dom, memory, cpu, cpu_weight): + def initDomain(self, dom, memory, ssidref, cpu, cpu_weight): """Initial domain create. @return domain id @@ -119,14 +119,14 @@ class ImageHandler: mem_kb = self.getDomainMemory(memory) if not self.vm.restore: - dom = xc.domain_create(dom = dom or 0) + dom = xc.domain_create(dom = dom or 0, ssidref = ssidref) # if bootloader, unlink here. But should go after buildDomain() ? if self.vm.bootloader: self.unlink(self.kernel) self.unlink(self.ramdisk) if dom <= 0: raise VmError('Creating domain failed: name=%s' % self.vm.name) - log.debug("initDomain: cpu=%d mem_kb=%d dom=%d", cpu, mem_kb, dom) + log.debug("initDomain: cpu=%d mem_kb=%d ssidref=%d dom=%d", cpu, mem_kb, ssidref, dom) # xc.domain_setuuid(dom, uuid) xc.domain_setcpuweight(dom, cpu_weight) xc.domain_setmaxmem(dom, mem_kb) diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py index d6f6291716..7fcc7c5cf7 100644 --- a/tools/python/xen/xend/server/SrvDomainDir.py +++ b/tools/python/xen/xend/server/SrvDomainDir.py @@ -142,6 +142,7 @@ class SrvDomainDir(SrvDir): % (url, d.name, d.name)) req.write('id=%s' % d.id) req.write('memory=%d'% d.memory) + req.write('ssidref=%d'% d.ssidref) req.write('') req.write('') diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 23001cc458..d2219f9668 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -120,6 +120,10 @@ gopts.var('memory', val='MEMORY', fn=set_int, default=128, use="Domain memory in MB.") +gopts.var('ssidref', val='SSIDREF', + fn=set_u32, default=0xffffffff, + use="Security Identifier.") + gopts.var('maxmem', val='MEMORY', fn=set_int, default=None, use="Maximum domain memory in MB.") @@ -405,7 +409,8 @@ def make_config(opts, vals): config = ['vm', ['name', vals.name ], - ['memory', vals.memory ]] + ['memory', vals.memory ], + ['ssidref', vals.ssidref ]] if vals.maxmem: config.append(['maxmem', vals.maxmem]) if vals.cpu is not None: diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index d02a190ac4..6eda17e2a9 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -383,7 +383,7 @@ class ProgList(Prog): self.brief_list(doms) def brief_list(self, doms): - print 'Name Id Mem(MB) CPU VCPU(s) State Time(s) Console' + print 'Name Id Mem(MB) CPU VCPU(s) State Time(s) Console SSID-REF' for dom in doms: info = server.xend_domain(dom) d = {} @@ -399,8 +399,12 @@ class ProgList(Prog): d['port'] = sxp.child_value(console, 'console_port') else: d['port'] = '' - print ("%(name)-16s %(dom)3d %(mem)7d %(cpu)3d %(vcpus)5d %(state)5s %(cpu_time)7.1f %(port)4s" - % d) + if ((int(sxp.child_value(info, 'ssidref', '-1'))) != -1): + d['ssidref1'] = int(sxp.child_value(info, 'ssidref', '-1')) & 0xffff + d['ssidref2'] = (int(sxp.child_value(info, 'ssidref', '-1')) >> 16) & 0xffff + print ("%(name)-16s %(dom)3d %(mem)7d %(cpu)3d %(vcpus)5d %(state)5s %(cpu_time)7.1f %(port)4s s:%(ssidref2)02x/p:%(ssidref1)02x" % d) + else: + print ("%(name)-16s %(dom)3d %(mem)7d %(cpu)3d %(vcpus)5d %(state)5s %(cpu_time)7.1f %(port)4s default" % d) def show_vcpus(self, doms): print 'Name Id VCPU CPU CPUMAP' diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py index f92c82dfe6..30900450dc 100644 --- a/tools/python/xen/xm/opts.py +++ b/tools/python/xen/xm/opts.py @@ -451,6 +451,13 @@ def set_bool(opt, k, v): else: opt.opts.err('Invalid value:' +v) +def set_u32(opt, k, v): + """Set an option to an u32 value.""" + try: + v = u32(v) + except: + opt.opts.err('Invalid value: ' + str(v)) + opt.set(v) def set_value(opt, k, v): """Set an option to a value.""" diff --git a/xen/Makefile b/xen/Makefile index e71898cf4d..15b55fb24d 100644 --- a/xen/Makefile +++ b/xen/Makefile @@ -46,6 +46,7 @@ clean: delete-unfresh-files $(MAKE) -C tools clean $(MAKE) -C common clean $(MAKE) -C drivers clean + $(MAKE) -C acm clean $(MAKE) -C arch/$(TARGET_ARCH) clean rm -f include/asm *.o $(TARGET)* *~ core rm -f include/asm-*/asm-offsets.h @@ -58,6 +59,7 @@ $(TARGET): delete-unfresh-files $(MAKE) include/asm-$(TARGET_ARCH)/asm-offsets.h $(MAKE) -C common $(MAKE) -C drivers + $(MAKE) -C acm $(MAKE) -C arch/$(TARGET_ARCH) # drivers/char/console.o may contain static banner/compile info. Blow it away. @@ -109,7 +111,7 @@ include/asm-$(TARGET_ARCH)/asm-offsets.h: arch/$(TARGET_ARCH)/asm-offsets.s .PHONY: default debug install dist clean delete-unfresh-files TAGS tags -SUBDIRS = arch/$(TARGET_ARCH) common drivers +SUBDIRS = acm arch/$(TARGET_ARCH) common drivers define all_sources ( find include/asm-$(TARGET_ARCH) -name SCCS -prune -o -name '*.h' -print; \ find include -type d -name SCCS -prune -o \( -name "asm-*" -o \ diff --git a/xen/Rules.mk b/xen/Rules.mk index 221882814a..c0b13ae368 100644 --- a/xen/Rules.mk +++ b/xen/Rules.mk @@ -35,6 +35,7 @@ OBJS += $(patsubst %.c,%.o,$(C_SRCS)) ALL_OBJS := $(BASEDIR)/common/common.o ALL_OBJS += $(BASEDIR)/drivers/char/driver.o ALL_OBJS += $(BASEDIR)/drivers/acpi/driver.o +ALL_OBJS += $(BASEDIR)/acm/acm.o ALL_OBJS += $(BASEDIR)/arch/$(TARGET_ARCH)/arch.o diff --git a/xen/acm/Makefile b/xen/acm/Makefile new file mode 100644 index 0000000000..b212041afa --- /dev/null +++ b/xen/acm/Makefile @@ -0,0 +1,15 @@ + +include $(BASEDIR)/Rules.mk +OBJS = acm_core.o +OBJS += acm_policy.o +OBJS += acm_simple_type_enforcement_hooks.o +OBJS += acm_chinesewall_hooks.o +OBJS += acm_null_hooks.o + +default: acm.o + +acm.o: $(OBJS) + $(LD) $(LDFLAGS) -r -o acm.o $(OBJS) + +clean: + rm -f *.o *~ core diff --git a/xen/acm/acm_chinesewall_hooks.c b/xen/acm/acm_chinesewall_hooks.c new file mode 100644 index 0000000000..938716d3f9 --- /dev/null +++ b/xen/acm/acm_chinesewall_hooks.c @@ -0,0 +1,503 @@ +/**************************************************************** + * acm_chinesewall_hooks.c + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * Contributions: + * Stefan Berger + * + * 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, version 2 of the + * License. + * + * sHype Chinese Wall Policy for Xen + * This code implements the hooks that are called + * throughout Xen operations and decides authorization + * based on domain types and Chinese Wall conflict type + * sets. The CHWALL policy decides if a new domain can be started + * based on the types of running domains and the type of the + * new domain to be started. If the new domain's type is in + * conflict with types of running domains, then this new domain + * is not allowed to be created. A domain can have multiple types, + * in which case all types of a new domain must be conflict-free + * with all types of already running domains. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* local cache structures for chinese wall policy */ +struct chwall_binary_policy chwall_bin_pol; + +/* + * Initializing chinese wall policy (will be filled by policy partition + * using setpolicy command) + */ +int acm_init_chwall_policy(void) +{ + /* minimal startup policy; policy write-locked already */ + chwall_bin_pol.max_types = 1; + chwall_bin_pol.max_ssidrefs = 1; + chwall_bin_pol.max_conflictsets = 1; + chwall_bin_pol.ssidrefs = (domaintype_t *)xmalloc_array(domaintype_t, chwall_bin_pol.max_ssidrefs*chwall_bin_pol.max_types); + chwall_bin_pol.conflict_sets = (domaintype_t *)xmalloc_array(domaintype_t, chwall_bin_pol.max_conflictsets*chwall_bin_pol.max_types); + chwall_bin_pol.running_types = (domaintype_t *)xmalloc_array(domaintype_t, chwall_bin_pol.max_types); + chwall_bin_pol.conflict_aggregate_set = (domaintype_t *)xmalloc_array(domaintype_t, chwall_bin_pol.max_types); + + if ((chwall_bin_pol.conflict_sets == NULL) || (chwall_bin_pol.running_types == NULL) || + (chwall_bin_pol.ssidrefs == NULL) || (chwall_bin_pol.conflict_aggregate_set == NULL)) + return ACM_INIT_SSID_ERROR; + + /* initialize state */ + memset((void *)chwall_bin_pol.ssidrefs, 0, chwall_bin_pol.max_ssidrefs*chwall_bin_pol.max_types*sizeof(domaintype_t)); + memset((void *)chwall_bin_pol.conflict_sets, 0, chwall_bin_pol.max_conflictsets*chwall_bin_pol.max_types*sizeof(domaintype_t)); + memset((void *)chwall_bin_pol.running_types, 0, chwall_bin_pol.max_types*sizeof(domaintype_t)); + memset((void *)chwall_bin_pol.conflict_aggregate_set, 0, chwall_bin_pol.max_types*sizeof(domaintype_t)); + return ACM_OK; +} + +static int +chwall_init_domain_ssid(void **chwall_ssid, ssidref_t ssidref) +{ + struct chwall_ssid *chwall_ssidp = xmalloc(struct chwall_ssid); + traceprintk("%s.\n", __func__); + if (chwall_ssidp == NULL) + return ACM_INIT_SSID_ERROR; + /* + * depending on wheter chwall is primary or secondary, get the respective + * part of the global ssidref (same way we'll get the partial ssid pointer) + */ + chwall_ssidp->chwall_ssidref = GET_SSIDREF(ACM_CHINESE_WALL_POLICY, ssidref); + if (chwall_ssidp->chwall_ssidref >= chwall_bin_pol.max_ssidrefs) { + printkd("%s: ERROR chwall_ssidref(%x) > max(%x).\n", + __func__, chwall_ssidp->chwall_ssidref, chwall_bin_pol.max_ssidrefs-1); + xfree(chwall_ssidp); + return ACM_INIT_SSID_ERROR; + } + (*chwall_ssid) = chwall_ssidp; + printkd("%s: determined chwall_ssidref to %x.\n", + __func__, chwall_ssidp->chwall_ssidref); + return ACM_OK; +} + +static void +chwall_free_domain_ssid(void *chwall_ssid) +{ + traceprintk("%s.\n", __func__); + if (chwall_ssid != NULL) + xfree(chwall_ssid); + return; +} + + +/* dump chinese wall cache; policy read-locked already */ +static int +chwall_dump_policy(u8 *buf, u16 buf_size) { + struct acm_chwall_policy_buffer *chwall_buf = (struct acm_chwall_policy_buffer *)buf; + int ret = 0; + + chwall_buf->chwall_max_types = htons(chwall_bin_pol.max_types); + chwall_buf->chwall_max_ssidrefs = htons(chwall_bin_pol.max_ssidrefs); + chwall_buf->policy_code = htons(ACM_CHINESE_WALL_POLICY); + chwall_buf->chwall_ssid_offset = htons(sizeof(struct acm_chwall_policy_buffer)); + chwall_buf->chwall_max_conflictsets = htons(chwall_bin_pol.max_conflictsets); + chwall_buf->chwall_conflict_sets_offset = + htons( + ntohs(chwall_buf->chwall_ssid_offset) + + sizeof(domaintype_t) * chwall_bin_pol.max_ssidrefs * + chwall_bin_pol.max_types); + + chwall_buf->chwall_running_types_offset = + htons( + ntohs(chwall_buf->chwall_conflict_sets_offset) + + sizeof(domaintype_t) * chwall_bin_pol.max_conflictsets * + chwall_bin_pol.max_types); + + chwall_buf->chwall_conflict_aggregate_offset = + htons( + ntohs(chwall_buf->chwall_running_types_offset) + + sizeof(domaintype_t) * chwall_bin_pol.max_types); + + ret = ntohs(chwall_buf->chwall_conflict_aggregate_offset) + + sizeof(domaintype_t) * chwall_bin_pol.max_types; + + /* now copy buffers over */ + arrcpy16((u16 *)(buf + ntohs(chwall_buf->chwall_ssid_offset)), + chwall_bin_pol.ssidrefs, + chwall_bin_pol.max_ssidrefs * chwall_bin_pol.max_types); + + arrcpy16((u16 *)(buf + ntohs(chwall_buf->chwall_conflict_sets_offset)), + chwall_bin_pol.conflict_sets, + chwall_bin_pol.max_conflictsets * chwall_bin_pol.max_types); + + arrcpy16((u16 *)(buf + ntohs(chwall_buf->chwall_running_types_offset)), + chwall_bin_pol.running_types, + chwall_bin_pol.max_types); + + arrcpy16((u16 *)(buf + ntohs(chwall_buf->chwall_conflict_aggregate_offset)), + chwall_bin_pol.conflict_aggregate_set, + chwall_bin_pol.max_types); + return ret; +} + +/* adapt security state (running_types and conflict_aggregate_set) to all running + * domains; chwall_init_state is called when a policy is changed to bring the security + * information into a consistent state and to detect violations (return != 0). + * from a security point of view, we simulate that all running domains are re-started + */ +static int +chwall_init_state(struct acm_chwall_policy_buffer *chwall_buf, domaintype_t *ssidrefs, domaintype_t *conflict_sets, + domaintype_t *running_types, domaintype_t *conflict_aggregate_set) +{ + int violation = 0, i, j; + struct chwall_ssid *chwall_ssid; + ssidref_t chwall_ssidref; + struct domain **pd; + + write_lock(&domlist_lock); + /* go through all domains and adjust policy as if this domain was started now */ + pd = &domain_list; + for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { + chwall_ssid = GET_SSIDP(ACM_CHINESE_WALL_POLICY, (struct acm_ssid_domain *)(*pd)->ssid); + chwall_ssidref = chwall_ssid->chwall_ssidref; + traceprintk("%s: validating policy for domain %x (chwall-REF=%x).\n", + __func__, (*pd)->domain_id, chwall_ssidref); + /* a) adjust types ref-count for running domains */ + for (i=0; i< chwall_buf->chwall_max_types; i++) + running_types[i] += + ssidrefs[chwall_ssidref*chwall_buf->chwall_max_types + i]; + + /* b) check for conflict */ + for (i=0; i< chwall_buf->chwall_max_types; i++) + if (conflict_aggregate_set[i] && + ssidrefs[chwall_ssidref*chwall_buf->chwall_max_types + i]) { + printk("%s: CHINESE WALL CONFLICT in type %02x.\n", __func__, i); + violation = 1; + goto out; + } + /* set violation and break out of the loop */ + /* c) adapt conflict aggregate set for this domain (notice conflicts) */ + for (i=0; ichwall_max_conflictsets; i++) { + int common = 0; + /* check if conflict_set_i and ssidref have common types */ + for (j=0; jchwall_max_types; j++) + if (conflict_sets[i*chwall_buf->chwall_max_types + j] && + ssidrefs[chwall_ssidref*chwall_buf->chwall_max_types + j]) { + common = 1; + break; + } + if (common == 0) + continue; /* try next conflict set */ + /* now add types of the conflict set to conflict_aggregate_set (except types in chwall_ssidref) */ + for (j=0; jchwall_max_types; j++) + if (conflict_sets[i*chwall_buf->chwall_max_types + j] && + !ssidrefs[chwall_ssidref*chwall_buf->chwall_max_types + j]) + conflict_aggregate_set[j]++; + } + } + out: + write_unlock(&domlist_lock); + return violation; + /* returning "violation != 0" means that the currently running set of domains would + * not be possible if the new policy had been enforced before starting them; for chinese + * wall, this means that the new policy includes at least one conflict set of which + * more than one type is currently running */ +} + +static int +chwall_set_policy(u8 *buf, u16 buf_size) +{ + /* policy write-locked already */ + struct acm_chwall_policy_buffer *chwall_buf = (struct acm_chwall_policy_buffer *)buf; + void *ssids = NULL, *conflict_sets = NULL, *running_types = NULL, *conflict_aggregate_set = NULL; + + /* rewrite the policy due to endianess */ + chwall_buf->policy_code = ntohs(chwall_buf->policy_code); + chwall_buf->chwall_max_types = ntohs(chwall_buf->chwall_max_types); + chwall_buf->chwall_max_ssidrefs = ntohs(chwall_buf->chwall_max_ssidrefs); + chwall_buf->chwall_max_conflictsets = ntohs(chwall_buf->chwall_max_conflictsets); + chwall_buf->chwall_ssid_offset = ntohs(chwall_buf->chwall_ssid_offset); + chwall_buf->chwall_conflict_sets_offset = ntohs(chwall_buf->chwall_conflict_sets_offset); + chwall_buf->chwall_running_types_offset = ntohs(chwall_buf->chwall_running_types_offset); + chwall_buf->chwall_conflict_aggregate_offset = ntohs(chwall_buf->chwall_conflict_aggregate_offset); + + /* 1. allocate new buffers */ + ssids = xmalloc_array(domaintype_t, chwall_buf->chwall_max_types*chwall_buf->chwall_max_ssidrefs); + conflict_sets = xmalloc_array(domaintype_t, chwall_buf->chwall_max_conflictsets*chwall_buf->chwall_max_types); + running_types = xmalloc_array(domaintype_t,chwall_buf->chwall_max_types); + conflict_aggregate_set = xmalloc_array(domaintype_t, chwall_buf->chwall_max_types); + + if ((ssids == NULL)||(conflict_sets == NULL)||(running_types == NULL)||(conflict_aggregate_set == NULL)) + goto error_free; + + /* 2. set new policy */ + if (chwall_buf->chwall_ssid_offset + sizeof(domaintype_t) * + chwall_buf->chwall_max_types * chwall_buf->chwall_max_ssidrefs > buf_size) + goto error_free; + arrcpy(ssids, buf + chwall_buf->chwall_ssid_offset, + sizeof(domaintype_t), + chwall_buf->chwall_max_types * chwall_buf->chwall_max_ssidrefs); + + if (chwall_buf->chwall_conflict_sets_offset + sizeof(domaintype_t) * + chwall_buf->chwall_max_types * chwall_buf->chwall_max_conflictsets > buf_size) + goto error_free; + + arrcpy(conflict_sets, buf + chwall_buf->chwall_conflict_sets_offset, + sizeof(domaintype_t), + chwall_buf->chwall_max_types * chwall_buf->chwall_max_conflictsets); + + /* we also use new state buffers since max_types can change */ + memset(running_types, 0, sizeof(domaintype_t)*chwall_buf->chwall_max_types); + memset(conflict_aggregate_set, 0, sizeof(domaintype_t)*chwall_buf->chwall_max_types); + + /* 3. now re-calculate the state for the new policy based on running domains; + * this can fail if new policy is conflicting with running domains */ + if (chwall_init_state(chwall_buf, ssids, conflict_sets, running_types, conflict_aggregate_set)) { + printk("%s: New policy conflicts with running domains. Policy load aborted.\n", __func__); + goto error_free; /* new policy conflicts with running domains */ + } + /* 4. free old policy buffers, replace with new ones */ + chwall_bin_pol.max_types = chwall_buf->chwall_max_types; + chwall_bin_pol.max_ssidrefs = chwall_buf->chwall_max_ssidrefs; + chwall_bin_pol.max_conflictsets = chwall_buf->chwall_max_conflictsets; + if (chwall_bin_pol.ssidrefs != NULL) + xfree(chwall_bin_pol.ssidrefs); + if (chwall_bin_pol.conflict_aggregate_set != NULL) + xfree(chwall_bin_pol.conflict_aggregate_set); + if (chwall_bin_pol.running_types != NULL) + xfree(chwall_bin_pol.running_types); + if (chwall_bin_pol.conflict_sets != NULL) + xfree(chwall_bin_pol.conflict_sets); + chwall_bin_pol.ssidrefs = ssids; + chwall_bin_pol.conflict_aggregate_set = conflict_aggregate_set; + chwall_bin_pol.running_types = running_types; + chwall_bin_pol.conflict_sets = conflict_sets; + return ACM_OK; + +error_free: + printk("%s: ERROR setting policy.\n", __func__); + if (ssids != NULL) xfree(ssids); + if (conflict_sets != NULL) xfree(conflict_sets); + if (running_types != NULL) xfree(running_types); + if (conflict_aggregate_set != NULL) xfree(conflict_aggregate_set); + return -EFAULT; +} + +static int +chwall_dump_stats(u8 *buf, u16 len) +{ + /* no stats for Chinese Wall Policy */ + return 0; +} + +/*************************** + * Authorization functions + ***************************/ + + +/* -------- DOMAIN OPERATION HOOKS -----------*/ + +static int +chwall_pre_domain_create(void *subject_ssid, ssidref_t ssidref) +{ + ssidref_t chwall_ssidref; + int i,j; + traceprintk("%s.\n", __func__); + + read_lock(&acm_bin_pol_rwlock); + chwall_ssidref = GET_SSIDREF(ACM_CHINESE_WALL_POLICY, ssidref); + if (chwall_ssidref == ACM_DEFAULT_LOCAL_SSID) { + printk("%s: ERROR CHWALL SSID is NOT SET but policy enforced.\n", __func__); + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_DENIED; /* catching and indicating config error */ + } + if (chwall_ssidref >= chwall_bin_pol.max_ssidrefs) { + printk("%s: ERROR chwall_ssidref > max(%x).\n", + __func__, chwall_bin_pol.max_ssidrefs-1); + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_DENIED; + } + /* A: chinese wall check for conflicts */ + for (i=0; i< chwall_bin_pol.max_types; i++) + if (chwall_bin_pol.conflict_aggregate_set[i] && + chwall_bin_pol.ssidrefs[chwall_ssidref*chwall_bin_pol.max_types + i]) { + printk("%s: CHINESE WALL CONFLICT in type %02x.\n", __func__, i); + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_DENIED; + } + + /* B: chinese wall conflict set adjustment (so that other + * other domains simultaneously created are evaluated against this new set)*/ + for (i=0; ichwall_ssidref; + + traceprintk("%s.\n", __func__); + + read_lock(&acm_bin_pol_rwlock); + /* adjust running types set */ + for (i=0; i< chwall_bin_pol.max_types; i++) + chwall_bin_pol.running_types[i] -= + chwall_bin_pol.ssidrefs[chwall_ssidref*chwall_bin_pol.max_types + i]; + + /* roll-back: re-adjust conflicting types aggregate */ + for (i=0; i + * + * 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, version 2 of the + * License. + * + * sHype access control module (ACM) + * This file handles initialization of the ACM + * as well as initializing/freeing security + * identifiers for domains (it calls on active + * policy hook functions). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* debug: + * include/acm/acm_hooks.h defines a constant ACM_TRACE_MODE; + * define/undefine this constant to receive / suppress any + * security hook debug output of sHype + * + * include/public/acm.h defines a constant ACM_DEBUG + * define/undefine this constant to receive non-hook-related + * debug output. + */ + +/* function prototypes */ +void acm_init_chwall_policy(void); +void acm_init_ste_policy(void); + +extern struct acm_operations acm_chinesewall_ops, + acm_simple_type_enforcement_ops, acm_null_ops; + +/* global ops structs called by the hooks */ +struct acm_operations *acm_primary_ops = NULL; +/* called in hook if-and-only-if primary succeeds */ +struct acm_operations *acm_secondary_ops = NULL; + +/* acm global binary policy (points to 'local' primary and secondary policies */ +struct acm_binary_policy acm_bin_pol; +/* acm binary policy lock */ +rwlock_t acm_bin_pol_rwlock = RW_LOCK_UNLOCKED; + +/* until we have endian support in Xen, we discover it at runtime */ +u8 little_endian = 1; +void acm_set_endian(void) +{ + u32 test = 1; + if (*((u8 *)&test) == 1) { + printk("ACM module running in LITTLE ENDIAN.\n"); + little_endian = 1; + } else { + printk("ACM module running in BIG ENDIAN.\n"); + little_endian = 0; + } +} + +/* initialize global security policy for Xen; policy write-locked already */ +static void +acm_init_binary_policy(void *primary, void *secondary) +{ + acm_bin_pol.primary_policy_code = 0; + acm_bin_pol.secondary_policy_code = 0; + acm_bin_pol.primary_binary_policy = primary; + acm_bin_pol.secondary_binary_policy = secondary; +} + +int +acm_init(void) +{ + int ret = -EINVAL; + + acm_set_endian(); + write_lock(&acm_bin_pol_rwlock); + + if (ACM_USE_SECURITY_POLICY == ACM_CHINESE_WALL_POLICY) { + acm_init_binary_policy(NULL, NULL); + acm_init_chwall_policy(); + acm_bin_pol.primary_policy_code = ACM_CHINESE_WALL_POLICY; + acm_primary_ops = &acm_chinesewall_ops; + acm_bin_pol.secondary_policy_code = ACM_NULL_POLICY; + acm_secondary_ops = &acm_null_ops; + ret = ACM_OK; + } else if (ACM_USE_SECURITY_POLICY == ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) { + acm_init_binary_policy(NULL, NULL); + acm_init_ste_policy(); + acm_bin_pol.primary_policy_code = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY; + acm_primary_ops = &acm_simple_type_enforcement_ops; + acm_bin_pol.secondary_policy_code = ACM_NULL_POLICY; + acm_secondary_ops = &acm_null_ops; + ret = ACM_OK; + } else if (ACM_USE_SECURITY_POLICY == ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY) { + acm_init_binary_policy(NULL, NULL); + acm_init_chwall_policy(); + acm_init_ste_policy(); + acm_bin_pol.primary_policy_code = ACM_CHINESE_WALL_POLICY; + acm_primary_ops = &acm_chinesewall_ops; + acm_bin_pol.secondary_policy_code = ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY; + acm_secondary_ops = &acm_simple_type_enforcement_ops; + ret = ACM_OK; + } else if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) { + acm_init_binary_policy(NULL, NULL); + acm_bin_pol.primary_policy_code = ACM_NULL_POLICY; + acm_primary_ops = &acm_null_ops; + acm_bin_pol.secondary_policy_code = ACM_NULL_POLICY; + acm_secondary_ops = &acm_null_ops; + ret = ACM_OK; + } + write_unlock(&acm_bin_pol_rwlock); + + if (ret != ACM_OK) + return -EINVAL; + printk("%s: Enforcing Primary %s, Secondary %s.\n", __func__, + ACM_POLICY_NAME(acm_bin_pol.primary_policy_code), ACM_POLICY_NAME(acm_bin_pol.secondary_policy_code)); + return ACM_OK; +} + + +int +acm_init_domain_ssid(domid_t id, ssidref_t ssidref) +{ + struct acm_ssid_domain *ssid; + struct domain *subj = find_domain_by_id(id); + int ret1, ret2; + + if (subj == NULL) { + printk("%s: ACM_NULL_POINTER ERROR (id=%x).\n", __func__, id); + return ACM_NULL_POINTER_ERROR; + } + if ((ssid = xmalloc(struct acm_ssid_domain)) == NULL) + return ACM_INIT_SSID_ERROR; + + ssid->datatype = DOMAIN; + ssid->subject = subj; + ssid->domainid = subj->domain_id; + ssid->primary_ssid = NULL; + ssid->secondary_ssid = NULL; + + if (ACM_USE_SECURITY_POLICY != ACM_NULL_POLICY) + ssid->ssidref = ssidref; + else + ssid->ssidref = ACM_DEFAULT_SSID; + + subj->ssid = ssid; + /* now fill in primary and secondary parts; we only get here through hooks */ + if (acm_primary_ops->init_domain_ssid != NULL) + ret1 = acm_primary_ops->init_domain_ssid(&(ssid->primary_ssid), ssidref); + else + ret1 = ACM_OK; + + if (acm_secondary_ops->init_domain_ssid != NULL) + ret2 = acm_secondary_ops->init_domain_ssid(&(ssid->secondary_ssid), ssidref); + else + ret2 = ACM_OK; + + if ((ret1 != ACM_OK) || (ret2 != ACM_OK)) { + printk("%s: ERROR instantiating individual ssids for domain 0x%02x.\n", + __func__, subj->domain_id); + acm_free_domain_ssid(ssid); + put_domain(subj); + return ACM_INIT_SSID_ERROR; + } + printk("%s: assigned domain %x the ssidref=%x.\n", __func__, id, ssid->ssidref); + put_domain(subj); + return ACM_OK; +} + + +int +acm_free_domain_ssid(struct acm_ssid_domain *ssid) +{ + domid_t id; + + /* domain is already gone, just ssid is left */ + if (ssid == NULL) { + printk("%s: ACM_NULL_POINTER ERROR.\n", __func__); + return ACM_NULL_POINTER_ERROR; + } + id = ssid->domainid; + ssid->subject = NULL; + + if (acm_primary_ops->free_domain_ssid != NULL) /* null policy */ + acm_primary_ops->free_domain_ssid(ssid->primary_ssid); + ssid->primary_ssid = NULL; + if (acm_secondary_ops->free_domain_ssid != NULL) + acm_secondary_ops->free_domain_ssid(ssid->secondary_ssid); + ssid->secondary_ssid = NULL; + xfree(ssid); + printkd("%s: Freed individual domain ssid (domain=%02x).\n",__func__, id); + return ACM_OK; +} diff --git a/xen/acm/acm_null_hooks.c b/xen/acm/acm_null_hooks.c new file mode 100644 index 0000000000..6433cbfed6 --- /dev/null +++ b/xen/acm/acm_null_hooks.c @@ -0,0 +1,76 @@ +/**************************************************************** + * acm_null_hooks.c + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * 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, version 2 of the + * License. + */ +#include + +static int +null_init_domain_ssid(void **chwall_ssid, ssidref_t ssidref) +{ + return ACM_OK; +} + + +static void +null_free_domain_ssid(void *chwall_ssid) +{ + return; +} + + +static int +null_dump_binary_policy(u8 *buf, u16 buf_size) +{ + return 0; +} + + + +static int +null_set_binary_policy(u8 *buf, u16 buf_size) +{ + return -1; +} + + +static int +null_dump_stats(u8 *buf, u16 buf_size) +{ + /* no stats for NULL policy */ + return 0; +} + + +/* now define the hook structure similarly to LSM */ +struct acm_operations acm_null_ops = { + .init_domain_ssid = null_init_domain_ssid, + .free_domain_ssid = null_free_domain_ssid, + .dump_binary_policy = null_dump_binary_policy, + .set_binary_policy = null_set_binary_policy, + .dump_statistics = null_dump_stats, + /* domain management control hooks */ + .pre_domain_create = NULL, + .post_domain_create = NULL, + .fail_domain_create = NULL, + .post_domain_destroy = NULL, + /* event channel control hooks */ + .pre_eventchannel_unbound = NULL, + .fail_eventchannel_unbound = NULL, + .pre_eventchannel_interdomain = NULL, + .fail_eventchannel_interdomain = NULL, + /* grant table control hooks */ + .pre_grant_map_ref = NULL, + .fail_grant_map_ref = NULL, + .pre_grant_setup = NULL, + .fail_grant_setup = NULL + +}; diff --git a/xen/acm/acm_policy.c b/xen/acm/acm_policy.c new file mode 100644 index 0000000000..3e08130d43 --- /dev/null +++ b/xen/acm/acm_policy.c @@ -0,0 +1,197 @@ +/**************************************************************** + * acm_policy.c + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * Contributions: + * Stefan Berger + * support for network-byte-order binary policies + * + * 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, version 2 of the + * License. + * + * sHype access control policy management for Xen. + * This interface allows policy tools in authorized + * domains to interact with the Xen access control module + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int +acm_set_policy(void *buf, u16 buf_size, u16 policy) +{ + u8 *policy_buffer = NULL; + struct acm_policy_buffer *pol; + + if (policy != ACM_USE_SECURITY_POLICY) { + printk("%s: Loading incompatible policy (running: %s).\n", __func__, + ACM_POLICY_NAME(ACM_USE_SECURITY_POLICY)); + return -EFAULT; + } + /* now check correct buffer sizes for policy combinations */ + if (policy == ACM_NULL_POLICY) { + printkd("%s: NULL Policy, no policy needed.\n", __func__); + goto out; + } + if (buf_size < sizeof(struct acm_policy_buffer)) + return -EFAULT; + /* 1. copy buffer from domain */ + if ((policy_buffer = xmalloc_array(u8, buf_size)) == NULL) + goto error_free; + if (copy_from_user(policy_buffer, buf, buf_size)) { + printk("%s: Error copying!\n",__func__); + goto error_free; + } + /* 2. some sanity checking */ + pol = (struct acm_policy_buffer *)policy_buffer; + + if ((ntohl(pol->magic) != ACM_MAGIC) || + (ntohs(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) || + (ntohs(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code)) { + printkd("%s: Wrong policy magics!\n", __func__); + goto error_free; + } + if (buf_size != ntohl(pol->len)) { + printk("%s: ERROR in buf size.\n", __func__); + goto error_free; + } + + /* get bin_policy lock and rewrite policy (release old one) */ + write_lock(&acm_bin_pol_rwlock); + + /* 3. now get/set primary policy data */ + if (acm_primary_ops->set_binary_policy(buf + ntohs(pol->primary_buffer_offset), + ntohs(pol->secondary_buffer_offset) - + ntohs(pol->primary_buffer_offset))) { + goto error_lock_free; + } + /* 4. now get/set secondary policy data */ + if (acm_secondary_ops->set_binary_policy(buf + ntohs(pol->secondary_buffer_offset), + ntohl(pol->len) - + ntohs(pol->secondary_buffer_offset))) { + goto error_lock_free; + } + write_unlock(&acm_bin_pol_rwlock); + out: + printk("%s: Done .\n", __func__); + if (policy_buffer != NULL) + xfree(policy_buffer); + return ACM_OK; + + error_lock_free: + write_unlock(&acm_bin_pol_rwlock); + error_free: + printk("%s: Error setting policy.\n", __func__); + if (policy_buffer != NULL) + xfree(policy_buffer); + return -ENOMEM; +} + +int +acm_get_policy(void *buf, u16 buf_size) +{ + u8 *policy_buffer; + int ret; + struct acm_policy_buffer *bin_pol; + + if ((policy_buffer = xmalloc_array(u8, buf_size)) == NULL) + return -ENOMEM; + + read_lock(&acm_bin_pol_rwlock); + /* future: read policy from file and set it */ + bin_pol = (struct acm_policy_buffer *)policy_buffer; + bin_pol->magic = htonl(ACM_MAGIC); + bin_pol->policyversion = htonl(POLICY_INTERFACE_VERSION); + bin_pol->primary_policy_code = htons(acm_bin_pol.primary_policy_code); + bin_pol->secondary_policy_code = htons(acm_bin_pol.secondary_policy_code); + + bin_pol->len = htonl(sizeof(struct acm_policy_buffer)); + bin_pol->primary_buffer_offset = htons(ntohl(bin_pol->len)); + bin_pol->secondary_buffer_offset = htons(ntohl(bin_pol->len)); + + ret = acm_primary_ops->dump_binary_policy (policy_buffer + ntohs(bin_pol->primary_buffer_offset), + buf_size - ntohs(bin_pol->primary_buffer_offset)); + if (ret < 0) { + printk("%s: ERROR creating chwallpolicy buffer.\n", __func__); + read_unlock(&acm_bin_pol_rwlock); + return -1; + } + bin_pol->len = htonl(ntohl(bin_pol->len) + ret); + bin_pol->secondary_buffer_offset = htons(ntohl(bin_pol->len)); + + ret = acm_secondary_ops->dump_binary_policy(policy_buffer + ntohs(bin_pol->secondary_buffer_offset), + buf_size - ntohs(bin_pol->secondary_buffer_offset)); + if (ret < 0) { + printk("%s: ERROR creating chwallpolicy buffer.\n", __func__); + read_unlock(&acm_bin_pol_rwlock); + return -1; + } + bin_pol->len = htonl(ntohl(bin_pol->len) + ret); + read_unlock(&acm_bin_pol_rwlock); + if (copy_to_user(buf, policy_buffer, ntohl(bin_pol->len))) + return -EFAULT; + xfree(policy_buffer); + return ACM_OK; +} + +int +acm_dump_statistics(void *buf, u16 buf_size) +{ + /* send stats to user space */ + u8 *stats_buffer; + int len1, len2; + struct acm_stats_buffer acm_stats; + + if ((stats_buffer = xmalloc_array(u8, buf_size)) == NULL) + return -ENOMEM; + + read_lock(&acm_bin_pol_rwlock); + + len1 = acm_primary_ops->dump_statistics(stats_buffer + sizeof(struct acm_stats_buffer), + buf_size - sizeof(struct acm_stats_buffer)); + if (len1 < 0) + goto error_lock_free; + + len2 = acm_secondary_ops->dump_statistics(stats_buffer + sizeof(struct acm_stats_buffer) + len1, + buf_size - sizeof(struct acm_stats_buffer) - len1); + if (len2 < 0) + goto error_lock_free; + + acm_stats.magic = htonl(ACM_MAGIC); + acm_stats.policyversion = htonl(POLICY_INTERFACE_VERSION); + acm_stats.primary_policy_code = htons(acm_bin_pol.primary_policy_code); + acm_stats.secondary_policy_code = htons(acm_bin_pol.secondary_policy_code); + acm_stats.primary_stats_offset = htons(sizeof(struct acm_stats_buffer)); + acm_stats.secondary_stats_offset = htons(sizeof(struct acm_stats_buffer) + len1); + acm_stats.len = htonl(sizeof(struct acm_stats_buffer) + len1 + len2); + memcpy(stats_buffer, &acm_stats, sizeof(struct acm_stats_buffer)); + + if (copy_to_user(buf, stats_buffer, sizeof(struct acm_stats_buffer) + len1 + len2)) + goto error_lock_free; + + read_unlock(&acm_bin_pol_rwlock); + xfree(stats_buffer); + return ACM_OK; + + error_lock_free: + read_unlock(&acm_bin_pol_rwlock); + xfree(stats_buffer); + return -EFAULT; +} + +/*eof*/ diff --git a/xen/acm/acm_simple_type_enforcement_hooks.c b/xen/acm/acm_simple_type_enforcement_hooks.c new file mode 100644 index 0000000000..17e75deca8 --- /dev/null +++ b/xen/acm/acm_simple_type_enforcement_hooks.c @@ -0,0 +1,638 @@ +/**************************************************************** + * acm_simple_type_enforcement_hooks.c + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * Contributors: + * Stefan Berger + * support for network order binary policies + * + * 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, version 2 of the + * License. + * + * sHype Simple Type Enforcement for Xen + * STE allows to control which domains can setup sharing + * (eventchannels right now) with which other domains. Hooks + * are defined and called throughout Xen when domains bind to + * shared resources (setup eventchannels) and a domain is allowed + * to setup sharing with another domain if and only if both domains + * share at least on common type. + * + */ +#include +#include +#include +#include +#include +#include + +/* local cache structures for chinese wall policy */ +struct ste_binary_policy ste_bin_pol; + +static inline int have_common_type (ssidref_t ref1, ssidref_t ref2) { + int i; + for(i=0; i< ste_bin_pol.max_types; i++) + if ( ste_bin_pol.ssidrefs[ref1*ste_bin_pol.max_types + i] && + ste_bin_pol.ssidrefs[ref2*ste_bin_pol.max_types + i]) { + printkd("%s: common type #%02x.\n", __func__, i); + return 1; + } + return 0; +} + +/* Helper function: return = (subj and obj share a common type) */ +static int share_common_type(struct domain *subj, struct domain *obj) +{ + ssidref_t ref_s, ref_o; + int ret; + + if ((subj == NULL) || (obj == NULL) || (subj->ssid == NULL) || (obj->ssid == NULL)) + return 0; + read_lock(&acm_bin_pol_rwlock); + /* lookup the policy-local ssids */ + ref_s = ((struct ste_ssid *)(GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)subj->ssid)))->ste_ssidref; + ref_o = ((struct ste_ssid *)(GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)obj->ssid)))->ste_ssidref; + /* check whether subj and obj share a common ste type */ + ret = have_common_type(ref_s, ref_o); + read_unlock(&acm_bin_pol_rwlock); + return ret; +} + +/* + * Initializing chinese wall policy (will be filled by policy partition + * using setpolicy command) + */ +int acm_init_ste_policy(void) +{ + /* minimal startup policy; policy write-locked already */ + ste_bin_pol.max_types = 1; + ste_bin_pol.max_ssidrefs = 1; + ste_bin_pol.ssidrefs = (domaintype_t *)xmalloc_array(domaintype_t, 1); + + if (ste_bin_pol.ssidrefs == NULL) + return ACM_INIT_SSID_ERROR; + + /* initialize state */ + ste_bin_pol.ssidrefs[0] = 1; + + /* init stats */ + atomic_set(&(ste_bin_pol.ec_eval_count), 0); + atomic_set(&(ste_bin_pol.ec_denied_count), 0); + atomic_set(&(ste_bin_pol.ec_cachehit_count), 0); + atomic_set(&(ste_bin_pol.gt_eval_count), 0); + atomic_set(&(ste_bin_pol.gt_denied_count), 0); + atomic_set(&(ste_bin_pol.gt_cachehit_count), 0); + return ACM_OK; +} + + +/* ste initialization function hooks */ +static int +ste_init_domain_ssid(void **ste_ssid, ssidref_t ssidref) +{ + int i; + struct ste_ssid *ste_ssidp = xmalloc(struct ste_ssid); + traceprintk("%s.\n", __func__); + + if (ste_ssidp == NULL) + return ACM_INIT_SSID_ERROR; + + /* get policy-local ssid reference */ + ste_ssidp->ste_ssidref = GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref); + if (ste_ssidp->ste_ssidref >= ste_bin_pol.max_ssidrefs) { + printkd("%s: ERROR ste_ssidref (%x) > max(%x).\n", + __func__, ste_ssidp->ste_ssidref, ste_bin_pol.max_ssidrefs-1); + xfree(ste_ssidp); + return ACM_INIT_SSID_ERROR; + } + /* clean ste cache */ + for (i=0; iste_cache[i].valid = FREE; + + (*ste_ssid) = ste_ssidp; + printkd("%s: determined ste_ssidref to %x.\n", + __func__, ste_ssidp->ste_ssidref); + return ACM_OK; +} + + +static void +ste_free_domain_ssid(void *ste_ssid) +{ + traceprintk("%s.\n", __func__); + if (ste_ssid != NULL) + xfree(ste_ssid); + return; +} + +/* dump type enforcement cache; policy read-locked already */ +static int +ste_dump_policy(u8 *buf, u16 buf_size) { + struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf; + int ret = 0; + + ste_buf->ste_max_types = htons(ste_bin_pol.max_types); + ste_buf->ste_max_ssidrefs = htons(ste_bin_pol.max_ssidrefs); + ste_buf->policy_code = htons(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); + ste_buf->ste_ssid_offset = htons(sizeof(struct acm_ste_policy_buffer)); + ret = ntohs(ste_buf->ste_ssid_offset) + + sizeof(domaintype_t)*ste_bin_pol.max_ssidrefs*ste_bin_pol.max_types; + + /* now copy buffer over */ + arrcpy(buf + ntohs(ste_buf->ste_ssid_offset), + ste_bin_pol.ssidrefs, + sizeof(domaintype_t), + ste_bin_pol.max_ssidrefs*ste_bin_pol.max_types); + + return ret; +} + +/* ste_init_state is called when a policy is changed to detect violations (return != 0). + * from a security point of view, we simulate that all running domains are re-started and + * all sharing decisions are replayed to detect violations or current sharing behavior + * (right now: event_channels, future: also grant_tables) + */ +static int +ste_init_state(struct acm_ste_policy_buffer *ste_buf, domaintype_t *ssidrefs) +{ + int violation = 1; + struct ste_ssid *ste_ssid, *ste_rssid; + ssidref_t ste_ssidref, ste_rssidref; + struct domain **pd, *rdom; + domid_t rdomid; + grant_entry_t sha_copy; + int port, i; + + read_lock(&domlist_lock); /* go by domain? or directly by global? event/grant list */ + /* go through all domains and adjust policy as if this domain was started now */ + pd = &domain_list; + for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(*pd)->ssid); + ste_ssidref = ste_ssid->ste_ssidref; + traceprintk("%s: validating policy for eventch domain %x (ste-Ref=%x).\n", + __func__, (*pd)->domain_id, ste_ssidref); + /* a) check for event channel conflicts */ + for (port=0; port < NR_EVTCHN_BUCKETS; port++) { + spin_lock(&(*pd)->evtchn_lock); + if ((*pd)->evtchn[port] == NULL) { + spin_unlock(&(*pd)->evtchn_lock); + continue; + } + if ((*pd)->evtchn[port]->state == ECS_INTERDOMAIN) { + rdom = (*pd)->evtchn[port]->u.interdomain.remote_dom; + rdomid = rdom->domain_id; + /* rdom now has remote domain */ + ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(rdom->ssid)); + ste_rssidref = ste_rssid->ste_ssidref; + } else if ((*pd)->evtchn[port]->state == ECS_UNBOUND) { + rdomid = (*pd)->evtchn[port]->u.unbound.remote_domid; + if ((rdom = find_domain_by_id(rdomid)) == NULL) { + printk("%s: Error finding domain to id %x!\n", __func__, rdomid); + goto out; + } + /* rdom now has remote domain */ + ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(rdom->ssid)); + ste_rssidref = ste_rssid->ste_ssidref; + put_domain(rdom); + } else { + spin_unlock(&(*pd)->evtchn_lock); + continue; /* port unused */ + } + spin_unlock(&(*pd)->evtchn_lock); + + /* rdom now has remote domain */ + ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(rdom->ssid)); + ste_rssidref = ste_rssid->ste_ssidref; + traceprintk("%s: eventch: domain %x (ssidref %x) --> domain %x (rssidref %x) used (port %x).\n", + __func__, (*pd)->domain_id, ste_ssidref, rdom->domain_id, ste_rssidref, port); + /* check whether on subj->ssid, obj->ssid share a common type*/ + if (!have_common_type(ste_ssidref, ste_rssidref)) { + printkd("%s: Policy violation in event channel domain %x -> domain %x.\n", + __func__, (*pd)->domain_id, rdomid); + goto out; + } + } + /* b) check for grant table conflicts on shared pages */ + if ((*pd)->grant_table->shared == NULL) { + printkd("%s: Grant ... sharing for domain %x not setup!\n", __func__, (*pd)->domain_id); + continue; + } + for ( i = 0; i < NR_GRANT_ENTRIES; i++ ) { + sha_copy = (*pd)->grant_table->shared[i]; + if ( sha_copy.flags ) { + printkd("%s: grant dom (%hu) SHARED (%d) flags:(%hx) dom:(%hu) frame:(%lx)\n", + __func__, (*pd)->domain_id, i, sha_copy.flags, sha_copy.domid, + (unsigned long)sha_copy.frame); + rdomid = sha_copy.domid; + if ((rdom = find_domain_by_id(rdomid)) == NULL) { + printkd("%s: domain not found ERROR!\n", __func__); + goto out; + }; + /* rdom now has remote domain */ + ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(rdom->ssid)); + ste_rssidref = ste_rssid->ste_ssidref; + put_domain(rdom); + if (!have_common_type(ste_ssidref, ste_rssidref)) { + printkd("%s: Policy violation in grant table sharing domain %x -> domain %x.\n", + __func__, (*pd)->domain_id, rdomid); + goto out; + } + } + } + } + violation = 0; + out: + read_unlock(&domlist_lock); + return violation; + /* returning "violation != 0" means that existing sharing between domains would not + * have been allowed if the new policy had been enforced before the sharing; for ste, + * this means that there are at least 2 domains that have established sharing through + * event-channels or grant-tables but these two domains don't have no longer a common + * type in their typesets referenced by their ssidrefs */ +} + +/* set new policy; policy write-locked already */ +static int +ste_set_policy(u8 *buf, u16 buf_size) +{ + struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf; + void *ssidrefsbuf; + struct ste_ssid *ste_ssid; + struct domain **pd; + int i; + + /* Convert endianess of policy */ + ste_buf->policy_code = ntohs(ste_buf->policy_code); + ste_buf->ste_max_types = ntohs(ste_buf->ste_max_types); + ste_buf->ste_max_ssidrefs = ntohs(ste_buf->ste_max_ssidrefs); + ste_buf->ste_ssid_offset = ntohs(ste_buf->ste_ssid_offset); + + /* 1. create and copy-in new ssidrefs buffer */ + ssidrefsbuf = xmalloc_array(u8, sizeof(domaintype_t)*ste_buf->ste_max_types*ste_buf->ste_max_ssidrefs); + if (ssidrefsbuf == NULL) { + return -ENOMEM; + } + if (ste_buf->ste_ssid_offset + sizeof(domaintype_t) * ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types > buf_size) + goto error_free; + + arrcpy(ssidrefsbuf, + buf + ste_buf->ste_ssid_offset, + sizeof(domaintype_t), + ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types); + + /* 2. now re-calculate sharing decisions based on running domains; + * this can fail if new policy is conflicting with sharing of running domains + * now: reject violating new policy; future: adjust sharing through revoking sharing */ + if (ste_init_state(ste_buf, (domaintype_t *)ssidrefsbuf)) { + printk("%s: New policy conflicts with running domains. Policy load aborted.\n", __func__); + goto error_free; /* new policy conflicts with sharing of running domains */ + } + /* 3. replace old policy (activate new policy) */ + ste_bin_pol.max_types = ste_buf->ste_max_types; + ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs; + if (ste_bin_pol.ssidrefs) + xfree(ste_bin_pol.ssidrefs); + ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf; + + /* clear all ste caches */ + read_lock(&domlist_lock); + pd = &domain_list; + for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(*pd)->ssid); + for (i=0; iste_cache[i].valid = FREE; + } + read_unlock(&domlist_lock); + return ACM_OK; + +error_free: + printk("%s: ERROR setting policy.\n", __func__); + if (ssidrefsbuf != NULL) xfree(ssidrefsbuf); + return -EFAULT; +} + +static int +ste_dump_stats(u8 *buf, u16 buf_len) +{ + struct acm_ste_stats_buffer stats; + +#ifdef ACM_DEBUG + int i; + struct ste_ssid *ste_ssid; + struct domain **pd; + + printk("ste: Decision caches:\n"); + /* go through all domains and adjust policy as if this domain was started now */ + read_lock(&domlist_lock); /* go by domain? or directly by global? event/grant list */ + pd = &domain_list; + for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { + printk("ste: Cache Domain %02x.\n", (*pd)->domain_id); + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(*pd)->ssid); + for (i=0; iste_cache[i].valid == VALID) ? + "VALID" : "FREE", + (ste_ssid->ste_cache[i].valid == VALID) ? + ste_ssid->ste_cache[i].id : 0xffffffff); + } + read_unlock(&domlist_lock); + /* init stats */ + printk("STE-Policy Security Hook Statistics:\n"); + printk("ste: event_channel eval_count = %x\n", atomic_read(&(ste_bin_pol.ec_eval_count))); + printk("ste: event_channel denied_count = %x\n", atomic_read(&(ste_bin_pol.ec_denied_count))); + printk("ste: event_channel cache_hit_count = %x\n", atomic_read(&(ste_bin_pol.ec_cachehit_count))); + printk("ste:\n"); + printk("ste: grant_table eval_count = %x\n", atomic_read(&(ste_bin_pol.gt_eval_count))); + printk("ste: grant_table denied_count = %x\n", atomic_read(&(ste_bin_pol.gt_denied_count))); + printk("ste: grant_table cache_hit_count = %x\n", atomic_read(&(ste_bin_pol.gt_cachehit_count))); +#endif + + if (buf_len < sizeof(struct acm_ste_stats_buffer)) + return -ENOMEM; + + /* now send the hook counts to user space */ + stats.ec_eval_count = htonl(atomic_read(&ste_bin_pol.ec_eval_count)); + stats.gt_eval_count = htonl(atomic_read(&ste_bin_pol.gt_eval_count)); + stats.ec_denied_count = htonl(atomic_read(&ste_bin_pol.ec_denied_count)); + stats.gt_denied_count = htonl(atomic_read(&ste_bin_pol.gt_denied_count)); + stats.ec_cachehit_count = htonl(atomic_read(&ste_bin_pol.ec_cachehit_count)); + stats.gt_cachehit_count = htonl(atomic_read(&ste_bin_pol.gt_cachehit_count)); + memcpy(buf, &stats, sizeof(struct acm_ste_stats_buffer)); + return sizeof(struct acm_ste_stats_buffer); +} + + +/* we need to go through this before calling the hooks, + * returns 1 == cache hit */ +static int inline +check_cache(struct domain *dom, domid_t rdom) { + struct ste_ssid *ste_ssid; + int i; + + printkd("checking cache: %x --> %x.\n", dom->domain_id, rdom); + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(dom)->ssid); + + for(i=0; i< ACM_TE_CACHE_SIZE; i++) { + if ((ste_ssid->ste_cache[i].valid == VALID) && + (ste_ssid->ste_cache[i].id == rdom)) { + printkd("cache hit (entry %x, id= %x!\n", i, ste_ssid->ste_cache[i].id); + return 1; + } + } + return 0; +} + + +/* we only get here if there is NO entry yet; no duplication check! */ +static void inline +cache_result(struct domain *subj, struct domain *obj) { + struct ste_ssid *ste_ssid; + int i; + printkd("caching from doms: %x --> %x.\n", subj->domain_id, obj->domain_id); + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(subj)->ssid); + for(i=0; i< ACM_TE_CACHE_SIZE; i++) + if (ste_ssid->ste_cache[i].valid == FREE) + break; + if (i< ACM_TE_CACHE_SIZE) { + ste_ssid->ste_cache[i].valid = VALID; + ste_ssid->ste_cache[i].id = obj->domain_id; + } else + printk ("Cache of dom %x is full!\n", subj->domain_id); +} + +/* deletes entries for domain 'id' from all caches (re-use) */ +static void inline +clean_id_from_cache(domid_t id) +{ + struct ste_ssid *ste_ssid; + int i; + struct domain **pd; + + printkd("deleting cache for dom %x.\n", id); + + read_lock(&domlist_lock); /* look through caches of all domains */ + pd = &domain_list; + for ( pd = &domain_list; *pd != NULL; pd = &(*pd)->next_in_list ) { + ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + (struct acm_ssid_domain *)(*pd)->ssid); + for (i=0; iste_cache[i].valid == VALID) && + (ste_ssid->ste_cache[i].id = id)) + ste_ssid->ste_cache[i].valid = FREE; + } + read_unlock(&domlist_lock); +} + +/*************************** + * Authorization functions + **************************/ + +static int +ste_pre_domain_create(void *subject_ssid, ssidref_t ssidref) +{ + /* check for ssidref in range for policy */ + ssidref_t ste_ssidref; + traceprintk("%s.\n", __func__); + + read_lock(&acm_bin_pol_rwlock); + ste_ssidref = GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref); + if (ste_ssidref == ACM_DEFAULT_LOCAL_SSID) { + printk("%s: ERROR STE SSID is NOT SET but policy enforced.\n", __func__); + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_DENIED; /* catching and indicating config error */ + } + if (ste_ssidref >= ste_bin_pol.max_ssidrefs) { + printk("%s: ERROR ste_ssidref > max(%x).\n", + __func__, ste_bin_pol.max_ssidrefs-1); + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_DENIED; + } + read_unlock(&acm_bin_pol_rwlock); + return ACM_ACCESS_PERMITTED; +} + +static void +ste_post_domain_destroy(void *subject_ssid, domid_t id) +{ + /* clean all cache entries for destroyed domain (might be re-used) */ + clean_id_from_cache(id); +} + +/* -------- EVENTCHANNEL OPERATIONS -----------*/ +static int +ste_pre_eventchannel_unbound(domid_t id) { + struct domain *subj, *obj; + int ret; + traceprintk("%s: dom%x-->dom%x.\n", + __func__, current->domain->domain_id, id); + + if (check_cache(current->domain, id)) { + atomic_inc(&ste_bin_pol.ec_cachehit_count); + return ACM_ACCESS_PERMITTED; + } + atomic_inc(&ste_bin_pol.ec_eval_count); + subj = current->domain; + obj = find_domain_by_id(id); + + if (share_common_type(subj, obj)) { + cache_result(subj, obj); + ret = ACM_ACCESS_PERMITTED; + } else { + atomic_inc(&ste_bin_pol.ec_denied_count); + ret = ACM_ACCESS_DENIED; + } + if (obj != NULL) + put_domain(obj); + return ret; +} + +static int +ste_pre_eventchannel_interdomain(domid_t id1, domid_t id2) +{ + struct domain *subj, *obj; + int ret; + traceprintk("%s: dom%x-->dom%x.\n", __func__, + (id1 == DOMID_SELF) ? current->domain->domain_id : id1, + (id2 == DOMID_SELF) ? current->domain->domain_id : id2); + + /* following is a bit longer but ensures that we + * "put" only domains that we where "find"-ing + */ + if (id1 == DOMID_SELF) id1 = current->domain->domain_id; + if (id2 == DOMID_SELF) id2 = current->domain->domain_id; + + subj = find_domain_by_id(id1); + obj = find_domain_by_id(id2); + if ((subj == NULL) || (obj == NULL)) { + ret = ACM_ACCESS_DENIED; + goto out; + } + /* cache check late, but evtchn is not on performance critical path */ + if (check_cache(subj, obj->domain_id)) { + atomic_inc(&ste_bin_pol.ec_cachehit_count); + ret = ACM_ACCESS_PERMITTED; + goto out; + } + atomic_inc(&ste_bin_pol.ec_eval_count); + + if (share_common_type(subj, obj)) { + cache_result(subj, obj); + ret = ACM_ACCESS_PERMITTED; + } else { + atomic_inc(&ste_bin_pol.ec_denied_count); + ret = ACM_ACCESS_DENIED; + } + out: + if (obj != NULL) + put_domain(obj); + if (subj != NULL) + put_domain(subj); + return ret; +} + +/* -------- SHARED MEMORY OPERATIONS -----------*/ + +static int +ste_pre_grant_map_ref (domid_t id) { + struct domain *obj, *subj; + int ret; + traceprintk("%s: dom%x-->dom%x.\n", __func__, + current->domain->domain_id, id); + + if (check_cache(current->domain, id)) { + atomic_inc(&ste_bin_pol.gt_cachehit_count); + return ACM_ACCESS_PERMITTED; + } + atomic_inc(&ste_bin_pol.gt_eval_count); + subj = current->domain; + obj = find_domain_by_id(id); + + if (share_common_type(subj, obj)) { + cache_result(subj, obj); + ret = ACM_ACCESS_PERMITTED; + } else { + atomic_inc(&ste_bin_pol.gt_denied_count); + printkd("%s: ACCESS DENIED!\n", __func__); + ret = ACM_ACCESS_DENIED; + } + if (obj != NULL) + put_domain(obj); + return ret; +} + +/* since setting up grant tables involves some implicit information + flow from the creating domain to the domain that is setup, we + check types in addition to the general authorization */ +static int +ste_pre_grant_setup (domid_t id) { + struct domain *obj, *subj; + int ret; + traceprintk("%s: dom%x-->dom%x.\n", __func__, + current->domain->domain_id, id); + + if (check_cache(current->domain, id)) { + atomic_inc(&ste_bin_pol.gt_cachehit_count); + return ACM_ACCESS_PERMITTED; + } + atomic_inc(&ste_bin_pol.gt_eval_count); + /* a) check authorization (eventually use specific capabilities) */ + if (!IS_PRIV(current->domain)) { + printk("%s: Grant table management authorization denied ERROR!\n", __func__); + return ACM_ACCESS_DENIED; + } + /* b) check types */ + subj = current->domain; + obj = find_domain_by_id(id); + + if (share_common_type(subj, obj)) { + cache_result(subj, obj); + ret = ACM_ACCESS_PERMITTED; + } else { + atomic_inc(&ste_bin_pol.gt_denied_count); + ret = ACM_ACCESS_DENIED; + } + if (obj != NULL) + put_domain(obj); + return ret; +} + +/* now define the hook structure similarly to LSM */ +struct acm_operations acm_simple_type_enforcement_ops = { + /* policy management services */ + .init_domain_ssid = ste_init_domain_ssid, + .free_domain_ssid = ste_free_domain_ssid, + .dump_binary_policy = ste_dump_policy, + .set_binary_policy = ste_set_policy, + .dump_statistics = ste_dump_stats, + /* domain management control hooks */ + .pre_domain_create = ste_pre_domain_create, + .post_domain_create = NULL, + .fail_domain_create = NULL, + .post_domain_destroy = ste_post_domain_destroy, + /* event channel control hooks */ + .pre_eventchannel_unbound = ste_pre_eventchannel_unbound, + .fail_eventchannel_unbound = NULL, + .pre_eventchannel_interdomain = ste_pre_eventchannel_interdomain, + .fail_eventchannel_interdomain = NULL, + /* grant table control hooks */ + .pre_grant_map_ref = ste_pre_grant_map_ref, + .fail_grant_map_ref = NULL, + .pre_grant_setup = ste_pre_grant_setup, + .fail_grant_setup = NULL, +}; diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c index b0ebecfcee..ca7cf17aca 100644 --- a/xen/arch/x86/setup.c +++ b/xen/arch/x86/setup.c @@ -20,6 +20,7 @@ #include #include #include +#include extern void dmi_scan_machine(void); extern void generic_apic_probe(void); @@ -393,12 +394,17 @@ void __init __start_xen(multiboot_info_t *mbi) shadow_mode_init(); + /* initialize access control security module */ + acm_init(); + /* Create initial domain 0. */ dom0 = do_createdomain(0, 0); if ( dom0 == NULL ) panic("Error creating domain 0\n"); set_bit(_DOMF_privileged, &dom0->domain_flags); + /* post-create hooks sets security label */ + acm_post_domain0_create(dom0->domain_id); /* Grab the DOM0 command line. */ cmdline = (char *)(mod[0].string ? __va(mod[0].string) : NULL); diff --git a/xen/arch/x86/x86_32/entry.S b/xen/arch/x86/x86_32/entry.S index 475474b99a..93827336d4 100644 --- a/xen/arch/x86/x86_32/entry.S +++ b/xen/arch/x86/x86_32/entry.S @@ -751,6 +751,7 @@ ENTRY(hypercall_table) .long do_boot_vcpu .long do_ni_hypercall /* 25 */ .long do_mmuext_op + .long do_policy_op /* 27 */ .rept NR_hypercalls-((.-hypercall_table)/4) .long do_ni_hypercall .endr diff --git a/xen/common/dom0_ops.c b/xen/common/dom0_ops.c index 8b3dd1edbc..ec86fc1b62 100644 --- a/xen/common/dom0_ops.c +++ b/xen/common/dom0_ops.c @@ -19,6 +19,7 @@ #include #include #include +#include extern long arch_do_dom0_op(dom0_op_t *op, dom0_op_t *u_dom0_op); extern void arch_getdomaininfo_ctxt( @@ -91,6 +92,7 @@ long do_dom0_op(dom0_op_t *u_dom0_op) { long ret = 0; dom0_op_t curop, *op = &curop; + void *ssid = NULL; /* save security ptr between pre and post/fail hooks */ if ( !IS_PRIV(current->domain) ) return -EPERM; @@ -101,6 +103,9 @@ long do_dom0_op(dom0_op_t *u_dom0_op) if ( op->interface_version != DOM0_INTERFACE_VERSION ) return -EACCES; + if ( acm_pre_dom0_op(op, &ssid) ) + return -EACCES; + switch ( op->cmd ) { @@ -357,6 +362,11 @@ long do_dom0_op(dom0_op_t *u_dom0_op) ((d->domain_flags & DOMF_shutdown) ? DOMFLAGS_SHUTDOWN : 0) | d->shutdown_code << DOMFLAGS_SHUTDOWNSHIFT; + if (d->ssid != NULL) + op->u.getdomaininfo.ssidref = ((struct acm_ssid_domain *)d->ssid)->ssidref; + else + op->u.getdomaininfo.ssidref = ACM_DEFAULT_SSID; + op->u.getdomaininfo.tot_pages = d->tot_pages; op->u.getdomaininfo.max_pages = d->max_pages; op->u.getdomaininfo.shared_info_frame = @@ -493,7 +503,10 @@ long do_dom0_op(dom0_op_t *u_dom0_op) ret = arch_do_dom0_op(op,u_dom0_op); } - + if (!ret) + acm_post_dom0_op(op, ssid); + else + acm_fail_dom0_op(op, ssid); return ret; } diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c index 6f6e707667..29d10ef3d2 100644 --- a/xen/common/event_channel.c +++ b/xen/common/event_channel.c @@ -26,6 +26,7 @@ #include #include +#include #define bucket_from_port(d,p) \ ((d)->evtchn[(p)/EVTCHNS_PER_BUCKET]) @@ -587,6 +588,9 @@ long do_event_channel_op(evtchn_op_t *uop) if ( copy_from_user(&op, uop, sizeof(op)) != 0 ) return -EFAULT; + if (acm_pre_event_channel(&op)) + return -EACCES; + switch ( op.cmd ) { case EVTCHNOP_alloc_unbound: diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index 683a051df3..9e7b7223e9 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -30,6 +30,7 @@ #include #include #include +#include #define PIN_FAIL(_lbl, _rc, _f, _a...) \ do { \ @@ -357,6 +358,11 @@ __gnttab_map_grant_ref( return GNTST_bad_gntref; } + if (acm_pre_grant_map_ref(dom)) { + (void)__put_user(GNTST_permission_denied, &uop->handle); + return GNTST_permission_denied; + } + if ( unlikely((rd = find_domain_by_id(dom)) == NULL) || unlikely(ld == rd) ) { diff --git a/xen/common/policy_ops.c b/xen/common/policy_ops.c new file mode 100644 index 0000000000..ff2b2f9ba4 --- /dev/null +++ b/xen/common/policy_ops.c @@ -0,0 +1,117 @@ +/****************************************************************************** + *policy_ops.c + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * 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, version 2 of the + * License. + * + * Process policy command requests from guest OS. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* function prototypes defined in acm/acm_policy.c */ +int acm_set_policy(void *buf, u16 buf_size, u16 policy); +int acm_get_policy(void *buf, u16 buf_size); +int acm_dump_statistics(void *buf, u16 buf_size); + +typedef enum policyoperation { + POLICY, /* access to policy interface (early drop) */ + GETPOLICY, /* dump policy cache */ + SETPOLICY, /* set policy cache (controls security) */ + DUMPSTATS /* dump policy statistics */ +} policyoperation_t; + +int +acm_authorize_policyops(struct domain *d, policyoperation_t pops) +{ + /* currently, all policy management functions are restricted to privileged domains, + * soon we will introduce finer-grained privileges for policy operations + */ + if (!IS_PRIV(d)) { + printk("%s: Policy management authorization denied ERROR!\n", __func__); + return ACM_ACCESS_DENIED; + } + return ACM_ACCESS_PERMITTED; +} + +long do_policy_op(policy_op_t *u_policy_op) +{ + long ret = 0; + policy_op_t curop, *op = &curop; + + /* check here policy decision for policy commands */ + /* for now allow DOM0 only, later indepedently */ + if (acm_authorize_policyops(current->domain, POLICY)) + return -EACCES; + + if ( copy_from_user(op, u_policy_op, sizeof(*op)) ) + return -EFAULT; + + if ( op->interface_version != POLICY_INTERFACE_VERSION ) + return -EACCES; + + switch ( op->cmd ) + { + case POLICY_SETPOLICY: + { + if (acm_authorize_policyops(current->domain, SETPOLICY)) + return -EACCES; + printkd("%s: setting policy.\n", __func__); + ret = acm_set_policy(op->u.setpolicy.pushcache, op->u.setpolicy.pushcache_size, op->u.setpolicy.policy_type); + if (ret == ACM_OK) + ret = 0; + else + ret = -ESRCH; + } + break; + + case POLICY_GETPOLICY: + { + if (acm_authorize_policyops(current->domain, GETPOLICY)) + return -EACCES; + printkd("%s: getting policy.\n", __func__); + ret = acm_get_policy(op->u.getpolicy.pullcache, op->u.getpolicy.pullcache_size); + if (ret == ACM_OK) + ret = 0; + else + ret = -ESRCH; + } + break; + + case POLICY_DUMPSTATS: + { + if (acm_authorize_policyops(current->domain, DUMPSTATS)) + return -EACCES; + printkd("%s: dumping statistics.\n", __func__); + ret = acm_dump_statistics(op->u.dumpstats.pullcache, op->u.dumpstats.pullcache_size); + if (ret == ACM_OK) + ret = 0; + else + ret = -ESRCH; + } + break; + + default: + ret = -ESRCH; + + } + return ret; +} diff --git a/xen/include/acm/acm_core.h b/xen/include/acm/acm_core.h new file mode 100644 index 0000000000..e404b455ad --- /dev/null +++ b/xen/include/acm/acm_core.h @@ -0,0 +1,117 @@ +/**************************************************************** + * acm_core.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * 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, version 2 of the + * License. + * + * sHype header file describing core data types and constants + * for the access control module and relevant policies + * + */ +#ifndef _ACM_CORE_H +#define _ACM_CORE_H + +#include +#include +#include + +/* Xen-internal representation of the binary policy */ +struct acm_binary_policy { + u16 primary_policy_code; + u16 secondary_policy_code; + void *primary_binary_policy; + void *secondary_binary_policy; + +}; + +struct chwall_binary_policy { + u16 max_types; + u16 max_ssidrefs; + u16 max_conflictsets; + domaintype_t *ssidrefs; /* [max_ssidrefs][max_types] */ + domaintype_t *conflict_aggregate_set; /* [max_types] */ + domaintype_t *running_types; /* [max_types] */ + domaintype_t *conflict_sets; /* [max_conflictsets][max_types]*/ +}; + +struct ste_binary_policy { + u16 max_types; + u16 max_ssidrefs; + domaintype_t *ssidrefs; /* [max_ssidrefs][max_types] */ + atomic_t ec_eval_count, gt_eval_count; + atomic_t ec_denied_count, gt_denied_count; + atomic_t ec_cachehit_count, gt_cachehit_count; +}; + +/* global acm policy */ +extern struct acm_binary_policy acm_bin_pol; +extern struct chwall_binary_policy chwall_bin_pol; +extern struct ste_binary_policy ste_bin_pol; +/* use the lock when reading / changing binary policy ! */ +extern rwlock_t acm_bin_pol_rwlock; + +/* subject and object type definitions */ +enum acm_datatype { DOMAIN }; + +/* defines number of access decisions to other domains can be cached + * one entry per domain, TE does not distinguish evtchn or grant_table */ +#define ACM_TE_CACHE_SIZE 8 +enum acm_ste_flag { VALID, FREE }; + +/* cache line: + * if cache_line.valid==VALID, then + * STE decision is cached as "permitted" + * on domain cache_line.id + */ +struct acm_ste_cache_line { + enum acm_ste_flag valid; + domid_t id; +}; + +/* general definition of a subject security id */ +struct acm_ssid_domain { + enum acm_datatype datatype; /* type of subject (e.g., partition) */ + ssidref_t ssidref; /* combined security reference */ + void *primary_ssid; /* primary policy ssid part (e.g. chinese wall) */ + void *secondary_ssid; /* secondary policy ssid part (e.g. type enforcement) */ + struct domain *subject; /* backpointer to subject structure */ + domid_t domainid; /* replicate id */ +}; + +/* chinese wall ssid type */ +struct chwall_ssid { + ssidref_t chwall_ssidref; +}; + +/* simple type enforcement ssid type */ +struct ste_ssid { + ssidref_t ste_ssidref; + struct acm_ste_cache_line ste_cache[ACM_TE_CACHE_SIZE]; /* decision cache */ +}; + +/* macros to access ssidref for primary / secondary policy + * primary ssidref = lower 16 bit + * secondary ssidref = higher 16 bit + */ +#define GET_SSIDREF(POLICY, ssidref) \ + ((POLICY) == acm_bin_pol.primary_policy_code) ? \ + ((ssidref) & 0xffff) : ((ssidref) >> 16) + +/* macros to access ssid pointer for primary / secondary policy */ +#define GET_SSIDP(POLICY, ssid) \ + ((POLICY) == acm_bin_pol.primary_policy_code) ? \ + ((ssid)->primary_ssid) : ((ssid)->secondary_ssid) + +/* protos */ +int acm_init_domain_ssid(domid_t id, ssidref_t ssidref); +int acm_free_domain_ssid(struct acm_ssid_domain *ssid); + +#endif + diff --git a/xen/include/acm/acm_endian.h b/xen/include/acm/acm_endian.h new file mode 100644 index 0000000000..fd7229b846 --- /dev/null +++ b/xen/include/acm/acm_endian.h @@ -0,0 +1,88 @@ +/**************************************************************** + * acm_endian.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Stefan Berger + * + * Contributions: + * Reiner Sailer + * + * 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, version 2 of the + * License. + * + * sHype header file defining endian-dependent functions for the + * big-endian policy interface + * + */ +#ifndef _ACM_ENDIAN_H +#define _ACM_ENDIAN_H + +/* don't use these functions in performance critical sections! */ + +/* set during initialization by testing */ +extern u8 little_endian; + +static inline u32 ntohl(u32 x) +{ + if (little_endian) + return + ( (((x) >> 24) & 0xff )| + (((x) >> 8) & 0xff00 )| + (((x) << 8) & 0xff0000 )| + (((x) << 24) & 0xff000000) ); + else + return x; +} + +static inline u16 ntohs(u16 x) +{ + if (little_endian) + return + ( (((x) >> 8) & 0xff )| + (((x) << 8) & 0xff00 ) ); + else + return x; +} + +#define htonl(x) ntohl(x) +#define htons(x) ntohs(x) + +static inline void arrcpy16(u16 *dest, const u16 *src, size_t n) +{ + unsigned int i = 0; + while (i < n) { + dest[i] = htons(src[i]); + i++; + } +} + +static inline void arrcpy32(u32 *dest, const u32 *src, size_t n) +{ + unsigned int i = 0; + while (i < n) { + dest[i] = htonl(src[i]); + i++; + } +} + +static inline void arrcpy(void *dest, const void *src, unsigned int elsize, size_t n) +{ + switch (elsize) { + case sizeof(u16): + arrcpy16((u16 *)dest, (u16 *)src, n); + break; + + case sizeof(u32): + arrcpy32((u32 *)dest, (u32 *)src, n); + break; + + default: + memcpy(dest, src, elsize*n); + } +} + +#endif diff --git a/xen/include/acm/acm_hooks.h b/xen/include/acm/acm_hooks.h new file mode 100644 index 0000000000..534d919ff4 --- /dev/null +++ b/xen/include/acm/acm_hooks.h @@ -0,0 +1,337 @@ +/**************************************************************** + * acm_hooks.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * 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, version 2 of the + * License. + * + * acm header file implementing the global (policy-independent) + * sHype hooks that are called throughout Xen. + * + */ +#ifndef _ACM_HOOKS_H +#define _ACM_HOOKS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* if ACM_TRACE_MODE defined, all hooks should + * print a short trace message */ +/* #define ACM_TRACE_MODE */ + +#ifdef ACM_TRACE_MODE +# define traceprintk(fmt, args...) printk(fmt,## args) +#else +# define traceprintk(fmt, args...) +#endif + +/* global variables */ +extern struct acm_operations *acm_primary_ops; +extern struct acm_operations *acm_secondary_ops; + +/********************************************************************************************** + * HOOK structure and meaning (justifies a few words about our model): + * + * General idea: every policy-controlled system operation is reflected in a + * transaction in the system's security state + * + * Keeping the security state consistent requires "atomic" transactions. + * The name of the hooks to place around policy-controlled transactions + * reflects this. If authorizations do not involve security state changes, + * then and only then POST and FAIL hooks remain empty since we don't care + * about the eventual outcome of the operation from a security viewpoint. + * + * PURPOSE of hook types: + * ====================== + * PRE-Hooks + * a) general authorization to guard a controlled system operation + * b) prepare security state change (means: fail hook must be able to "undo" this) + * + * POST-Hooks + * a) commit prepared state change + * + * FAIL-Hooks + * a) roll-back prepared security state change from PRE-Hook + * + * + * PLACEMENT of hook types: + * ======================== + * PRE-Hooks must be called: + * a) before a guarded/controlled system operation is started + * (return is ACM_ACCESS_PERMITTED or ACM_ACCESS_DENIED or error) + * --> operation must be aborted if return is != ACM_ACCESS_PERMITTED + * + * POST-Hooks must be called: + * a) after successful transaction (no return value; commit shall never fail) + * + * FAIL-Hooks must be called: + * a) if system transaction (operation) fails somewhen after calling the PRE-hook + * (obviously the POST-Hook is not called in this case) + * b) if another (secondary) policy denies access in its PRE-Hook + * (policy layering is useful but requires additional handling) + * + * + * + * Hook model from a security transaction viewpoint: + * + * start-sys-ops--> prepare ----succeed-----> commit --> sys-ops success + * (pre-hook) \ (post-hook) + * \ + * fail + * \ + * \ + * roll-back + * (fail-hook) + * \ + * sys-ops error + * + *************************************************************************************************/ + +struct acm_operations { + /* policy management functions (must always be defined!) */ + int (*init_domain_ssid) (void **ssid, ssidref_t ssidref); + void (*free_domain_ssid) (void *ssid); + int (*dump_binary_policy) (u8 *buffer, u16 buf_size); + int (*set_binary_policy) (u8 *buffer, u16 buf_size); + int (*dump_statistics) (u8 *buffer, u16 buf_size); + /* domain management control hooks (can be NULL) */ + int (*pre_domain_create) (void *subject_ssid, ssidref_t ssidref); + void (*post_domain_create) (domid_t domid, ssidref_t ssidref); + void (*fail_domain_create) (void *subject_ssid, ssidref_t ssidref); + void (*post_domain_destroy) (void *object_ssid, domid_t id); + /* event channel control hooks (can be NULL) */ + int (*pre_eventchannel_unbound) (domid_t id); + void (*fail_eventchannel_unbound) (domid_t id); + int (*pre_eventchannel_interdomain) (domid_t id1, domid_t id2); + int (*fail_eventchannel_interdomain) (domid_t id1, domid_t id2); + /* grant table control hooks (can be NULL) */ + int (*pre_grant_map_ref) (domid_t id); + void (*fail_grant_map_ref) (domid_t id); + int (*pre_grant_setup) (domid_t id); + void (*fail_grant_setup) (domid_t id); +}; + +static inline int acm_pre_domain_create (void *subject_ssid, ssidref_t ssidref) +{ + if ((acm_primary_ops->pre_domain_create != NULL) && + acm_primary_ops->pre_domain_create (subject_ssid, ssidref)) + return ACM_ACCESS_DENIED; + else if ((acm_secondary_ops->pre_domain_create != NULL) && + acm_secondary_ops->pre_domain_create (subject_ssid, ssidref)) { + /* roll-back primary */ + if (acm_primary_ops->fail_domain_create != NULL) + acm_primary_ops->fail_domain_create (subject_ssid, ssidref); + return ACM_ACCESS_DENIED; + } else + return ACM_ACCESS_PERMITTED; +} + +static inline void acm_post_domain_create (domid_t domid, ssidref_t ssidref) +{ + if (acm_primary_ops->post_domain_create != NULL) + acm_primary_ops->post_domain_create (domid, ssidref); + if (acm_secondary_ops->post_domain_create != NULL) + acm_secondary_ops->post_domain_create (domid, ssidref); +} + +static inline void acm_fail_domain_create (void *subject_ssid, ssidref_t ssidref) +{ + if (acm_primary_ops->fail_domain_create != NULL) + acm_primary_ops->fail_domain_create (subject_ssid, ssidref); + if (acm_secondary_ops->fail_domain_create != NULL) + acm_secondary_ops->fail_domain_create (subject_ssid, ssidref); +} + +static inline void acm_post_domain_destroy (void *object_ssid, domid_t id) +{ + if (acm_primary_ops->post_domain_destroy != NULL) + acm_primary_ops->post_domain_destroy (object_ssid, id); + if (acm_secondary_ops->post_domain_destroy != NULL) + acm_secondary_ops->post_domain_destroy (object_ssid, id); + return; +} + +/* event channel ops */ + +static inline int acm_pre_eventchannel_unbound (domid_t id) +{ + if ((acm_primary_ops->pre_eventchannel_unbound != NULL) && + acm_primary_ops->pre_eventchannel_unbound (id)) + return ACM_ACCESS_DENIED; + else if ((acm_secondary_ops->pre_eventchannel_unbound != NULL) && + acm_secondary_ops->pre_eventchannel_unbound (id)) { + /* roll-back primary */ + if (acm_primary_ops->fail_eventchannel_unbound != NULL) + acm_primary_ops->fail_eventchannel_unbound (id); + return ACM_ACCESS_DENIED; + } else + return ACM_ACCESS_PERMITTED; +} + +static inline int acm_pre_eventchannel_interdomain (domid_t id1, domid_t id2) +{ + if ((acm_primary_ops->pre_eventchannel_interdomain != NULL) && + acm_primary_ops->pre_eventchannel_interdomain (id1, id2)) + return ACM_ACCESS_DENIED; + else if ((acm_secondary_ops->pre_eventchannel_interdomain != NULL) && + acm_secondary_ops->pre_eventchannel_interdomain (id1, id2)) { + /* roll-back primary */ + if (acm_primary_ops->fail_eventchannel_interdomain != NULL) + acm_primary_ops->fail_eventchannel_interdomain (id1, id2); + return ACM_ACCESS_DENIED; + } else + return ACM_ACCESS_PERMITTED; +} + +/************ Xen inline hooks ***************/ + +/* small macro to make the hooks more readable + * (eliminates hooks if NULL policy is active) + */ +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) +static inline int acm_pre_dom0_op(dom0_op_t *op, void **ssid) +{ return 0; } +#else +static inline int acm_pre_dom0_op(dom0_op_t *op, void **ssid) +{ + int ret = -EACCES; + struct domain *d; + + switch(op->cmd) { + case DOM0_CREATEDOMAIN: + ret = acm_pre_domain_create(current->domain->ssid, op->u.createdomain.ssidref); + break; + case DOM0_DESTROYDOMAIN: + d = find_domain_by_id(op->u.destroydomain.domain); + if (d != NULL) { + *ssid = d->ssid; /* save for post destroy when d is gone */ + /* no policy-specific hook */ + put_domain(d); + ret = 0; + } + break; + default: + ret = 0; /* ok */ + } + return ret; +} +#endif + + +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) +static inline void acm_post_dom0_op(dom0_op_t *op, void *ssid) +{ return; } +#else +static inline void acm_post_dom0_op(dom0_op_t *op, void *ssid) +{ + switch(op->cmd) { + case DOM0_CREATEDOMAIN: + /* initialialize shared sHype security labels for new domain */ + acm_init_domain_ssid(op->u.createdomain.domain, op->u.createdomain.ssidref); + acm_post_domain_create(op->u.createdomain.domain, op->u.createdomain.ssidref); + break; + case DOM0_DESTROYDOMAIN: + acm_post_domain_destroy(ssid, op->u.destroydomain.domain); + /* free security ssid for the destroyed domain (also if running null policy */ + acm_free_domain_ssid((struct acm_ssid_domain *)ssid); + break; + } +} +#endif + + +#if (ACM_USE_SECURITY_POLICy == ACM_NULL_POLICY) +static inline void acm_fail_dom0_op(dom0_op_t *op, void *ssid) +{ return; } +#else +static inline void acm_fail_dom0_op(dom0_op_t *op, void *ssid) +{ + switch(op->cmd) { + case DOM0_CREATEDOMAIN: + acm_fail_domain_create(current->domain->ssid, op->u.createdomain.ssidref); + break; + } +} +#endif + + +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) +static inline int acm_pre_event_channel(evtchn_op_t *op) +{ return 0; } +#else +static inline int acm_pre_event_channel(evtchn_op_t *op) +{ + int ret = -EACCES; + + switch(op->cmd) { + case EVTCHNOP_alloc_unbound: + ret = acm_pre_eventchannel_unbound(op->u.alloc_unbound.dom); + break; + case EVTCHNOP_bind_interdomain: + ret = acm_pre_eventchannel_interdomain(op->u.bind_interdomain.dom1, op->u.bind_interdomain.dom2); + break; + default: + ret = 0; /* ok */ + } + return ret; +} +#endif + +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) +static inline int acm_pre_grant_map_ref(domid_t id) +{ return 0; } +#else +static inline int acm_pre_grant_map_ref (domid_t id) +{ + if ((acm_primary_ops->pre_grant_map_ref != NULL) && + acm_primary_ops->pre_grant_map_ref (id)) + return ACM_ACCESS_DENIED; + else if ((acm_secondary_ops->pre_grant_map_ref != NULL) && + acm_secondary_ops->pre_grant_map_ref (id)) { + /* roll-back primary */ + if (acm_primary_ops->fail_grant_map_ref != NULL) + acm_primary_ops->fail_grant_map_ref (id); + return ACM_ACCESS_DENIED; + } else + return ACM_ACCESS_PERMITTED; +} +#endif + + +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) +static inline int acm_pre_grant_setup(domid_t id) +{ return 0; } +#else +static inline int acm_pre_grant_setup (domid_t id) +{ + if ((acm_primary_ops->pre_grant_setup != NULL) && + acm_primary_ops->pre_grant_setup (id)) + return ACM_ACCESS_DENIED; + else if ((acm_secondary_ops->pre_grant_setup != NULL) && + acm_secondary_ops->pre_grant_setup (id)) { + /* roll-back primary */ + if (acm_primary_ops->fail_grant_setup != NULL) + acm_primary_ops->fail_grant_setup (id); + return ACM_ACCESS_DENIED; + } else + return ACM_ACCESS_PERMITTED; +} +#endif + + +#endif diff --git a/xen/include/public/acm.h b/xen/include/public/acm.h new file mode 100644 index 0000000000..31191b7b5a --- /dev/null +++ b/xen/include/public/acm.h @@ -0,0 +1,161 @@ +/**************************************************************** + * acm.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * Contributors: + * Stefan Berger + * added network byte order support for binary policies + * + * 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, version 2 of the + * License. + * + * sHype general access control module header file. + * here are all definitions that are shared between + * xen-core, guest-kernels, and applications. + * + * todo: move from static policy choice to compile option. + */ + +#ifndef _XEN_PUBLIC_SHYPE_H +#define _XEN_PUBLIC_SHYPE_H + +#include "xen.h" +#include "sched_ctl.h" + +/* if ACM_DEBUG defined, all hooks should + * print a short trace message (comment it out + * when not in testing mode ) + */ +/* #define ACM_DEBUG */ + +#ifdef ACM_DEBUG +# define printkd(fmt, args...) printk(fmt,## args) +#else +# define printkd(fmt, args...) +#endif + +/* default ssid reference value if not supplied */ +#define ACM_DEFAULT_SSID 0xffffffff +#define ACM_DEFAULT_LOCAL_SSID 0xffff + +/* Internal ACM ERROR types */ +#define ACM_OK 0 +#define ACM_UNDEF -1 +#define ACM_INIT_SSID_ERROR -2 +#define ACM_INIT_SOID_ERROR -3 +#define ACM_ERROR -4 + +/* External ACCESS DECISIONS */ +#define ACM_ACCESS_PERMITTED 0 +#define ACM_ACCESS_DENIED -111 +#define ACM_NULL_POINTER_ERROR -200 + +#define ACM_MAX_POLICY 3 + +#define ACM_NULL_POLICY 0 +#define ACM_CHINESE_WALL_POLICY 1 +#define ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY 2 +#define ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY 3 + +/* policy: */ +#define ACM_POLICY_NAME(X) \ + (X == ACM_NULL_POLICY) ? "NULL policy" : \ + (X == ACM_CHINESE_WALL_POLICY) ? "CHINESE WALL policy" : \ + (X == ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) ? "SIMPLE TYPE ENFORCEMENT policy" : \ + (X == ACM_CHINESE_WALL_AND_SIMPLE_TYPE_ENFORCEMENT_POLICY) ? "CHINESE WALL AND SIMPLE TYPE ENFORCEMENT policy" : \ + "UNDEFINED policy" + +#ifndef ACM_USE_SECURITY_POLICY +#define ACM_USE_SECURITY_POLICY ACM_NULL_POLICY +#endif + +/* defines a ssid reference used by xen */ +typedef u32 ssidref_t; + +/* -------security policy relevant type definitions-------- */ + +/* type identifier; compares to "equal" or "not equal" */ +typedef u16 domaintype_t; + +/* CHINESE WALL POLICY DATA STRUCTURES + * + * current accumulated conflict type set: + * When a domain is started and has a type that is in + * a conflict set, the conflicting types are incremented in + * the aggregate set. When a domain is destroyed, the + * conflicting types to its type are decremented. + * If a domain has multiple types, this procedure works over + * all those types. + * + * conflict_aggregate_set[i] holds the number of + * running domains that have a conflict with type i. + * + * running_types[i] holds the number of running domains + * that include type i in their ssidref-referenced type set + * + * conflict_sets[i][j] is "0" if type j has no conflict + * with type i and is "1" otherwise. + */ +/* high-16 = version, low-16 = check magic */ +#define ACM_MAGIC 0x0001debc + +/* each offset in bytes from start of the struct they + * the are part of */ +/* each buffer consists of all policy information for + * the respective policy given in the policy code + */ +struct acm_policy_buffer { + u32 magic; + u32 policyversion; + u32 len; + u16 primary_policy_code; + u16 primary_buffer_offset; + u16 secondary_policy_code; + u16 secondary_buffer_offset; +}; + +struct acm_chwall_policy_buffer { + u16 policy_code; + u16 chwall_max_types; + u16 chwall_max_ssidrefs; + u16 chwall_max_conflictsets; + u16 chwall_ssid_offset; + u16 chwall_conflict_sets_offset; + u16 chwall_running_types_offset; + u16 chwall_conflict_aggregate_offset; +}; + +struct acm_ste_policy_buffer { + u16 policy_code; + u16 ste_max_types; + u16 ste_max_ssidrefs; + u16 ste_ssid_offset; +}; + +struct acm_stats_buffer { + u32 magic; + u32 policyversion; + u32 len; + u16 primary_policy_code; + u16 primary_stats_offset; + u16 secondary_policy_code; + u16 secondary_stats_offset; +}; + +struct acm_ste_stats_buffer { + u32 ec_eval_count; + u32 gt_eval_count; + u32 ec_denied_count; + u32 gt_denied_count; + u32 ec_cachehit_count; + u32 gt_cachehit_count; +}; + + +#endif diff --git a/xen/include/public/acm_dom0_setup.h b/xen/include/public/acm_dom0_setup.h new file mode 100644 index 0000000000..6604156ccf --- /dev/null +++ b/xen/include/public/acm_dom0_setup.h @@ -0,0 +1,34 @@ +/**************************************************************** + * acm_dom0_setup.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * Includes necessary definitions to bring-up dom0 + */ +#include + +extern int acm_init(void); + +#if (ACM_USE_SECURITY_POLICY == ACM_NULL_POLICY) + +static inline void acm_post_domain0_create(domid_t domid) +{ + return; +} + +#else + +/* predefined ssidref for DOM0 used by xen when creating DOM0 */ +#define ACM_DOM0_SSIDREF 0 + +static inline void acm_post_domain0_create(domid_t domid) +{ + /* initialialize shared sHype security labels for new domain */ + acm_init_domain_ssid(domid, ACM_DOM0_SSIDREF); + acm_post_domain_create(domid, ACM_DOM0_SSIDREF); +} + +#endif diff --git a/xen/include/public/dom0_ops.h b/xen/include/public/dom0_ops.h index 0768b8c6ae..3ff82b43ac 100644 --- a/xen/include/public/dom0_ops.h +++ b/xen/include/public/dom0_ops.h @@ -43,6 +43,8 @@ typedef struct sched_adjdom_cmd dom0_adjustdom_t; #define DOM0_CREATEDOMAIN 8 typedef struct { + /* IN parameters */ + u32 ssidref; /* IN/OUT parameters. */ /* Identifier for new domain (auto-allocate if zero is specified). */ domid_t domain; @@ -88,6 +90,7 @@ typedef struct { u32 n_vcpu; s32 vcpu_to_cpu[MAX_VIRT_CPUS]; /* current mapping */ cpumap_t cpumap[MAX_VIRT_CPUS]; /* allowable mapping */ + u32 ssidref; } dom0_getdomaininfo_t; #define DOM0_SETDOMAININFO 13 diff --git a/xen/include/public/policy_ops.h b/xen/include/public/policy_ops.h new file mode 100644 index 0000000000..6b55f764b1 --- /dev/null +++ b/xen/include/public/policy_ops.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * policy_ops.h + * + * Copyright (C) 2005 IBM Corporation + * + * Author: + * Reiner Sailer + * + * 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, version 2 of the + * License. + * + * Process policy command requests from guest OS. + * access checked by policy; not restricted to DOM0 + * + */ + + +#ifndef __XEN_PUBLIC_POLICY_OPS_H__ +#define __XEN_PUBLIC_POLICY_OPS_H__ + +#include "xen.h" +#include "sched_ctl.h" + +/* + * Make sure you increment the interface version whenever you modify this file! + * This makes sure that old versions of policy tools will stop working in a + * well-defined way (rather than crashing the machine, for instance). + */ +#define POLICY_INTERFACE_VERSION 0xAAAA0001 + +/************************************************************************/ + +#define POLICY_SETPOLICY 4 +typedef struct { + /* IN variables. */ + u16 policy_type; + u16 padding1; + /* OUT variables */ + void *pushcache; + u16 pushcache_size; +} PACKED policy_setpolicy_t; + + +#define POLICY_GETPOLICY 5 +typedef struct { + /* IN variables. */ + u16 policy_type; + u16 padding1; + /* OUT variables */ + void *pullcache; + u16 pullcache_size; +} PACKED policy_getpolicy_t; + +#define POLICY_DUMPSTATS 6 +typedef struct { + void *pullcache; + u16 pullcache_size; +} PACKED policy_dumpstats_t; + + +typedef struct { + u32 cmd; /* 0 */ + u32 interface_version; /* 4 */ /* POLICY_INTERFACE_VERSION */ + union { /* 8 */ + u32 dummy[14]; /* 72bytes */ + policy_setpolicy_t setpolicy; + policy_getpolicy_t getpolicy; + policy_dumpstats_t dumpstats; + } PACKED u; +} PACKED policy_op_t; /* 80 bytes */ + +#endif /* __XEN_PUBLIC_POLICY_OPS_H__ */ diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h index 2fdd400e92..8b183491a6 100644 --- a/xen/include/public/xen.h +++ b/xen/include/public/xen.h @@ -58,6 +58,7 @@ #define __HYPERVISOR_boot_vcpu 24 #define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ #define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_policy_op 27 /* * VIRTUAL INTERRUPTS diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h index 35a3c36cab..7649f1a450 100644 --- a/xen/include/xen/sched.h +++ b/xen/include/xen/sched.h @@ -137,6 +137,8 @@ struct domain cpumask_t cpumask; struct arch_domain arch; + + void *ssid; /* sHype security subject identifier */ }; struct domain_setup_info -- cgit v1.2.3