WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-changelog

[Xen-changelog] [xen-unstable] acm: Updating a policy on a running syste

To: xen-changelog@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-changelog] [xen-unstable] acm: Updating a policy on a running system.
From: Xen patchbot-unstable <patchbot-unstable@xxxxxxxxxxxxxxxxxxx>
Date: Wed, 25 Apr 2007 05:20:08 -0700
Delivery-date: Wed, 25 Apr 2007 05:19:25 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-changelog-request@lists.xensource.com?subject=help>
List-id: BK change log <xen-changelog.lists.xensource.com>
List-post: <mailto:xen-changelog@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-changelog>, <mailto:xen-changelog-request@lists.xensource.com?subject=unsubscribe>
Reply-to: xen-devel@xxxxxxxxxxxxxxxxxxx
Sender: xen-changelog-bounces@xxxxxxxxxxxxxxxxxxx
# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1177490402 -3600
# Node ID 4677ee247aa9a94ddf787077be12e022a38dd99f
# Parent  d5d6d2a8d10c0e03c18af1db00d7b09fd488073b
acm: Updating a policy on a running system.

Allow a policy to be updated on a running system and domains to be
relabeled. The updating of a policy is happening in several steps:
relabeling the domains, testing whether the system would be in a valid
state after the relabeling (according to the policy), committing the
changes if state is determined to be valid.

I have followed Keir's suggestion of building a 2nd linked list
parallel to the domain list. That 2nd list holds security information
related to the running domains. Each entry is pointed to by its domain
structure. The list is protected by its own read/write-lock. I have
moved nearly all ACM-related code that was traversing the domain list
previously to traverse this list instead and not hold onto the domain
list lock.

Signed-off-by: Stefan Berger <stefanb@xxxxxxxxxx>
---
 xen/acm/acm_chinesewall_hooks.c             |  162 +++++---
 xen/acm/acm_core.c                          |   24 +
 xen/acm/acm_null_hooks.c                    |   10 
 xen/acm/acm_policy.c                        |  548 +++++++++++++++++++++++++---
 xen/acm/acm_simple_type_enforcement_hooks.c |  181 ++++++---
 xen/common/acm_ops.c                        |   36 +
 xen/include/acm/acm_core.h                  |   60 ++-
 xen/include/acm/acm_hooks.h                 |   75 ++-
 xen/include/public/acm.h                    |   13 
 xen/include/public/acm_ops.h                |   56 ++
 10 files changed, 961 insertions(+), 204 deletions(-)

diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/acm/acm_chinesewall_hooks.c
--- a/xen/acm/acm_chinesewall_hooks.c   Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/acm/acm_chinesewall_hooks.c   Wed Apr 25 09:40:02 2007 +0100
@@ -183,29 +183,33 @@ 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);
+    struct acm_ssid_domain *rawssid;
+
+    read_lock(&ssid_list_rwlock);
+
     /* go through all domains and adjust policy as if this domain was started 
now */
-    for_each_domain ( d )
+    for_each_acmssid( rawssid )
     {
         chwall_ssid =
-            GET_SSIDP(ACM_CHINESE_WALL_POLICY,
-                      (struct acm_ssid_domain *)d->ssid);
+            GET_SSIDP(ACM_CHINESE_WALL_POLICY, rawssid);
         chwall_ssidref = chwall_ssid->chwall_ssidref;
         traceprintk("%s: validating policy for domain %x (chwall-REF=%x).\n",
                     __func__, d->domain_id, chwall_ssidref);
@@ -222,6 +226,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 +256,7 @@ chwall_init_state(struct acm_chwall_poli
         }
     }
  out:
-    spin_unlock(&domlist_update_lock);
+    read_unlock(&ssid_list_rwlock);
     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,43 +264,44 @@ 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 =
@@ -343,12 +351,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 +380,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)
@@ -590,6 +655,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,
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/acm/acm_core.c
--- a/xen/acm/acm_core.c        Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/acm/acm_core.c        Wed Apr 25 09:40:02 2007 +0100
@@ -62,9 +62,13 @@ struct acm_binary_policy acm_bin_pol;
 /* acm binary policy lock */
 DEFINE_RWLOCK(acm_bin_pol_rwlock);
 
-/* ACM's only accepted policy name */
+/* ACM's only accepted policy name during boot */
 char polname[80];
 char *acm_accepted_boot_policy_name = NULL;
+
+/* a lits of all chained ssid structures */
+LIST_HEAD(ssid_list);
+DEFINE_RWLOCK(ssid_list_rwlock);
 
 static void __init set_dom0_ssidref(const char *val)
 {
@@ -77,7 +81,7 @@ static void __init set_dom0_ssidref(cons
     int i;
     int dom0_ssidref = simple_strtoull(val, &c, 0);
 
-    if (!strncmp(&c[0],":sHype:", 7)) {
+    if (!strncmp(&c[0],":ACM:", 5)) {
         lo = dom0_ssidref & 0xffff;
         if (lo < ACM_MAX_NUM_TYPES && lo >= 1)
             dom0_chwall_ssidref = lo;
@@ -249,7 +253,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);
@@ -337,9 +342,10 @@ int acm_init_domain_ssid_new(struct doma
         return ACM_INIT_SSID_ERROR;
     }
 
+    INIT_LIST_HEAD(&ssid->node);
     ssid->datatype       = ACM_DATATYPE_domain;
     ssid->subject        = subj;
-    ssid->domainid      = subj->domain_id;
+    ssid->domainid       = subj->domain_id;
     ssid->primary_ssid   = NULL;
     ssid->secondary_ssid = NULL;
 
@@ -367,6 +373,11 @@ int acm_init_domain_ssid_new(struct doma
         acm_free_domain_ssid(ssid);
         return ACM_INIT_SSID_ERROR;
     }
+
+    write_lock(&ssid_list_rwlock);
+    list_add(&ssid->node, &ssid_list);
+    write_unlock(&ssid_list_rwlock);
+
     printkd("%s: assigned domain %x the ssidref=%x.\n",
            __func__, subj->domain_id, ssid->ssidref);
     return ACM_OK;
@@ -387,6 +398,11 @@ acm_free_domain_ssid(struct acm_ssid_dom
     if (acm_secondary_ops->free_domain_ssid != NULL)
         acm_secondary_ops->free_domain_ssid(ssid->secondary_ssid);
     ssid->secondary_ssid = NULL;
+
+    write_lock(&ssid_list_rwlock);
+    list_del(&ssid->node);
+    write_unlock(&ssid_list_rwlock);
+
     xfree(ssid);
     printkd("%s: Freed individual domain ssid (domain=%02x).\n",
             __func__, id);
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/acm/acm_null_hooks.c
--- a/xen/acm/acm_null_hooks.c  Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/acm/acm_null_hooks.c  Wed Apr 25 09:40:02 2007 +0100
@@ -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,
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/acm/acm_policy.c
--- a/xen/acm/acm_policy.c      Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/acm/acm_policy.c      Wed Apr 25 09:40:02 2007 +0100
@@ -1,7 +1,7 @@
 /****************************************************************
  * acm_policy.c
  * 
- * Copyright (C) 2005 IBM Corporation
+ * Copyright (C) 2005-2007 IBM Corporation
  *
  * Author:
  * Reiner Sailer <sailer@xxxxxxxxxxxxxx>
@@ -27,10 +27,23 @@
 #include <xen/delay.h>
 #include <xen/sched.h>
 #include <xen/guest_access.h>
+#include <public/xen.h>
 #include <acm/acm_core.h>
 #include <public/acm_ops.h>
 #include <acm/acm_hooks.h>
 #include <acm/acm_endian.h>
+#include <asm/current.h>
+
+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 acm_ssid_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 acm_ssid_domain *,
+                                    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,11 +72,109 @@ acm_set_policy(XEN_GUEST_HANDLE(void) bu
 }
 
 
-int
-do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy)
+/*
+ * 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)
+{
+    uint32_t offset, length;
+
+    write_lock(&acm_bin_pol_rwlock);
+
+    /*
+       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 ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
+        /* assign all running domains new ssidrefs as requested */
+        acm_doms_change_ssidref(oldssid_to_newssid, ssidchanges);
+    }
+
+    /* 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;
+    }
+
+    /* 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) )
+        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 ( 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 ( 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));
+
+    write_unlock(&acm_bin_pol_rwlock);
+    return ACM_OK;
+
+error_lock_free:
+    if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
+        acm_doms_restore_ssidref();
+    }
+    do_chwall_init_state_curr(NULL);
+    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;
-    uint32_t offset, length;
+
     /* some sanity checking */
     if ((be32_to_cpu(pol->magic) != ACM_MAGIC) ||
         (buf_size != be32_to_cpu(pol->len)) ||
@@ -78,8 +190,8 @@ do_acm_set_policy(void *buf, u32 buf_siz
                                    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;
+        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 */
@@ -90,43 +202,11 @@ do_acm_set_policy(void *buf, u32 buf_siz
         goto error_free;
     }
 
-    /* get bin_policy lock and rewrite policy (release old one) */
-    write_lock(&acm_bin_pol_rwlock);
-
-    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))
-        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))
-        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))
-        goto error_lock_free;
-
-    memcpy(&acm_bin_pol.xml_pol_version,
-           &pol->xml_pol_version,
-           sizeof(acm_bin_pol.xml_pol_version));
-
-    write_unlock(&acm_bin_pol_rwlock);
-    return ACM_OK;
-
- error_lock_free:
-    write_unlock(&acm_bin_pol_rwlock);
+    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 +236,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));
@@ -328,6 +408,384 @@ acm_get_decision(ssidref_t ssidref1, ssi
             (ret == ACM_ACCESS_PERMITTED) ? "GRANTED" : "DENIED");
 
     return ret;
+}
+
+
+
+/*
+   Check if an ssidref of the current policy type is being used by any
+   domain.
+ */
+static int
+acm_check_used_ssidref(uint32_t policy_type, uint32_t search_ssidref,
+                       struct acm_sized_buffer *errors)
+{
+    int rc = 0;
+    struct acm_ssid_domain *rawssid;
+
+    read_lock(&ssid_list_rwlock);
+
+    for_each_acmssid( rawssid ) {
+        ssidref_t ssidref;
+        void *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: search ssidref=%d, ssidref=%d\n",
+                 rawssid->domainid,search_ssidref,ssidref);
+        if (ssidref == search_ssidref) {
+            /* one is enough */
+            acm_array_append_tuple(errors, ACM_SSIDREF_IN_USE, search_ssidref);
+            rc = 1;
+            break;
+        }
+    }
+
+    read_unlock(&ssid_list_rwlock);
+
+    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 acm_ssid_domain *rawssid,
+                   const struct acm_sized_buffer *map)
+{
+    uint i;
+
+    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 acm_ssid_domain *rawssid, ssidref_t 
new_ssid)
+{
+    struct chwall_ssid *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 acm_ssid_domain *rawssid, ssidref_t 
new_ssid)
+{
+    struct ste_ssid *ste = (struct ste_ssid *)rawssid->secondary_ssid;
+    ste->ste_ssidref = new_ssid;
+}
+
+/*
+   Change the ssidrefs on each domain using a passed translation function;
+ */
+static void
+acm_doms_change_ssidref(ssidref_t (*translator_fn)
+                          (const struct acm_ssid_domain *,
+                           const struct acm_sized_buffer *),
+                        struct acm_sized_buffer *translation_map)
+{
+    struct acm_ssid_domain *rawssid;
+
+    write_lock(&ssid_list_rwlock);
+
+    for_each_acmssid( rawssid ) {
+        ssidref_t new_ssid;
+
+        rawssid->old_ssidref = rawssid->ssidref;
+
+        new_ssid = translator_fn(rawssid, translation_map);
+        if (new_ssid == ACM_INVALID_SSIDREF) {
+            /* means no mapping found, so no change -- old = new */
+            continue;
+        }
+
+        acm_pri_policy_assign_ssidref(rawssid, ACM_PRIMARY  (new_ssid) );
+        acm_sec_policy_assign_ssidref(rawssid, ACM_SECONDARY(new_ssid) );
+
+        rawssid->ssidref = new_ssid;
+    }
+
+    write_unlock(&ssid_list_rwlock);
+}
+
+/*
+ * Restore the previous ssidref values on all domains
+ */
+static void
+acm_doms_restore_ssidref(void)
+{
+    struct acm_ssid_domain *rawssid;
+
+    write_lock(&ssid_list_rwlock);
+
+    for_each_acmssid( rawssid ) {
+        ssidref_t old_ssid;
+
+        if (rawssid->old_ssidref == rawssid->ssidref)
+            continue;
+
+        old_ssid = rawssid->old_ssidref & 0xffff;
+        rawssid->ssidref = rawssid->old_ssidref;
+
+        acm_pri_policy_assign_ssidref(rawssid, old_ssid);
+        acm_sec_policy_assign_ssidref(rawssid, old_ssid);
+    }
+
+    write_unlock(&ssid_list_rwlock);
+}
+
+
+/*
+   Check the list of domains whether either one of them uses a
+   to-be-deleted ssidref.
+ */
+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 acm_ssid_domain *rawssid,
+                 const struct acm_sized_buffer *map)
+{
+    domid_t domid = rawssid->domainid;
+    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;
+
+    write_lock(&acm_bin_pol_rwlock);
+
+    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;
+    }
+
+    write_unlock(&acm_bin_pol_rwlock);
+
+    return rc;
+
+acm_relabel_doms_lock_err_exit:
+    /* revert the new ssidref assignment */
+    acm_doms_restore_ssidref();
+    do_chwall_init_state_curr(NULL);
+
+    write_unlock(&acm_bin_pol_rwlock);
+
+    return rc;
+}
+
+
+int
+acm_relabel_domains(struct acm_relabel_doms *relabel)
+{
+    int rc = ACM_OK;
+    struct acm_sized_buffer relabels = {
+        .array = NULL,
+    };
+    struct acm_sized_buffer 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;
 }
 
 /*
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/acm/acm_simple_type_enforcement_hooks.c
--- a/xen/acm/acm_simple_type_enforcement_hooks.c       Wed Apr 25 09:31:52 
2007 +0100
+++ b/xen/acm/acm_simple_type_enforcement_hooks.c       Wed Apr 25 09:40:02 
2007 +0100
@@ -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;
@@ -190,7 +190,7 @@ ste_init_state(struct acm_ste_policy_buf
     int port, i;
 
     rcu_read_lock(&domlist_read_lock);
-    /* go by domain? or directly by global? event/grant list */
+    read_lock(&ssid_list_rwlock);
     /* go through all domains and adjust policy as if this domain was started 
now */
     for_each_domain ( d )
     {
@@ -232,11 +232,16 @@ 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);
@@ -252,6 +257,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 +273,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,6 +285,7 @@ ste_init_state(struct acm_ste_policy_buf
     }
     violation = 0;
  out:
+    read_unlock(&ssid_list_rwlock);
     rcu_read_unlock(&domlist_read_lock);
     return violation;
     /* returning "violation != 0" means that existing sharing between domains 
would not 
@@ -281,15 +295,98 @@ 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;
+    struct acm_ssid_domain *rawssid;
     int i;
+
+
+    /* 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);
+
+
+    /* 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 (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;
+    xfree(ste_bin_pol.ssidrefs);
+    ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf;
+
+    /* clear all ste caches */
+    read_lock(&ssid_list_rwlock);
+
+    for_each_acmssid( rawssid ) {
+        ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid);
+        for (i=0; i<ACM_TE_CACHE_SIZE; i++)
+            ste_ssid->ste_cache[i].valid = ACM_STE_free;
+    }
+
+    read_unlock(&ssid_list_rwlock);
+
+    return ACM_OK;
+
+ error_free:
+    if (!test_only) printk("%s: ERROR setting policy.\n", __func__);
+    xfree(ssidrefsbuf);
+    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;
@@ -306,52 +403,17 @@ ste_set_policy(u8 *buf, u32 buf_size, in
         (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) {
-        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;
-
     /* 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;
-     *    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;
-    xfree(ste_bin_pol.ssidrefs);
-    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; i<ACM_TE_CACHE_SIZE; i++)
-            ste_ssid->ste_cache[i].valid = ACM_STE_free;
-    }
-    rcu_read_unlock(&domlist_read_lock);
-    return ACM_OK;
-
- error_free:
-    printk("%s: ERROR setting policy.\n", __func__);
-    xfree(ssidrefsbuf);
-    return -EFAULT;
+    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 
@@ -447,18 +509,15 @@ clean_id_from_cache(domid_t id)
 {
     struct ste_ssid *ste_ssid;
     int i;
-    struct domain *d;
-    struct acm_ssid_domain *ssid;
+    struct acm_ssid_domain *rawssid;
 
     printkd("deleting cache for dom %x.\n", id);
-    rcu_read_lock(&domlist_read_lock);
+    read_lock(&ssid_list_rwlock);
     /* look through caches of all domains */
-    for_each_domain ( d ) {
-        ssid = (struct acm_ssid_domain *)(d->ssid);
-
-        if (ssid == NULL)
-            continue; /* hanging domain structure, no ssid any more ... */
-        ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssid);
+
+    for_each_acmssid ( rawssid ) {
+
+        ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid);
         if (!ste_ssid) {
             printk("%s: deleting ID from cache ERROR (no ste_ssid)!\n",
                    __func__);
@@ -469,8 +528,9 @@ clean_id_from_cache(domid_t id)
                 (ste_ssid->ste_cache[i].id == id))
                 ste_ssid->ste_cache[i].valid = ACM_STE_free;
     }
+
  out:
-    rcu_read_unlock(&domlist_read_lock);
+    read_unlock(&ssid_list_rwlock);
 }
 
 /***************************
@@ -687,6 +747,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,
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/common/acm_ops.c
--- a/xen/common/acm_ops.c      Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/common/acm_ops.c      Wed Apr 25 09:40:02 2007 +0100
@@ -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;
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/include/acm/acm_core.h
--- a/xen/include/acm/acm_core.h        Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/include/acm/acm_core.h        Wed Apr 25 09:40:02 2007 +0100
@@ -20,9 +20,11 @@
 #define _ACM_CORE_H
 
 #include <xen/spinlock.h>
+#include <xen/list.h>
 #include <public/acm.h>
 #include <xen/acm_policy.h>
 #include <public/acm_ops.h>
+#include <acm/acm_endian.h>
 
 /* Xen-internal representation of the binary policy */
 struct acm_binary_policy {
@@ -58,6 +60,7 @@ extern struct ste_binary_policy ste_bin_
 extern struct ste_binary_policy ste_bin_pol;
 /* use the lock when reading / changing binary policy ! */
 extern rwlock_t acm_bin_pol_rwlock;
+extern rwlock_t ssid_list_rwlock;
 
 /* subject and object type definitions */
 #define ACM_DATATYPE_domain 1
@@ -80,12 +83,14 @@ 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 */
+    struct list_head node; /* all are chained together */
+    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,25 +123,64 @@ 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);
 int acm_init_domain_ssid_new(struct domain *, 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 */
 extern ssidref_t dom0_chwall_ssidref;
 extern ssidref_t dom0_ste_ssidref;
 #define ACM_MAX_NUM_TYPES   (256)
+
+/* traversing the list of ssids */
+extern struct list_head ssid_list;
+#define for_each_acmssid( N )                               \
+   for ( N =  (struct acm_ssid_domain *)ssid_list.next;     \
+         N != (struct acm_ssid_domain *)&ssid_list;         \
+         N =  (struct acm_ssid_domain *)N->node.next     )
 
 #endif
 
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/include/acm/acm_hooks.h
--- a/xen/include/acm/acm_hooks.h       Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/include/acm/acm_hooks.h       Wed Apr 25 09:40:02 2007 +0100
@@ -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) */
@@ -228,37 +230,6 @@ static inline int acm_pre_grant_setup(do
 }
 
 
-static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
-{
-    void *subject_ssid = current->domain->ssid;
-    domid_t domid = d->domain_id;
-    int rc;
-
-    /*
-       To be called when a domain is created; returns '0' if the
-       domain is allowed to be created, != '0' if not.
-     */
-    rc = acm_init_domain_ssid_new(d, ssidref);
-    if (rc != ACM_OK)
-        return rc;
-
-    if ((acm_primary_ops->domain_create != NULL) &&
-        acm_primary_ops->domain_create(subject_ssid, ssidref, domid)) {
-        return ACM_ACCESS_DENIED;
-    } else if ((acm_secondary_ops->domain_create != NULL) &&
-                acm_secondary_ops->domain_create(subject_ssid, ssidref,
-                                                 domid)) {
-        /* roll-back primary */
-        if (acm_primary_ops->domain_destroy != NULL)
-            acm_primary_ops->domain_destroy(d->ssid, d);
-        acm_free_domain_ssid(d->ssid);
-        return ACM_ACCESS_DENIED;
-    }
-
-    return 0;
-}
-
-
 static inline void acm_domain_destroy(struct domain *d)
 {
     void *ssid = d->ssid;
@@ -270,6 +241,44 @@ static inline void acm_domain_destroy(st
         /* free security ssid for the destroyed domain (also if null policy */
         acm_free_domain_ssid((struct acm_ssid_domain *)(ssid));
     }
+}
+
+
+static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
+{
+    void *subject_ssid = current->domain->ssid;
+    domid_t domid = d->domain_id;
+    int rc = 0;
+
+    read_lock(&acm_bin_pol_rwlock);
+    /*
+       To be called when a domain is created; returns '0' if the
+       domain is allowed to be created, != '0' if not.
+     */
+
+    if ((acm_primary_ops->domain_create != NULL) &&
+        acm_primary_ops->domain_create(subject_ssid, ssidref, domid)) {
+        rc = ACM_ACCESS_DENIED;
+    } else if ((acm_secondary_ops->domain_create != NULL) &&
+                acm_secondary_ops->domain_create(subject_ssid, ssidref,
+                                                 domid)) {
+        /* roll-back primary */
+        if (acm_primary_ops->domain_destroy != NULL)
+            acm_primary_ops->domain_destroy(d->ssid, d);
+        acm_free_domain_ssid(d->ssid);
+        rc = ACM_ACCESS_DENIED;
+    }
+
+    if (rc == 0) {
+        rc = acm_init_domain_ssid_new(d, ssidref);
+
+        if (rc != ACM_OK) {
+            acm_domain_destroy(d);
+        }
+    }
+
+    read_unlock(&acm_bin_pol_rwlock);
+    return rc;
 }
 
 
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/include/public/acm.h
--- a/xen/include/public/acm.h  Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/include/public/acm.h  Wed Apr 25 09:40:02 2007 +0100
@@ -55,6 +55,19 @@
 #define ACM_ACCESS_PERMITTED        0
 #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
diff -r d5d6d2a8d10c -r 4677ee247aa9 xen/include/public/acm_ops.h
--- a/xen/include/public/acm_ops.h      Wed Apr 25 09:31:52 2007 +0100
+++ b/xen/include/public/acm_ops.h      Wed Apr 25 09:40:02 2007 +0100
@@ -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
 
 /************************************************************************/
 
@@ -50,7 +50,7 @@ struct acm_setpolicy {
 struct acm_setpolicy {
     /* IN */
     uint32_t interface_version;
-    XEN_GUEST_HANDLE(void) pushcache;
+    XEN_GUEST_HANDLE_64(void) pushcache;
     uint32_t pushcache_size;
 };
 
@@ -59,7 +59,7 @@ struct acm_getpolicy {
 struct acm_getpolicy {
     /* IN */
     uint32_t interface_version;
-    XEN_GUEST_HANDLE(void) pullcache;
+    XEN_GUEST_HANDLE_64(void) pullcache;
     uint32_t pullcache_size;
 };
 
@@ -68,7 +68,7 @@ struct acm_dumpstats {
 struct acm_dumpstats {
     /* IN */
     uint32_t interface_version;
-    XEN_GUEST_HANDLE(void) pullcache;
+    XEN_GUEST_HANDLE_64(void) pullcache;
     uint32_t pullcache_size;
 };
 
@@ -84,7 +84,7 @@ struct acm_getssid {
         domaintype_t domainid;
         ssidref_t    ssidref;
     } id;
-    XEN_GUEST_HANDLE(void) ssidbuf;
+    XEN_GUEST_HANDLE_64(void) ssidbuf;
     uint32_t ssidbuf_size;
 };
 
@@ -107,6 +107,52 @@ struct acm_getdecision {
     uint32_t acm_decision;
 };
 
+
+#define ACMOP_chgpolicy        6
+struct acm_change_policy {
+    /* IN */
+    uint32_t interface_version;
+    XEN_GUEST_HANDLE_64(void) policy_pushcache;
+    uint32_t policy_pushcache_size;
+    XEN_GUEST_HANDLE_64(void) del_array;
+    uint32_t delarray_size;
+    XEN_GUEST_HANDLE_64(void) chg_array;
+    uint32_t chgarray_size;
+    /* OUT */
+    /* array with error code */
+    XEN_GUEST_HANDLE_64(void) err_array;
+    uint32_t errarray_size;
+};
+
+#define ACMOP_relabeldoms       7
+struct acm_relabel_doms {
+    /* IN */
+    uint32_t interface_version;
+    XEN_GUEST_HANDLE_64(void) relabel_map;
+    uint32_t relabel_map_size;
+    /* OUT */
+    XEN_GUEST_HANDLE_64(void) err_array;
+    uint32_t errarray_size;
+};
+
+/* future interface to Xen */
+struct xen_acmctl {
+    uint32_t cmd;
+    uint32_t interface_version;
+    union {
+        struct acm_setpolicy     setpolicy;
+        struct acm_getpolicy     getpolicy;
+        struct acm_dumpstats     dumpstats;
+        struct acm_getssid       getssid;
+        struct acm_getdecision   getdecision;
+        struct acm_change_policy change_policy;
+        struct acm_relabel_doms  relabel_doms;
+    } u;
+};
+
+typedef struct xen_acmctl xen_acmctl_t;
+DEFINE_XEN_GUEST_HANDLE(xen_acmctl_t);
+
 #endif /* __XEN_PUBLIC_ACM_OPS_H__ */
 
 /*

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-changelog] [xen-unstable] acm: Updating a policy on a running system., Xen patchbot-unstable <=