--- xen-unstable.hg/xen/acm/acm_chinesewall_hooks.c | 148 ++- xen-unstable.hg/xen/acm/acm_core.c | 3 xen-unstable.hg/xen/acm/acm_null_hooks.c | 10 xen-unstable.hg/xen/acm/acm_policy.c | 539 +++++++++++- xen-unstable.hg/xen/acm/acm_simple_type_enforcement_hooks.c | 120 +- xen-unstable.hg/xen/common/acm_ops.c | 36 xen-unstable.hg/xen/include/acm/acm_core.h | 49 - xen-unstable.hg/xen/include/acm/acm_hooks.h | 6 xen-unstable.hg/xen/include/public/acm.h | 13 xen-unstable.hg/xen/include/public/acm_ops.h | 30 10 files changed, 830 insertions(+), 124 deletions(-) Index: root/xen-unstable.hg/xen/acm/acm_chinesewall_hooks.c =================================================================== --- root.orig/xen-unstable.hg/xen/acm/acm_chinesewall_hooks.c +++ root/xen-unstable.hg/xen/acm/acm_chinesewall_hooks.c @@ -183,23 +183,26 @@ static int chwall_dump_policy(u8 * buf, 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 +/* + * 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 * ssidrefs, + domaintype_t * conflict_sets, domaintype_t * running_types, - domaintype_t * conflict_aggregate_set) + domaintype_t * conflict_aggregate_set, + struct acm_sized_buffer *errors /* may be NULL */) { int violation = 0, i, j; struct chwall_ssid *chwall_ssid; ssidref_t chwall_ssidref; struct domain *d; - spin_lock(&domlist_update_lock); /* go through all domains and adjust policy as if this domain was started now */ for_each_domain ( d ) { @@ -222,6 +225,9 @@ chwall_init_state(struct acm_chwall_poli printk("%s: CHINESE WALL CONFLICT in type %02x.\n", __func__, i); violation = 1; + + acm_array_append_tuple(errors, ACM_CHWALL_CONFLICT, i); + goto out; } /* set violation and break out of the loop */ @@ -249,7 +255,6 @@ chwall_init_state(struct acm_chwall_poli } } out: - spin_unlock(&domlist_update_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 @@ -257,44 +262,45 @@ chwall_init_state(struct acm_chwall_poli * more than one type is currently running */ } -static int chwall_set_policy(u8 * buf, u32 buf_size, int is_bootpolicy) + +int +do_chwall_init_state_curr(struct acm_sized_buffer *errors) +{ + struct acm_chwall_policy_buffer chwall_buf = { + /* only these two are important */ + .chwall_max_types = chwall_bin_pol.max_types, + .chwall_max_conflictsets = chwall_bin_pol.max_conflictsets, + }; + /* reset running_types and aggregate set for recalculation */ + memset(chwall_bin_pol.running_types, + 0x0, + sizeof(domaintype_t) * chwall_bin_pol.max_types); + memset(chwall_bin_pol.conflict_aggregate_set, + 0x0, + sizeof(domaintype_t) * chwall_bin_pol.max_types); + return chwall_init_state(&chwall_buf, + chwall_bin_pol.ssidrefs, + chwall_bin_pol.conflict_sets, + chwall_bin_pol.running_types, + chwall_bin_pol.conflict_aggregate_set, + errors); +} + +/* + * Attempt to set the policy. This function must be called in test_only + * mode first to only perform checks. A second call then does the + * actual changes. + */ +static int _chwall_update_policy(u8 *buf, u32 buf_size, int test_only, + struct acm_sized_buffer *errors) { + int rc = -EFAULT; /* 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; - if (buf_size < sizeof(struct acm_chwall_policy_buffer)) - return -EINVAL; - - /* rewrite the policy due to endianess */ - chwall_buf->policy_code = be32_to_cpu(chwall_buf->policy_code); - chwall_buf->policy_version = be32_to_cpu(chwall_buf->policy_version); - chwall_buf->chwall_max_types = be32_to_cpu(chwall_buf->chwall_max_types); - chwall_buf->chwall_max_ssidrefs = - be32_to_cpu(chwall_buf->chwall_max_ssidrefs); - chwall_buf->chwall_max_conflictsets = - be32_to_cpu(chwall_buf->chwall_max_conflictsets); - chwall_buf->chwall_ssid_offset = be32_to_cpu(chwall_buf->chwall_ssid_offset); - chwall_buf->chwall_conflict_sets_offset = - be32_to_cpu(chwall_buf->chwall_conflict_sets_offset); - chwall_buf->chwall_running_types_offset = - be32_to_cpu(chwall_buf->chwall_running_types_offset); - chwall_buf->chwall_conflict_aggregate_offset = - be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset); - - /* policy type and version checks */ - if ((chwall_buf->policy_code != ACM_CHINESE_WALL_POLICY) || - (chwall_buf->policy_version != ACM_CHWALL_VERSION)) - return -EINVAL; - - /* during boot dom0_chwall_ssidref is set */ - if (is_bootpolicy && - (dom0_chwall_ssidref >= chwall_buf->chwall_max_ssidrefs)) { - goto error_free; - } - /* 1. allocate new buffers */ ssids = xmalloc_array(domaintype_t, @@ -343,12 +349,20 @@ static int chwall_set_policy(u8 * buf, u * 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)) + conflict_aggregate_set, + errors)) { printk("%s: New policy conflicts with running domains. Policy load aborted.\n", __func__); goto error_free; /* new policy conflicts with running domains */ } + + /* if this was only a test run, exit with ACM_OK */ + if (test_only) { + rc = ACM_OK; + goto error_free; + } + /* 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; @@ -364,12 +378,61 @@ static int chwall_set_policy(u8 * buf, u return ACM_OK; error_free: - printk("%s: ERROR setting policy.\n", __func__); + if (!test_only) printk("%s: ERROR setting policy.\n", __func__); xfree(ssids); xfree(conflict_sets); xfree(running_types); xfree(conflict_aggregate_set); - return -EFAULT; + return rc; +} + +/* + * This function MUST be called before the chwall_ste_policy function! + */ +static int chwall_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy, + struct acm_sized_buffer *errors) +{ + struct acm_chwall_policy_buffer *chwall_buf = + (struct acm_chwall_policy_buffer *) buf; + + if (buf_size < sizeof(struct acm_chwall_policy_buffer)) + return -EINVAL; + + /* rewrite the policy due to endianess */ + chwall_buf->policy_code = be32_to_cpu(chwall_buf->policy_code); + chwall_buf->policy_version = be32_to_cpu(chwall_buf->policy_version); + chwall_buf->chwall_max_types = + be32_to_cpu(chwall_buf->chwall_max_types); + chwall_buf->chwall_max_ssidrefs = + be32_to_cpu(chwall_buf->chwall_max_ssidrefs); + chwall_buf->chwall_max_conflictsets = + be32_to_cpu(chwall_buf->chwall_max_conflictsets); + chwall_buf->chwall_ssid_offset = + be32_to_cpu(chwall_buf->chwall_ssid_offset); + chwall_buf->chwall_conflict_sets_offset = + be32_to_cpu(chwall_buf->chwall_conflict_sets_offset); + chwall_buf->chwall_running_types_offset = + be32_to_cpu(chwall_buf->chwall_running_types_offset); + chwall_buf->chwall_conflict_aggregate_offset = + be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset); + + /* policy type and version checks */ + if ((chwall_buf->policy_code != ACM_CHINESE_WALL_POLICY) || + (chwall_buf->policy_version != ACM_CHWALL_VERSION)) + return -EINVAL; + + /* during boot dom0_chwall_ssidref is set */ + if (is_bootpolicy && + (dom0_chwall_ssidref >= chwall_buf->chwall_max_ssidrefs)) + return -EINVAL; + + + return _chwall_update_policy(buf, buf_size, 1, errors); +} + +static int chwall_set_policy(u8 *buf, u32 buf_size) +{ + return _chwall_update_policy(buf, buf_size, 0, NULL); } static int chwall_dump_stats(u8 * buf, u16 len) @@ -610,6 +673,7 @@ struct acm_operations acm_chinesewall_op .init_domain_ssid = chwall_init_domain_ssid, .free_domain_ssid = chwall_free_domain_ssid, .dump_binary_policy = chwall_dump_policy, + .test_binary_policy = chwall_test_policy, .set_binary_policy = chwall_set_policy, .dump_statistics = chwall_dump_stats, .dump_ssid_types = chwall_dump_ssid_types, Index: root/xen-unstable.hg/xen/acm/acm_core.c =================================================================== --- root.orig/xen-unstable.hg/xen/acm/acm_core.c +++ root/xen-unstable.hg/xen/acm/acm_core.c @@ -249,7 +249,8 @@ acm_setup(char *policy_start, return rc; rc = do_acm_set_policy((void *)policy_start, (u32)policy_len, - is_bootpolicy); + is_bootpolicy, + NULL, NULL, NULL); if (rc == ACM_OK) { printkd("Policy len 0x%lx, start at %p.\n",policy_len,policy_start); Index: root/xen-unstable.hg/xen/acm/acm_null_hooks.c =================================================================== --- root.orig/xen-unstable.hg/xen/acm/acm_null_hooks.c +++ root/xen-unstable.hg/xen/acm/acm_null_hooks.c @@ -33,7 +33,14 @@ null_dump_binary_policy(u8 *buf, u32 buf } static int -null_set_binary_policy(u8 *buf, u32 buf_size, int is_bootpolicy) +null_test_binary_policy(u8 *buf, u32 buf_size, int is_bootpolicy, + struct acm_sized_buffer *errors) +{ + return ACM_OK; +} + +static int +null_set_binary_policy(u8 *buf, u32 buf_size) { return ACM_OK; } @@ -58,6 +65,7 @@ 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, + .test_binary_policy = null_test_binary_policy, .set_binary_policy = null_set_binary_policy, .dump_statistics = null_dump_stats, .dump_ssid_types = null_dump_ssid_types, Index: root/xen-unstable.hg/xen/acm/acm_policy.c =================================================================== --- root.orig/xen-unstable.hg/xen/acm/acm_policy.c +++ root/xen-unstable.hg/xen/acm/acm_policy.c @@ -1,7 +1,7 @@ /**************************************************************** * acm_policy.c * - * Copyright (C) 2005 IBM Corporation + * Copyright (C) 2005-2007 IBM Corporation * * Author: * Reiner Sailer @@ -27,10 +27,23 @@ #include #include #include +#include #include #include #include #include +#include + +static int acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels, + struct acm_sized_buffer *errors); +static void acm_doms_change_ssidref(ssidref_t (*translator) + (const struct domain *, + const struct acm_sized_buffer *), + struct acm_sized_buffer *translation_map); +static void acm_doms_restore_ssidref(void); +static ssidref_t oldssid_to_newssid(const struct domain *d, + const struct acm_sized_buffer *map); + int acm_set_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size) @@ -50,7 +63,8 @@ acm_set_policy(XEN_GUEST_HANDLE(void) bu printk("%s: Error copying!\n",__func__); goto error_free; } - ret = do_acm_set_policy(policy_buffer, buf_size, 0); + ret = do_acm_set_policy(policy_buffer, buf_size, 0, + NULL, NULL, NULL); error_free: xfree(policy_buffer); @@ -58,75 +72,148 @@ acm_set_policy(XEN_GUEST_HANDLE(void) bu } -int -do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy) +/* a lock for preventing domains from being created */ +#define DOM_CREATE_LOCK spin_lock(&domlist_update_lock); +#define DOM_CREATE_UNLOCK spin_unlock(&domlist_update_lock); + +/* + * Update the policy of the running system by: + * - deleting ssidrefs that are not in the new policy anymore + * -> no running domain may use such an ssidref + * - assign new ssidrefs to domains based on their old ssidrefs + * + */ +static int +_acm_update_policy(void *buf, u32 buf_size, int is_bootpolicy, + struct acm_policy_buffer *pol, + struct acm_sized_buffer *deletions, + struct acm_sized_buffer *ssidchanges, + struct acm_sized_buffer *errors) { - struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf; uint32_t offset, length; - /* some sanity checking */ - if ((be32_to_cpu(pol->magic) != ACM_MAGIC) || - (buf_size != be32_to_cpu(pol->len)) || - (be32_to_cpu(pol->policy_version) != ACM_POLICY_VERSION)) - { - printk("%s: ERROR in Magic, Version, or buf size.\n", __func__); - goto error_free; + + write_lock(&acm_bin_pol_rwlock); + DOM_CREATE_LOCK + + /* + first some tests to check compatibility of new policy with + current state of system/domains + */ + + /* if ssidrefs are to be deleted, make sure no domain is using them */ + if (deletions != NULL) { + if (acm_check_deleted_ssidrefs(deletions, errors)) + goto error_lock_free; } - if (acm_active_security_policy == ACM_POLICY_UNDEFINED) { - /* setup the policy with the boot policy */ - if (acm_init_binary_policy((be32_to_cpu(pol->secondary_policy_code) << 4) | - be32_to_cpu(pol->primary_policy_code))) { - goto error_free; - } - acm_active_security_policy = - (acm_bin_pol.secondary_policy_code << 4) | acm_bin_pol.primary_policy_code; + if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) { + /* assign all domains new ssidrefs as requested */ + acm_doms_change_ssidref(oldssid_to_newssid, ssidchanges); } - /* once acm_active_security_policy is set, it cannot be changed */ - if ((be32_to_cpu(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) || - (be32_to_cpu(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code)) - { - printkd("%s: Wrong policy type in boot policy!\n", __func__); - goto error_free; + /* test primary policy data with the new ssidrefs */ + offset = be32_to_cpu(pol->primary_buffer_offset); + length = be32_to_cpu(pol->secondary_buffer_offset) - offset; + + if ( (offset + length) > buf_size || + acm_primary_ops->test_binary_policy(buf + offset, length, + is_bootpolicy, + errors)) + goto error_lock_free; + + /* test secondary policy data with the new ssidrefs */ + offset = be32_to_cpu(pol->secondary_buffer_offset); + length = be32_to_cpu(pol->len) - offset; + if ( (offset + length) > buf_size || + acm_secondary_ops->test_binary_policy(buf + offset, length, + is_bootpolicy, + errors)) { + goto error_lock_free; } - /* get bin_policy lock and rewrite policy (release old one) */ - write_lock(&acm_bin_pol_rwlock); + /* end of testing --- now real updates */ offset = be32_to_cpu(pol->policy_reference_offset); length = be32_to_cpu(pol->primary_buffer_offset) - offset; /* set label reference name */ if ( (offset + length) > buf_size || - acm_set_policy_reference(buf + offset, length)) + acm_set_policy_reference(buf + offset, length) ) goto error_lock_free; /* set primary policy data */ offset = be32_to_cpu(pol->primary_buffer_offset); length = be32_to_cpu(pol->secondary_buffer_offset) - offset; - if ( (offset + length) > buf_size || - acm_primary_ops->set_binary_policy(buf + offset, length, - is_bootpolicy)) + if ( acm_primary_ops->set_binary_policy(buf + offset, length) ) goto error_lock_free; /* set secondary policy data */ offset = be32_to_cpu(pol->secondary_buffer_offset); length = be32_to_cpu(pol->len) - offset; - if ( (offset + length) > buf_size || - acm_secondary_ops->set_binary_policy(buf + offset, length, - is_bootpolicy)) + if ( acm_secondary_ops->set_binary_policy(buf + offset, length) ) goto error_lock_free; memcpy(&acm_bin_pol.xml_pol_version, &pol->xml_pol_version, sizeof(acm_bin_pol.xml_pol_version)); + DOM_CREATE_UNLOCK write_unlock(&acm_bin_pol_rwlock); return ACM_OK; - error_lock_free: +error_lock_free: + if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) { + acm_doms_restore_ssidref(); + } + do_chwall_init_state_curr(NULL); + DOM_CREATE_UNLOCK write_unlock(&acm_bin_pol_rwlock); + + return -EFAULT; +} + + +int +do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy, + struct acm_sized_buffer *deletions, + struct acm_sized_buffer *ssidchanges, + struct acm_sized_buffer *errors) +{ + struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf; + + /* some sanity checking */ + if ((be32_to_cpu(pol->magic) != ACM_MAGIC) || + (buf_size != be32_to_cpu(pol->len)) || + (be32_to_cpu(pol->policy_version) != ACM_POLICY_VERSION)) + { + printk("%s: ERROR in Magic, Version, or buf size.\n", __func__); + goto error_free; + } + + if (acm_active_security_policy == ACM_POLICY_UNDEFINED) { + /* setup the policy with the boot policy */ + if (acm_init_binary_policy((be32_to_cpu(pol->secondary_policy_code) << 4) | + be32_to_cpu(pol->primary_policy_code))) { + goto error_free; + } + acm_active_security_policy = (acm_bin_pol.secondary_policy_code << 4) | + acm_bin_pol.primary_policy_code; + } + + /* once acm_active_security_policy is set, it cannot be changed */ + if ((be32_to_cpu(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) || + (be32_to_cpu(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code)) + { + printkd("%s: Wrong policy type in boot policy!\n", __func__); + goto error_free; + } + + return _acm_update_policy(buf, buf_size, is_bootpolicy, + pol, + deletions, ssidchanges, + errors); + error_free: printk("%s: Error setting policy.\n", __func__); return -EFAULT; @@ -156,7 +243,7 @@ acm_get_policy(XEN_GUEST_HANDLE(void) bu bin_pol->policy_reference_offset = cpu_to_be32(be32_to_cpu(bin_pol->len)); bin_pol->primary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len)); bin_pol->secondary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len)); - + memcpy(&bin_pol->xml_pol_version, &acm_bin_pol.xml_pol_version, sizeof(struct acm_policy_version)); @@ -330,6 +417,384 @@ acm_get_decision(ssidref_t ssidref1, ssi return ret; } + +/* + Check if an ssidref of the current policy type is being used by any + domain. + The caller must hold the lock to the domain list. + */ +static int +acm_check_used_ssidref(uint32_t policy_type, uint32_t idx, + struct acm_sized_buffer *errors) +{ + int rc = 0; + struct domain *d; + + for_each_domain(d) { + struct acm_ssid_domain *rawssid; + ssidref_t ssidref; + void *s; + + rawssid = (struct acm_ssid_domain *)d->ssid; + + if (rawssid == NULL) + continue; + + s = GET_SSIDP(policy_type, rawssid); + if (policy_type == ACM_CHINESE_WALL_POLICY) { + ssidref = ((struct chwall_ssid *)s)->chwall_ssidref; + } else { + ssidref = ((struct ste_ssid *)s)->ste_ssidref; + } + gdprintk(XENLOG_INFO,"domid=%d: idx=%d, ssidref=%d\n", + idx,ssidref,d->domain_id); + if (ssidref == idx) { + /* one is enough */ + acm_array_append_tuple(errors, ACM_SSIDREF_IN_USE, idx); + rc = 1; + break; + } + } + return rc; +} + + +/* + * Translate a current ssidref into its future representation under + * the new policy. + * The map provides translation of ssidrefs from old to new in tuples + * of (old ssidref, new ssidref). + */ +static ssidref_t +oldssid_to_newssid(const struct domain *d, const struct acm_sized_buffer *map) +{ + uint i; + struct acm_ssid_domain *rawssid = (struct acm_ssid_domain *)d->ssid; + + if (rawssid != NULL) { + ssidref_t ssid = rawssid->ssidref & 0xffff; + for (i = 0; i+1 < map->num_items; i += 2) { + if (map->array[i] == ssid) { + return (map->array[i+1] << 16 | map->array[i+1]); + } + } + } + return ACM_INVALID_SSIDREF; +} + + +/* + * Assign an ssidref to the CHWALL policy component of the domain + */ +static void +acm_pri_policy_assign_ssidref(struct domain *d, ssidref_t new_ssid) +{ + struct acm_ssid_domain *rawssid = (struct acm_ssid_domain *)d->ssid; + struct chwall_ssid *chwall; + + chwall = (struct chwall_ssid *)rawssid->primary_ssid; + chwall->chwall_ssidref = new_ssid; +} + + +/* + * Assign an ssidref to the STE policy component of the domain + */ +static void +acm_sec_policy_assign_ssidref(struct domain *d, ssidref_t new_ssid) +{ + struct acm_ssid_domain *rawssid = (struct acm_ssid_domain *)d->ssid; + struct ste_ssid *ste; + + ste = (struct ste_ssid *)rawssid->secondary_ssid; + ste->ste_ssidref = new_ssid; +} + +/* + change the ssidrefs on each domain using a passed translation function; + The caller must hold the write-lock to the domain list + */ +static void +acm_doms_change_ssidref(ssidref_t (*translator_fn) + (const struct domain *, + const struct acm_sized_buffer *), + struct acm_sized_buffer *translation_map) +{ + struct domain *d; + for_each_domain(d) { + ssidref_t new_ssid; + struct acm_ssid_domain *rawssid = (struct acm_ssid_domain *)d->ssid; + + rawssid->old_ssidref = rawssid->ssidref; + + new_ssid = translator_fn(d, translation_map); + if (new_ssid == ACM_INVALID_SSIDREF) { + /* means no mapping found, so no change -- old = new */ + continue; + } + + acm_pri_policy_assign_ssidref(d, ACM_PRIMARY (new_ssid) ); + acm_sec_policy_assign_ssidref(d, ACM_SECONDARY(new_ssid) ); + + rawssid->ssidref = new_ssid; + } +} + +/* + * Restore the previous ssidref values on all domains + */ +static void +acm_doms_restore_ssidref(void) +{ + struct domain *d; + for_each_domain(d) { + struct acm_ssid_domain *rawssid; + ssidref_t old_ssid; + + rawssid = (struct acm_ssid_domain *)d->ssid; + + if (rawssid == NULL || + rawssid->old_ssidref == rawssid->ssidref) + continue; + + old_ssid = rawssid->old_ssidref & 0xffff; + rawssid->ssidref = rawssid->old_ssidref; + + acm_pri_policy_assign_ssidref(d, old_ssid); + acm_sec_policy_assign_ssidref(d, old_ssid); + } +} + +/* + Check the list of domains whether either one of them uses a + to-be-deleted ssidref. + The caller must hold the lock to the domain list. + */ +static int +acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels, + struct acm_sized_buffer *errors) +{ + int rc = 0; + uint idx; + /* check for running domains that should not be there anymore */ + for (idx = 0; idx < dels->num_items; idx++) { + if (acm_check_used_ssidref(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, + dels->array[idx], + errors) > 0 || + acm_check_used_ssidref(ACM_CHINESE_WALL_POLICY, + dels->array[idx], + errors) > 0) { + rc = ACM_ERROR; + break; + } + } + return rc; +} + + +/* + * Change the policy of the system. + */ +int +acm_change_policy(struct acm_change_policy *chgpolicy) +{ + int rc = 0; + u8 *binpolicy = NULL; + struct acm_sized_buffer dels = { + .array = NULL, + }; + struct acm_sized_buffer ssidmap = { + .array = NULL, + }; + struct acm_sized_buffer errors = { + .array = NULL, + }; + + gdprintk(XENLOG_INFO, "change policy operation\n"); + + if ((chgpolicy->delarray_size > 4096) || + (chgpolicy->chgarray_size > 4096) || + (chgpolicy->errarray_size > 4096)) { + return ACM_ERROR; + } + + dels.num_items = chgpolicy->delarray_size / sizeof(uint32_t); + if (dels.num_items > 0) { + dels.array = xmalloc_array(uint32_t, dels.num_items); + if (dels.array == NULL) { + rc = -ENOMEM; + goto acm_chg_policy_exit; + } + } + + ssidmap.num_items = chgpolicy->chgarray_size / sizeof(uint32_t); + if (ssidmap.num_items > 0) { + ssidmap.array = xmalloc_array(uint32_t, ssidmap.num_items); + if (ssidmap.array == NULL) { + rc = -ENOMEM; + goto acm_chg_policy_exit; + } + } + + errors.num_items = chgpolicy->errarray_size / sizeof(uint32_t); + if (errors.num_items > 0) { + errors.array = xmalloc_array(uint32_t, errors.num_items); + if (errors.array == NULL) { + rc = -ENOMEM; + goto acm_chg_policy_exit; + } + memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items); + } + + binpolicy = xmalloc_array(u8, + chgpolicy->policy_pushcache_size); + if (binpolicy == NULL) { + rc = -ENOMEM; + goto acm_chg_policy_exit; + } + + if ( copy_from_guest(dels.array, + chgpolicy->del_array, + chgpolicy->delarray_size) || + copy_from_guest(ssidmap.array, + chgpolicy->chg_array, + chgpolicy->chgarray_size) || + copy_from_guest(binpolicy, + chgpolicy->policy_pushcache, + chgpolicy->policy_pushcache_size )) { + rc = -EFAULT; + goto acm_chg_policy_exit; + } + + rc = do_acm_set_policy(binpolicy, + chgpolicy->policy_pushcache_size, + 0, + &dels, &ssidmap, &errors); + + if ( (errors.num_items > 0) && + copy_to_guest(chgpolicy->err_array, + errors.array, + errors.num_items ) ) { + rc = -EFAULT; + goto acm_chg_policy_exit; + } + + +acm_chg_policy_exit: + xfree(dels.array); + xfree(ssidmap.array); + xfree(errors.array); + xfree(binpolicy); + + return rc; +} + + +/* + * Lookup the new ssidref given the domain's id. + * The translation map provides a list of tuples in the format + * (domid, new ssidref). + */ +static ssidref_t +domid_to_newssid(const struct domain *d, const struct acm_sized_buffer *map) +{ + domid_t domid = d->domain_id; + uint i; + for (i = 0; (i+1) < map->num_items; i += 2) { + if (map->array[i] == domid) { + return (ssidref_t)map->array[i+1]; + } + } + return ACM_INVALID_SSIDREF; +} + + +int +do_acm_relabel_doms(struct acm_sized_buffer *relabel_map, + struct acm_sized_buffer *errors) +{ + int rc = 0, irc; + + DOM_CREATE_LOCK + + acm_doms_change_ssidref(domid_to_newssid, relabel_map); + + /* run tests; collect as much error info as possible */ + irc = do_chwall_init_state_curr(errors); + irc += do_ste_init_state_curr(errors); + if (irc != 0) { + rc = -EFAULT; + goto acm_relabel_doms_lock_err_exit; + } + + DOM_CREATE_UNLOCK + return rc; + +acm_relabel_doms_lock_err_exit: + /* revert the new ssidref assignment */ + acm_doms_restore_ssidref(); + do_chwall_init_state_curr(NULL); + + DOM_CREATE_UNLOCK + + return rc; +} + + +int +acm_relabel_domains(struct acm_relabel_doms *relabel) +{ + int rc = ACM_OK; + struct acm_sized_buffer relabels; + struct acm_sized_buffer errors; + relabels.array = NULL; + errors.array = NULL; + + if (relabel->relabel_map_size > 4096) { + return ACM_ERROR; + } + + relabels.num_items = relabel->relabel_map_size / sizeof(uint32_t); + if (relabels.num_items > 0) { + relabels.array = xmalloc_array(uint32_t, relabels.num_items); + if (relabels.array == NULL) { + rc = -ENOMEM; + goto acm_relabel_doms_exit; + } + } + + errors.num_items = relabel->errarray_size / sizeof(uint32_t); + if (errors.num_items > 0) { + errors.array = xmalloc_array(uint32_t, errors.num_items); + if (errors.array == NULL) { + rc = -ENOMEM; + goto acm_relabel_doms_exit; + } + memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items); + } + + if ( copy_from_guest(relabels.array, + relabel->relabel_map, + relabel->relabel_map_size) ) { + rc = -EFAULT; + goto acm_relabel_doms_exit; + } + + rc = do_acm_relabel_doms(&relabels, &errors); + + if ( copy_to_guest(relabel->err_array, + errors.array, + errors.num_items ) ) { + rc = -EFAULT; + goto acm_relabel_doms_exit; + } + +acm_relabel_doms_exit: + xfree(relabels.array); + xfree(errors.array); + return rc; +} + /* * Local variables: * mode: C Index: root/xen-unstable.hg/xen/acm/acm_simple_type_enforcement_hooks.c =================================================================== --- root.orig/xen-unstable.hg/xen/acm/acm_simple_type_enforcement_hooks.c +++ root/xen-unstable.hg/xen/acm/acm_simple_type_enforcement_hooks.c @@ -179,7 +179,7 @@ ste_dump_policy(u8 *buf, u32 buf_size) { * (right now: event_channels, future: also grant_tables) */ static int -ste_init_state(struct acm_ste_policy_buffer *ste_buf, domaintype_t *ssidrefs) +ste_init_state(struct acm_sized_buffer *errors) { int violation = 1; struct ste_ssid *ste_ssid, *ste_rssid; @@ -189,8 +189,6 @@ ste_init_state(struct acm_ste_policy_buf struct active_grant_entry *act; int port, i; - rcu_read_lock(&domlist_read_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 */ for_each_domain ( d ) { @@ -232,12 +230,17 @@ ste_init_state(struct acm_ste_policy_buf "%x -> domain %x.\n", __func__, d->domain_id, rdomid); spin_unlock(&d->evtchn_lock); + + acm_array_append_tuple(errors, + ACM_EVTCHN_SHARING_VIOLATION, + d->domain_id << 16 | rdomid); goto out; } } spin_unlock(&d->evtchn_lock); } + /* b) check for grant table conflicts on shared pages */ spin_lock(&d->grant_table->lock); for ( i = 0; i < nr_active_grant_frames(d->grant_table); i++ ) { @@ -252,6 +255,10 @@ ste_init_state(struct acm_ste_policy_buf if ((rdom = rcu_lock_domain_by_id(rdomid)) == NULL) { spin_unlock(&d->grant_table->lock); printkd("%s: domain not found ERROR!\n", __func__); + + acm_array_append_tuple(errors, + ACM_DOMAIN_LOOKUP, + rdomid); goto out; } /* rdom now has remote domain */ @@ -264,6 +271,10 @@ ste_init_state(struct acm_ste_policy_buf printkd("%s: Policy violation in grant table " "sharing domain %x -> domain %x.\n", __func__, d->domain_id, rdomid); + + acm_array_append_tuple(errors, + ACM_GNTTAB_SHARING_VIOLATION, + d->domain_id << 16 | rdomid); goto out; } } @@ -272,7 +283,6 @@ ste_init_state(struct acm_ste_policy_buf } violation = 0; out: - rcu_read_unlock(&domlist_read_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, @@ -281,31 +291,29 @@ ste_init_state(struct acm_ste_policy_buf * type in their typesets referenced by their ssidrefs */ } + +/* + * Call ste_init_state with the current policy. + */ +int +do_ste_init_state_curr(struct acm_sized_buffer *errors) +{ + return ste_init_state(errors); +} + + /* set new policy; policy write-locked already */ static int -ste_set_policy(u8 *buf, u32 buf_size, int is_bootpolicy) +_ste_update_policy(u8 *buf, u32 buf_size, int test_only, + struct acm_sized_buffer *errors) { + int rc = -EFAULT; struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf; void *ssidrefsbuf; struct ste_ssid *ste_ssid; struct domain *d; int i; - if (buf_size < sizeof(struct acm_ste_policy_buffer)) - return -EINVAL; - - /* Convert endianess of policy */ - ste_buf->policy_code = be32_to_cpu(ste_buf->policy_code); - ste_buf->policy_version = be32_to_cpu(ste_buf->policy_version); - ste_buf->ste_max_types = be32_to_cpu(ste_buf->ste_max_types); - ste_buf->ste_max_ssidrefs = be32_to_cpu(ste_buf->ste_max_ssidrefs); - ste_buf->ste_ssid_offset = be32_to_cpu(ste_buf->ste_ssid_offset); - - /* policy type and version checks */ - if ((ste_buf->policy_code != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) || - (ste_buf->policy_version != ACM_STE_VERSION)) - return -EINVAL; - /* 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) { @@ -314,23 +322,35 @@ ste_set_policy(u8 *buf, u32 buf_size, in if (ste_buf->ste_ssid_offset + sizeof(domaintype_t) * ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types > buf_size) goto error_free; - /* during boot dom0_chwall_ssidref is set */ - if (is_bootpolicy && (dom0_ste_ssidref >= ste_buf->ste_max_ssidrefs)) { - 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; + + /* 3. in test mode: 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 */ + + if (test_only) { + /* temporarily replace old policy with new one for the testing */ + struct ste_binary_policy orig_ste_bin_pol = ste_bin_pol; + ste_bin_pol.max_types = ste_buf->ste_max_types; + ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs; + ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf; + + if (ste_init_state(NULL)) { + /* new policy conflicts with sharing of running domains */ + printk("%s: New policy conflicts with running domains. " + "Policy load aborted.\n", __func__); + } else { + rc = ACM_OK; + } + /* revert changes, no matter whether testing was successful or not */ + ste_bin_pol = orig_ste_bin_pol; + goto error_free; } + /* 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; @@ -338,20 +358,53 @@ ste_set_policy(u8 *buf, u32 buf_size, in ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf; /* clear all ste caches */ - rcu_read_lock(&domlist_read_lock); for_each_domain ( d ) { ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, (struct acm_ssid_domain *)(d)->ssid); for (i=0; iste_cache[i].valid = ACM_STE_free; } - rcu_read_unlock(&domlist_read_lock); return ACM_OK; error_free: - printk("%s: ERROR setting policy.\n", __func__); + if (!test_only) printk("%s: ERROR setting policy.\n", __func__); xfree(ssidrefsbuf); - return -EFAULT; + return rc; +} + +static int +ste_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy, + struct acm_sized_buffer *errors) +{ + struct acm_ste_policy_buffer *ste_buf = + (struct acm_ste_policy_buffer *)buf; + + if (buf_size < sizeof(struct acm_ste_policy_buffer)) + return -EINVAL; + + /* Convert endianess of policy */ + ste_buf->policy_code = be32_to_cpu(ste_buf->policy_code); + ste_buf->policy_version = be32_to_cpu(ste_buf->policy_version); + ste_buf->ste_max_types = be32_to_cpu(ste_buf->ste_max_types); + ste_buf->ste_max_ssidrefs = be32_to_cpu(ste_buf->ste_max_ssidrefs); + ste_buf->ste_ssid_offset = be32_to_cpu(ste_buf->ste_ssid_offset); + + /* policy type and version checks */ + if ((ste_buf->policy_code != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) || + (ste_buf->policy_version != ACM_STE_VERSION)) + return -EINVAL; + + /* during boot dom0_chwall_ssidref is set */ + if (is_bootpolicy && (dom0_ste_ssidref >= ste_buf->ste_max_ssidrefs)) + return -EINVAL; + + return _ste_update_policy(buf, buf_size, 1, errors); +} + +static int +ste_set_policy(u8 *buf, u32 buf_size) +{ + return _ste_update_policy(buf, buf_size, 0, NULL); } static int @@ -680,6 +733,7 @@ struct acm_operations acm_simple_type_en .init_domain_ssid = ste_init_domain_ssid, .free_domain_ssid = ste_free_domain_ssid, .dump_binary_policy = ste_dump_policy, + .test_binary_policy = ste_test_policy, .set_binary_policy = ste_set_policy, .dump_statistics = ste_dump_stats, .dump_ssid_types = ste_dump_ssid_types, Index: root/xen-unstable.hg/xen/common/acm_ops.c =================================================================== --- root.orig/xen-unstable.hg/xen/common/acm_ops.c +++ root/xen-unstable.hg/xen/common/acm_ops.c @@ -216,6 +216,42 @@ ret_t do_acm_op(int cmd, XEN_GUEST_HANDL break; } + case ACMOP_chgpolicy: { + struct acm_change_policy chgpolicy; + + if (copy_from_guest(&chgpolicy, arg, 1) != 0) + return -EFAULT; + if (chgpolicy.interface_version != ACM_INTERFACE_VERSION) + return -EACCES; + + rc = acm_change_policy(&chgpolicy); + + if (rc == 0) { + if (copy_to_guest(arg, &chgpolicy, 1) != 0) { + rc = -EFAULT; + } + } + break; + } + + case ACMOP_relabeldoms: { + struct acm_relabel_doms relabeldoms; + + if (copy_from_guest(&relabeldoms, arg, 1) != 0) + return -EFAULT; + if (relabeldoms.interface_version != ACM_INTERFACE_VERSION) + return -EACCES; + + rc = acm_relabel_domains(&relabeldoms); + + if (rc == 0) { + if (copy_to_guest(arg, &relabeldoms, 1) != 0) { + rc = -EFAULT; + } + } + break; + } + default: rc = -ENOSYS; break; Index: root/xen-unstable.hg/xen/include/acm/acm_core.h =================================================================== --- root.orig/xen-unstable.hg/xen/include/acm/acm_core.h +++ root/xen-unstable.hg/xen/include/acm/acm_core.h @@ -23,6 +23,7 @@ #include #include #include +#include /* Xen-internal representation of the binary policy */ struct acm_binary_policy { @@ -80,12 +81,13 @@ struct acm_ste_cache_line { /* general definition of a subject security id */ struct acm_ssid_domain { - int datatype; /* type of subject (e.g., partition): ACM_DATATYPE_* */ - 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 */ + int datatype; /* type of subject (e.g., partition): ACM_DATATYPE_* */ + ssidref_t ssidref; /* combined security reference */ + ssidref_t old_ssidref; /* holds previous value of ssidref during relabeling */ + 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 */ @@ -118,18 +120,51 @@ struct ste_ssid { ((POLICY) == acm_bin_pol.primary_policy_code) ? \ ((ssid)->primary_ssid) : ((ssid)->secondary_ssid) +#define ACM_INVALID_SSIDREF (0xffffffff) + +struct acm_sized_buffer +{ + uint32_t *array; + uint num_items; + uint position; +}; + +static inline int acm_array_append_tuple(struct acm_sized_buffer *buf, + uint32_t a, uint32_t b) +{ + uint i; + if (buf == NULL) + return 0; + + i = buf->position; + + if ((i + 2) > buf->num_items) + return 0; + + buf->array[i] = cpu_to_be32(a); + buf->array[i+1] = cpu_to_be32(b); + buf->position += 2; + return 1; +} + /* protos */ int acm_init_domain_ssid(domid_t id, ssidref_t ssidref); void acm_free_domain_ssid(struct acm_ssid_domain *ssid); int acm_init_binary_policy(u32 policy_code); int acm_set_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size); -int do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy); +int do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy, + struct acm_sized_buffer *, struct acm_sized_buffer *, + struct acm_sized_buffer *); int acm_get_policy(XEN_GUEST_HANDLE(void) buf, u32 buf_size); int acm_dump_statistics(XEN_GUEST_HANDLE(void) buf, u16 buf_size); int acm_get_ssid(ssidref_t ssidref, XEN_GUEST_HANDLE(void) buf, u16 buf_size); int acm_get_decision(ssidref_t ssidref1, ssidref_t ssidref2, u32 hook); int acm_set_policy_reference(u8 * buf, u32 buf_size); int acm_dump_policy_reference(u8 *buf, u32 buf_size); +int acm_change_policy(struct acm_change_policy *); +int acm_relabel_domains(struct acm_relabel_doms *); +int do_chwall_init_state_curr(struct acm_sized_buffer *); +int do_ste_init_state_curr(struct acm_sized_buffer *); /* variables */ Index: root/xen-unstable.hg/xen/include/acm/acm_hooks.h =================================================================== --- root.orig/xen-unstable.hg/xen/include/acm/acm_hooks.h +++ root/xen-unstable.hg/xen/include/acm/acm_hooks.h @@ -91,8 +91,10 @@ struct acm_operations { int (*init_domain_ssid) (void **ssid, ssidref_t ssidref); void (*free_domain_ssid) (void *ssid); int (*dump_binary_policy) (u8 *buffer, u32 buf_size); - int (*set_binary_policy) (u8 *buffer, u32 buf_size, - int is_bootpolicy); + int (*test_binary_policy) (u8 *buffer, u32 buf_size, + int is_bootpolicy, + struct acm_sized_buffer *); + int (*set_binary_policy) (u8 *buffer, u32 buf_size); int (*dump_statistics) (u8 *buffer, u16 buf_size); int (*dump_ssid_types) (ssidref_t ssidref, u8 *buffer, u16 buf_size); /* domain management control hooks (can be NULL) */ Index: root/xen-unstable.hg/xen/include/public/acm.h =================================================================== --- root.orig/xen-unstable.hg/xen/include/public/acm.h +++ root/xen-unstable.hg/xen/include/public/acm.h @@ -56,6 +56,19 @@ #define ACM_ACCESS_DENIED -111 #define ACM_NULL_POINTER_ERROR -200 +/* + Error codes reported in when trying to test for a new policy + These error codes are reported in an array of tuples where + each error code is followed by a parameter describing the error + more closely, such as a domain id. +*/ +#define ACM_EVTCHN_SHARING_VIOLATION 0x100 +#define ACM_GNTTAB_SHARING_VIOLATION 0x101 +#define ACM_DOMAIN_LOOKUP 0x102 +#define ACM_CHWALL_CONFLICT 0x103 +#define ACM_SSIDREF_IN_USE 0x104 + + /* primary policy in lower 4 bits */ #define ACM_NULL_POLICY 0 #define ACM_CHINESE_WALL_POLICY 1 Index: root/xen-unstable.hg/xen/include/public/acm_ops.h =================================================================== --- root.orig/xen-unstable.hg/xen/include/public/acm_ops.h +++ root/xen-unstable.hg/xen/include/public/acm_ops.h @@ -34,7 +34,7 @@ * This makes sure that old versions of acm tools will stop working in a * well-defined way (rather than crashing the machine, for instance). */ -#define ACM_INTERFACE_VERSION 0xAAAA0008 +#define ACM_INTERFACE_VERSION 0xAAAA0009 /************************************************************************/ @@ -107,6 +107,34 @@ struct acm_getdecision { uint32_t acm_decision; }; + +#define ACMOP_chgpolicy 6 +struct acm_change_policy { + /* IN */ + uint32_t interface_version; + XEN_GUEST_HANDLE(void) policy_pushcache; + uint32_t policy_pushcache_size; + XEN_GUEST_HANDLE(void) del_array; + uint32_t delarray_size; + XEN_GUEST_HANDLE(void) chg_array; + uint32_t chgarray_size; + /* OUT */ + /* array with error code */ + XEN_GUEST_HANDLE(void) err_array; + uint32_t errarray_size; +}; + +#define ACMOP_relabeldoms 7 +struct acm_relabel_doms { + /* IN */ + uint32_t interface_version; + XEN_GUEST_HANDLE(void) relabel_map; + uint32_t relabel_map_size; + /* OUT */ + XEN_GUEST_HANDLE(void) err_array; + uint32_t errarray_size; +}; + #endif /* __XEN_PUBLIC_ACM_OPS_H__ */ /*