ia64/xen-unstable

changeset 10576:25c6ea6d4024

This patch adds new xm subcommands to support working with resource
labels. The new subcommands are 'xm resources', 'xm rmlabel', 'xm
getlabel' and 'xm dry-run'. In addition, the 'xm addlabel' subcommand
now uses an updated syntax to support labeling both domains and
resources. See the xm man page for details on each subcommand.

Beyond the new subcommands, this patch allows users to immediately see
when security checks will fail by pushing some basic security checking
into the beginning of 'xm create' and 'xm block-attach'. ACM security
attributes for block devices are added to XenStore in order to support
the final security enforcement, which will be performed in the kernel
and included in a separate patch.

Signed-off-by: Bryan D. Payne <bdpayne@us.ibm.com>
Signed-off-by: Reiner Sailer <sailer@us.ibm.com>
author emellor@leeni.uk.xensource.com
date Wed Jun 28 16:05:27 2006 +0100 (2006-06-28)
parents ae245d35457b
children 1d17ddd9e45d
files docs/man/xm.pod.1 tools/python/xen/util/security.py tools/python/xen/xend/server/blkif.py tools/python/xen/xm/addlabel.py tools/python/xen/xm/create.py tools/python/xen/xm/dry-run.py tools/python/xen/xm/getlabel.py tools/python/xen/xm/main.py tools/python/xen/xm/resources.py tools/python/xen/xm/rmlabel.py
line diff
     1.1 --- a/docs/man/xm.pod.1	Wed Jun 28 13:59:29 2006 +0100
     1.2 +++ b/docs/man/xm.pod.1	Wed Jun 28 16:05:27 2006 +0100
     1.3 @@ -875,14 +875,43 @@ defined in the I<policy>. Unless specifi
     1.4  the currently enforced access control policy. The default for I<type>
     1.5  is 'dom'. The labels are arranged in alphabetical order.
     1.6  
     1.7 -=item B<addlabel> I<configfile> I<label> [I<policy>]
     1.8 +=item B<addlabel> I<label> dom I<configfile> [I<policy>]
     1.9 +
    1.10 +=item B<addlabel> I<label> res I<resource> [I<policy>]
    1.11  
    1.12  Adds the security label with name I<label> to a domain
    1.13 -I<configfile>. Unless specified, the default I<policy> is the
    1.14 +I<configfile> (dom) or to the global resource label file for the
    1.15 +given I<resource> (res). Unless specified, the default I<policy> is the
    1.16  currently enforced access control policy. This subcommand also
    1.17  verifies that the I<policy> definition supports the specified I<label>
    1.18  name.
    1.19  
    1.20 +=item B<rmlabel> dom I<configfile>
    1.21 +
    1.22 +=item B<rmlabel> res I<resource>
    1.23 +
    1.24 +Works the same as the I<addlabel> command (above), except that this
    1.25 +command will remove the label from the domain I<configfile> (dom) or
    1.26 +the global resource label file (res).
    1.27 +
    1.28 +=item B<getlabel> dom I<configfile>
    1.29 +
    1.30 +=item B<getlabel> res I<resource>
    1.31 +
    1.32 +Shows the label for the given I<configfile> or I<resource>
    1.33 +
    1.34 +=item B<resources>
    1.35 +
    1.36 +Lists all resources in the global resource label file.  Each resource
    1.37 +is listed with its associated label and policy name.
    1.38 +
    1.39 +=item B<dry-run> I<configfile>
    1.40 +
    1.41 +Determines if the specified I<configfile> describes a domain with a valid
    1.42 +security configuration for type enforcement. The test shows the policy
    1.43 +decision made for each resource label against the domain label as well as
    1.44 +the overall decision.
    1.45 +
    1.46  B<CONFIGURING SECURITY>
    1.47  
    1.48  =over 4
    1.49 @@ -960,17 +989,18 @@ B<ATTACHING A SECURITY LABEL TO A DOMAIN
    1.50  
    1.51  =over 4
    1.52  
    1.53 -This subcommand attaches a security label to a domain configuration
    1.54 -file, here a HomeBanking label. The example policy ensures that this
    1.55 -domain does not share information with other non-hombanking user
    1.56 -domains (i.e., domains labeled as dom_Fun or dom_Boinc) and that it
    1.57 -will not run simultaneously with domains labeled as dom_Fun.
    1.58 +The I<addlabel> subcommand can attach a security label to a domain
    1.59 +configuration file, here a HomeBanking label. The example policy
    1.60 +ensures that this domain does not share information with other
    1.61 +non-hombanking user domains (i.e., domains labeled as dom_Fun or
    1.62 +dom_Boinc) and that it will not run simultaneously with domains
    1.63 +labeled as dom_Fun.
    1.64  
    1.65  We assume that the specified myconfig.xm configuration file actually
    1.66  instantiates a domain that runs workloads related to home-banking,
    1.67  probably just a browser environment for online-banking.
    1.68  
    1.69 -    xm addlabel myconfig.xm dom_HomeBanking
    1.70 +    xm addlabel dom_HomeBanking dom myconfig.xm
    1.71  
    1.72  The very simple configuration file might now look as printed
    1.73  below. The I<addlabel> subcommand added the B<access_control> entry at
    1.74 @@ -997,6 +1027,38 @@ permitted".
    1.75  
    1.76  =back
    1.77  
    1.78 +B<ATTACHING A SECURITY LABEL TO A RESOURCE>
    1.79 +
    1.80 +=over 4
    1.81 +
    1.82 +The I<addlabel> subcommand can also be used to attach a security
    1.83 +label to a resource. Following the home banking example from above,
    1.84 +we can label a disk resource (e.g., a physical partition or a file)
    1.85 +to make it accessible to the home banking domain. The example policy
    1.86 +provides a resource label, res_LogicalDiskPartition1(hda1), that is
    1.87 +compatible with the HomeBanking domain label.
    1.88 +
    1.89 +    xm addlabel "res_LogicalDiskPartition1(hda1)" res phy:hda6
    1.90 +
    1.91 +After labeling this disk resource, it can be attached to the domain
    1.92 +by adding a line to the domain configuration file. The line below
    1.93 +attaches this disk to the domain at boot time.
    1.94 +
    1.95 +    disk = [ 'phy:hda6,sda2,w' ]
    1.96 +
    1.97 +Alternatively, the resource can be attached after booting the domain
    1.98 +by using the I<block-attach> subcommand.
    1.99 +
   1.100 +    xm block-attach homebanking phy:hda6 sda2 w
   1.101 +
   1.102 +Note that labeled resources cannot be used when security is turned
   1.103 +off.  Any attempt to use labeled resources with security turned off
   1.104 +will result in a failure with a corresponding error message.  The
   1.105 +solution is to enable security or, if security is no longer desired,
   1.106 +to remove the resource label using the I<rmlabel> subcommand.
   1.107 +
   1.108 +=back
   1.109 +
   1.110  B<STARTING AND LISTING LABELED DOMAINS>
   1.111  
   1.112  =over 4
   1.113 @@ -1011,6 +1073,21 @@ B<STARTING AND LISTING LABELED DOMAINS>
   1.114  
   1.115  =back
   1.116  
   1.117 +B<LISTING LABELED RESOURCES>
   1.118 +
   1.119 +=over 4
   1.120 +
   1.121 +    xm resources
   1.122 +
   1.123 +      phy:hda6
   1.124 +          policy: example.chwall_ste.client_v1
   1.125 +          label:  res_LogicalDiskPartition1(hda1)
   1.126 +      file:/xen/disk_image/disk.img
   1.127 +          policy: example.chwall_ste.client_v1
   1.128 +          label:  res_LogicalDiskPartition2(hda2)
   1.129 +
   1.130 +=back
   1.131 +
   1.132  B<POLICY REPRESENTATIONS>
   1.133  
   1.134  =over 4
     2.1 --- a/tools/python/xen/util/security.py	Wed Jun 28 13:59:29 2006 +0100
     2.2 +++ b/tools/python/xen/util/security.py	Wed Jun 28 16:05:27 2006 +0100
     2.3 @@ -14,6 +14,7 @@
     2.4  #============================================================================
     2.5  # Copyright (C) 2006 International Business Machines Corp.
     2.6  # Author: Reiner Sailer
     2.7 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
     2.8  #============================================================================
     2.9  
    2.10  import commands
    2.11 @@ -21,11 +22,14 @@ import logging
    2.12  import sys, os, string, re
    2.13  import traceback
    2.14  import shutil
    2.15 +from xml.marshal import generic
    2.16  from xen.lowlevel import acm
    2.17  from xen.xend import sxp
    2.18 +from xen.xend.XendLogging import log
    2.19  
    2.20  #global directories and tools for security management
    2.21  policy_dir_prefix = "/etc/xen/acm-security/policies"
    2.22 +res_label_filename = policy_dir_prefix + "/resource_labels"
    2.23  boot_filename = "/boot/grub/menu.lst"
    2.24  xensec_xml2bin = "/usr/sbin/xensec_xml2bin"
    2.25  xensec_tool = "/usr/sbin/xensec_tool"
    2.26 @@ -530,3 +534,98 @@ def list_labels(policy_name, condition):
    2.27              if label not in labels:
    2.28                  labels.append(label)
    2.29      return labels
    2.30 +
    2.31 +
    2.32 +def get_res_label(resource):
    2.33 +    """Returns resource label information (label, policy) if it exists.
    2.34 +       Otherwise returns null label and policy.
    2.35 +    """
    2.36 +    def default_res_label():
    2.37 +        ssidref = NULL_SSIDREF
    2.38 +        if on():
    2.39 +            label = ssidref2label(ssidref)
    2.40 +        else:
    2.41 +            label = None
    2.42 +        return (label, 'NULL')
    2.43 +
    2.44 +    (label, policy) = default_res_label()
    2.45 +
    2.46 +    # load the resource label file
    2.47 +    configfile = res_label_filename
    2.48 +    if not os.path.isfile(configfile):
    2.49 +        log.info("Resource label file not found.")
    2.50 +        return default_res_label()
    2.51 +    fd = open(configfile, "rb")
    2.52 +    res_label_cache = generic.load(fd)
    2.53 +    fd.close()
    2.54 +
    2.55 +    # find the resource information
    2.56 +    if res_label_cache.has_key(resource):
    2.57 +        (policy, label) = res_label_cache[resource]
    2.58 +
    2.59 +    return (label, policy)
    2.60 +
    2.61 +
    2.62 +def get_res_security_details(resource):
    2.63 +    """Returns the (label, ssidref, policy) associated with a given
    2.64 +       resource from the global resource label file.
    2.65 +    """
    2.66 +    def default_security_details():
    2.67 +        ssidref = NULL_SSIDREF
    2.68 +        if on():
    2.69 +            label = ssidref2label(ssidref)
    2.70 +        else:
    2.71 +            label = None
    2.72 +        policy = active_policy
    2.73 +        return (label, ssidref, policy)
    2.74 +
    2.75 +    (label, ssidref, policy) = default_security_details()
    2.76 +
    2.77 +    # find the entry associated with this resource
    2.78 +    (label, policy) = get_res_label(resource)
    2.79 +    if policy == 'NULL':
    2.80 +        log.info("Resource label for "+resource+" not in file, using DEFAULT.")
    2.81 +        return default_security_details()
    2.82 +
    2.83 +    # is this resource label for the running policy?
    2.84 +    if policy == active_policy:
    2.85 +        ssidref = label2ssidref(label, policy, 'res')
    2.86 +    else:
    2.87 +        log.info("Resource label not for active policy, using DEFAULT.")
    2.88 +        return default_security_details()
    2.89 +
    2.90 +    return (label, ssidref, policy)
    2.91 +
    2.92 +
    2.93 +def res_security_check(resource, domain_label):
    2.94 +    """Checks if the given resource can be used by the given domain
    2.95 +       label.  Returns 1 if the resource can be used, otherwise 0.
    2.96 +    """
    2.97 +    rtnval = 1
    2.98 +
    2.99 +    # if security is on, ask the hypervisor for a decision
   2.100 +    if on():
   2.101 +        (label, ssidref, policy) = get_res_security_details(resource)
   2.102 +        domac = ['access_control']
   2.103 +        domac.append(['policy', active_policy])
   2.104 +        domac.append(['label', domain_label])
   2.105 +        domac.append(['type', 'dom'])
   2.106 +        decision = get_decision(domac, ['ssidref', str(ssidref)])
   2.107 +
   2.108 +        # provide descriptive error messages
   2.109 +        if decision == 'DENIED':
   2.110 +            if label == ssidref2label(NULL_SSIDREF):
   2.111 +                raise ACMError("Resource '"+resource+"' is not labeled")
   2.112 +                rtnval = 0
   2.113 +            else:
   2.114 +                raise ACMError("Permission denied for resource '"+resource+"' because label '"+label+"' is not allowed")
   2.115 +                rtnval = 0
   2.116 +
   2.117 +    # security is off, make sure resource isn't labeled
   2.118 +    else:
   2.119 +        (label, policy) = get_res_label(resource)
   2.120 +        if policy != 'NULL':
   2.121 +            raise ACMError("Security is off, but '"+resource+"' is labeled")
   2.122 +            rtnval = 0
   2.123 +
   2.124 +    return rtnval
     3.1 --- a/tools/python/xen/xend/server/blkif.py	Wed Jun 28 13:59:29 2006 +0100
     3.2 +++ b/tools/python/xen/xend/server/blkif.py	Wed Jun 28 16:05:27 2006 +0100
     3.3 @@ -21,6 +21,7 @@ import re
     3.4  import string
     3.5  
     3.6  from xen.util import blkif
     3.7 +from xen.util import security
     3.8  from xen.xend import sxp
     3.9  from xen.xend.XendError import VmError
    3.10  
    3.11 @@ -40,15 +41,22 @@ class BlkifController(DevController):
    3.12  
    3.13      def getDeviceDetails(self, config):
    3.14          """@see DevController.getDeviceDetails"""
    3.15 +        uname = sxp.child_value(config, 'uname')
    3.16  
    3.17          dev = sxp.child_value(config, 'dev')
    3.18  
    3.19 -        (typ, params) = string.split(sxp.child_value(config, 'uname'), ':', 1)
    3.20 +        (typ, params) = string.split(uname, ':', 1)
    3.21          back = { 'dev'    : dev,
    3.22                   'type'   : typ,
    3.23                   'params' : params,
    3.24                   'mode'   : sxp.child_value(config, 'mode', 'r')
    3.25 -                 }
    3.26 +               }
    3.27 +
    3.28 +        if security.on():
    3.29 +            (label, ssidref, policy) = security.get_res_security_details(uname)
    3.30 +            back.update({'acm_label'  : label,
    3.31 +                         'acm_ssidref': str(ssidref),
    3.32 +                         'acm_policy' : policy})
    3.33  
    3.34          if 'ioemu:' in dev:
    3.35              (dummy, dev1) = string.split(dev, ':', 1)
     4.1 --- a/tools/python/xen/xm/addlabel.py	Wed Jun 28 13:59:29 2006 +0100
     4.2 +++ b/tools/python/xen/xm/addlabel.py	Wed Jun 28 16:05:27 2006 +0100
     4.3 @@ -14,61 +14,156 @@
     4.4  #============================================================================
     4.5  # Copyright (C) 2006 International Business Machines Corp.
     4.6  # Author: Reiner Sailer <sailer@us.ibm.com>
     4.7 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
     4.8  #============================================================================
     4.9  
    4.10 -"""Labeling a domain configuration file.
    4.11 +"""Labeling a domain configuration file or a resoruce.
    4.12  """
    4.13  import sys, os
    4.14 +import string
    4.15  import traceback
    4.16 -
    4.17 -
    4.18 -from xen.util.security import ACMError, err, active_policy, label2ssidref, on, access_control_re
    4.19 -
    4.20 +from xml.marshal import generic
    4.21 +from xen.util import security
    4.22  
    4.23  def usage():
    4.24 -    print "\nUsage: xm addlabel <configfile> <label> [<policy>]\n"
    4.25 -    print "  This program adds an acm_label entry into the 'configfile'."
    4.26 -    print "  It derives the policy from the running hypervisor if it"
    4.27 -    print "  is not given (optional parameter). If the configfile is"
    4.28 -    print "  already labeled, then addlabel fails.\n"
    4.29 -    err("Usage")
    4.30 +    print "\nUsage: xm addlabel <label> dom <configfile> [<policy>]"
    4.31 +    print "       xm addlabel <label> res <resource> [<policy>]\n"
    4.32 +    print "  This program adds an acm_label entry into the 'configfile'"
    4.33 +    print "  for a domain or to the global resource label file for a"
    4.34 +    print "  resource. It derives the policy from the running hypervisor"
    4.35 +    print "  if it is not given (optional parameter). If a label already"
    4.36 +    print "  exists for the given domain or resource, then addlabel fails.\n"
    4.37 +    security.err("Usage")
    4.38 +
    4.39 +def validate_config_file(configfile):
    4.40 +    """Performs a simple sanity check on the configuration file passed on
    4.41 +       the command line.  We basically just want to make sure that it's
    4.42 +       not a domain image file so we check for a few configuration values
    4.43 +       and then we are satisfied.  Returned 1 on success, otherwise 0.
    4.44 +    """
    4.45 +    # read in the config file
    4.46 +    globs = {}
    4.47 +    locs = {}
    4.48 +    try:
    4.49 +        execfile(configfile, globs, locs)
    4.50 +    except:
    4.51 +        print "Invalid configuration file."
    4.52 +        return 0
    4.53 +
    4.54 +    # sanity check on the data from the file
    4.55 +    count = 0
    4.56 +    required = ['kernel', 'memory', 'name']
    4.57 +    for (k, v) in locs.items():
    4.58 +        if k in required:
    4.59 +            count += 1
    4.60 +    if count != 3:
    4.61 +        print "Invalid configuration file."
    4.62 +        return 0
    4.63 +    else:
    4.64 +        return 1
    4.65  
    4.66  
    4.67 -def main(argv):
    4.68 +def add_resource_label(label, resource, policyref):
    4.69 +    """Adds a resource label to the global resource label file.
    4.70 +    """
    4.71      try:
    4.72 -        policyref = None
    4.73 -        if len(argv) not in [3,4]:
    4.74 -            usage()
    4.75 -        configfile = argv[1]
    4.76 -        label = argv[2]
    4.77 -
    4.78 -        if len(argv) == 4:
    4.79 -            policyref = argv[3]
    4.80 -        elif on():
    4.81 -            policyref = active_policy
    4.82 -        else:
    4.83 -            err("No active policy. Policy must be specified in command line.")
    4.84 +        # sanity check: make sure this label can be instantiated later on
    4.85 +        ssidref = security.label2ssidref(label, policyref, 'res')
    4.86  
    4.87 -        #sanity checks: make sure this label can be instantiated later on
    4.88 -        ssidref = label2ssidref(label, policyref, 'dom')
    4.89 +        # sanity check on resource name
    4.90 +        (type, file) = resource.split(":")
    4.91 +        if type == "phy":
    4.92 +            file = "/dev/" + file
    4.93 +        if not os.path.exists(file):
    4.94 +            print "Invalid resource '"+resource+"'"
    4.95 +            return
    4.96  
    4.97 -        new_label = "access_control = ['policy=%s,label=%s']\n" % (policyref, label)
    4.98 -        if not os.path.isfile(configfile):
    4.99 -            err("Configuration file \'" + configfile + "\' not found.")
   4.100 -        config_fd = open(configfile, "ra+")
   4.101 -        for line in config_fd:
   4.102 -            if not access_control_re.match(line):
   4.103 -                continue
   4.104 -            config_fd.close()
   4.105 -            err("Config file \'" + configfile + "\' is already labeled.")
   4.106 -        config_fd.write(new_label)
   4.107 -        config_fd.close()
   4.108 +        # see if this resource is already in the file
   4.109 +        file = security.res_label_filename
   4.110 +        if not os.path.isfile(file):
   4.111 +            print "Resource file not found, creating new file at:"
   4.112 +            print "%s" % (file)
   4.113 +            fd = open(file, "w")
   4.114 +            fd.close();
   4.115 +            access_control = {}
   4.116 +        else:
   4.117 +            fd = open(file, "rb")
   4.118 +            access_control = generic.load(fd)
   4.119 +            fd.close()
   4.120  
   4.121 -    except ACMError:
   4.122 +        if access_control.has_key(resource):
   4.123 +            security.err("This resource is already labeled.")
   4.124 +
   4.125 +        # write the data to file
   4.126 +        new_entry = { resource : tuple([policyref, label]) }
   4.127 +        access_control.update(new_entry)
   4.128 +        fd = open(file, "wb")
   4.129 +        generic.dump(access_control, fd)
   4.130 +        fd.close()
   4.131 +
   4.132 +    except security.ACMError:
   4.133          pass
   4.134      except:
   4.135          traceback.print_exc(limit=1)
   4.136  
   4.137 +def add_domain_label(label, configfile, policyref):
   4.138 +    try:
   4.139 +        # sanity checks: make sure this label can be instantiated later on
   4.140 +        ssidref = security.label2ssidref(label, policyref, 'dom')
   4.141 +
   4.142 +        new_label = "access_control = ['policy=%s,label=%s']\n" % (policyref, label)
   4.143 +        if not os.path.isfile(configfile):
   4.144 +            security.err("Configuration file \'" + configfile + "\' not found.")
   4.145 +        config_fd = open(configfile, "ra+")
   4.146 +        for line in config_fd:
   4.147 +            if not security.access_control_re.match(line):
   4.148 +                continue
   4.149 +            config_fd.close()
   4.150 +            security.err("Config file \'" + configfile + "\' is already labeled.")
   4.151 +        config_fd.write(new_label)
   4.152 +        config_fd.close()
   4.153 +
   4.154 +    except security.ACMError:
   4.155 +        pass
   4.156 +    except:
   4.157 +        traceback.print_exc(limit=1)
   4.158 +
   4.159 +def main (argv):
   4.160 +    try:
   4.161 +        policyref = None
   4.162 +        if len(argv) not in [4,5]:
   4.163 +            usage()
   4.164 +        label = argv[1]
   4.165 +
   4.166 +        if len(argv) == 5:
   4.167 +            policyref = argv[4]
   4.168 +        elif security.on():
   4.169 +            policyref = security.active_policy
   4.170 +        else:
   4.171 +            security.err("No active policy. Policy must be specified in command line.")
   4.172 +
   4.173 +        if argv[2].lower() == "dom":
   4.174 +            configfile = argv[3]
   4.175 +            if configfile[0] != '/':
   4.176 +                for prefix in [".", "/etc/xen"]:
   4.177 +                    configfile = prefix + "/" + configfile
   4.178 +                    if os.path.isfile(configfile):
   4.179 +                        fd = open(configfile, "rb")
   4.180 +                        break
   4.181 +            if not validate_config_file(configfile):
   4.182 +                usage()
   4.183 +            else:
   4.184 +                add_domain_label(label, configfile, policyref)
   4.185 +        elif argv[2].lower() == "res":
   4.186 +            resource = argv[3]
   4.187 +            add_resource_label(label, resource, policyref)
   4.188 +        else:
   4.189 +            usage()
   4.190 +
   4.191 +    except security.ACMError:
   4.192 +        pass
   4.193 +    except:
   4.194 +        traceback.print_exc(limit=1)
   4.195  
   4.196  if __name__ == '__main__':
   4.197      main(sys.argv)
     5.1 --- a/tools/python/xen/xm/create.py	Wed Jun 28 13:59:29 2006 +0100
     5.2 +++ b/tools/python/xen/xm/create.py	Wed Jun 28 16:05:27 2006 +0100
     5.3 @@ -988,6 +988,56 @@ def parseCommandLine(argv):
     5.4      return (gopts, config)
     5.5  
     5.6  
     5.7 +def config_security_check(config, verbose):
     5.8 +    """Checks each resource listed in the config to see if the active
     5.9 +       policy will permit creation of a new domain using the config.
    5.10 +       Returns 1 if the config passes all tests, otherwise 0.
    5.11 +    """
    5.12 +    answer = 1
    5.13 +
    5.14 +    # get the domain acm_label
    5.15 +    domain_label = None
    5.16 +    domain_policy = None
    5.17 +    for x in sxp.children(config):
    5.18 +        if sxp.name(x) == 'security':
    5.19 +            domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
    5.20 +            domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
    5.21 +
    5.22 +    # if no domain label, use default
    5.23 +    if not domain_label and security.on():
    5.24 +        domain_label = security.ssidref2label(security.NULL_SSIDREF)
    5.25 +        domain_policy = 'NULL'
    5.26 +    elif not domain_label:
    5.27 +        domain_label = ""
    5.28 +        domain_policy = 'NULL'
    5.29 +
    5.30 +    if verbose:
    5.31 +        print "Checking resources:"
    5.32 +
    5.33 +    # build a list of all resources in the config file
    5.34 +    resources = []
    5.35 +    for x in sxp.children(config):
    5.36 +        if sxp.name(x) == 'device':
    5.37 +            if sxp.name(sxp.child0(x)) == 'vbd':
    5.38 +                resources.append(sxp.child_value(sxp.child0(x), 'uname'))
    5.39 +
    5.40 +    # perform a security check on each resource
    5.41 +    for resource in resources:
    5.42 +        try:
    5.43 +            security.res_security_check(resource, domain_label)
    5.44 +            if verbose:
    5.45 +                print "   %s: PERMITTED" % (resource)
    5.46 +
    5.47 +        except security.ACMError:
    5.48 +            print "   %s: DENIED" % (resource)
    5.49 +            (res_label, res_policy) = security.get_res_label(resource)
    5.50 +            print "   --> res:"+res_label+" ("+res_policy+")"
    5.51 +            print "   --> dom:"+domain_label+" ("+domain_policy+")"
    5.52 +            answer = 0
    5.53 +
    5.54 +    return answer
    5.55 +
    5.56 +
    5.57  def main(argv):
    5.58      try:
    5.59          (opts, config) = parseCommandLine(argv)
    5.60 @@ -1000,9 +1050,12 @@ def main(argv):
    5.61      if opts.vals.dryrun:
    5.62          PrettyPrint.prettyprint(config)
    5.63      else:
    5.64 -        dom = make_domain(opts, config)
    5.65 -        if opts.vals.console_autoconnect:
    5.66 -            console.execConsole(dom)
    5.67 +        if not config_security_check(config, verbose=0):
    5.68 +            err("Resource access violation")
    5.69 +        else:
    5.70 +            dom = make_domain(opts, config)
    5.71 +            if opts.vals.console_autoconnect:
    5.72 +                console.execConsole(dom)
    5.73          
    5.74  if __name__ == '__main__':
    5.75      main(sys.argv)
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/tools/python/xen/xm/dry-run.py	Wed Jun 28 16:05:27 2006 +0100
     6.3 @@ -0,0 +1,95 @@
     6.4 +#============================================================================
     6.5 +# This library is free software; you can redistribute it and/or
     6.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     6.7 +# License as published by the Free Software Foundation.
     6.8 +#
     6.9 +# This library is distributed in the hope that it will be useful,
    6.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.12 +# Lesser General Public License for more details.
    6.13 +#
    6.14 +# You should have received a copy of the GNU Lesser General Public
    6.15 +# License along with this library; if not, write to the Free Software
    6.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.17 +#============================================================================
    6.18 +# Copyright (C) 2006 International Business Machines Corp.
    6.19 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
    6.20 +#============================================================================
    6.21 +
    6.22 +"""Tests the security settings for a domain and its resources.
    6.23 +"""
    6.24 +from xen.util import security
    6.25 +from xen.xm import create
    6.26 +from xen.xend import sxp
    6.27 +
    6.28 +def usage():
    6.29 +    print "\nUsage: xm dry-run <configfile>\n"
    6.30 +    print "This program checks each resource listed in the configfile"
    6.31 +    print "to see if the domain created by the configfile can access"
    6.32 +    print "the resources.  The status of each resource is listed"
    6.33 +    print "individually along with the final security decision.\n"
    6.34 +
    6.35 +
    6.36 +def check_domain_label(config):
    6.37 +    """All that we need to check here is that the domain label exists and
    6.38 +       is not null when security is on.  Other error conditions are
    6.39 +       handled when the config file is parsed.
    6.40 +    """
    6.41 +    answer = 0
    6.42 +    secon = 0
    6.43 +    default_label = security.ssidref2label(security.NULL_SSIDREF)
    6.44 +    if security.on():
    6.45 +        secon = 1
    6.46 +
    6.47 +    # get the domain acm_label
    6.48 +    dom_label = None
    6.49 +    dom_name = None
    6.50 +    for x in sxp.children(config):
    6.51 +        if sxp.name(x) == 'security':
    6.52 +            dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
    6.53 +        if sxp.name(x) == 'name':
    6.54 +            dom_name = sxp.child0(x)
    6.55 +
    6.56 +    # sanity check on domain label
    6.57 +    print "Checking domain:"
    6.58 +    if (not secon) and (not dom_label):
    6.59 +        print "   %s: PERMITTED" % (dom_name)
    6.60 +        answer = 1
    6.61 +    elif (secon) and (dom_label) and (dom_label != default_label):
    6.62 +        print "   %s: PERMITTED" % (dom_name)
    6.63 +        answer = 1
    6.64 +    else:
    6.65 +        print "   %s: DENIED" % (dom_name)
    6.66 +        if not secon:
    6.67 +            print "   --> Security off, but domain labeled"
    6.68 +        else:
    6.69 +            print "   --> Domain not labeled"
    6.70 +        answer = 0
    6.71 +
    6.72 +    return answer
    6.73 +
    6.74 +
    6.75 +def main (argv):
    6.76 +    if len(argv) != 2:
    6.77 +        usage()
    6.78 +        return
    6.79 +
    6.80 +    try:
    6.81 +        passed = 0
    6.82 +        (opts, config) = create.parseCommandLine(argv)
    6.83 +        if check_domain_label(config):
    6.84 +            if create.config_security_check(config, verbose=1):
    6.85 +                passed = 1
    6.86 +        else:
    6.87 +            print "Checking resources: (skipped)"
    6.88 +
    6.89 +        if passed:
    6.90 +            print "Dry Run: PASSED"
    6.91 +        else:
    6.92 +            print "Dry Run: FAILED"
    6.93 +    except security.ACMError:
    6.94 +        pass
    6.95 +
    6.96 +
    6.97 +if __name__ == '__main__':
    6.98 +    main(sys.argv)
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/tools/python/xen/xm/getlabel.py	Wed Jun 28 16:05:27 2006 +0100
     7.3 @@ -0,0 +1,134 @@
     7.4 +#============================================================================
     7.5 +# This library is free software; you can redistribute it and/or
     7.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     7.7 +# License as published by the Free Software Foundation.
     7.8 +#
     7.9 +# This library is distributed in the hope that it will be useful,
    7.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    7.12 +# Lesser General Public License for more details.
    7.13 +#
    7.14 +# You should have received a copy of the GNU Lesser General Public
    7.15 +# License along with this library; if not, write to the Free Software
    7.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    7.17 +#============================================================================
    7.18 +# Copyright (C) 2006 International Business Machines Corp.
    7.19 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
    7.20 +#============================================================================
    7.21 +
    7.22 +"""Show the label for a domain or resoruce.
    7.23 +"""
    7.24 +import sys, os, re
    7.25 +import string
    7.26 +import traceback
    7.27 +from xml.marshal import generic
    7.28 +from xen.util import security
    7.29 +
    7.30 +def usage():
    7.31 +    print "\nUsage: xm getlabel dom <configfile>"
    7.32 +    print "       xm getlabel res <resource>\n"
    7.33 +    print "  This program shows the label for a domain or resource.\n"
    7.34 +
    7.35 +
    7.36 +def get_resource_label(resource):
    7.37 +    """Gets the resource label
    7.38 +    """
    7.39 +    try:
    7.40 +        # read in the resource file
    7.41 +        file = security.res_label_filename
    7.42 +        if os.path.isfile(file):
    7.43 +            fd = open(file, "rb")
    7.44 +            access_control = generic.load(fd)
    7.45 +            fd.close()
    7.46 +        else:
    7.47 +            print "Resource label file not found"
    7.48 +            return
    7.49 +
    7.50 +        # get the entry and print label
    7.51 +        if access_control.has_key(resource):
    7.52 +            policy = access_control[resource][0]
    7.53 +            label = access_control[resource][1]
    7.54 +            print "policy="+policy+",label="+label
    7.55 +        else:
    7.56 +            print "Resource not labeled"
    7.57 +            return
    7.58 +
    7.59 +    except security.ACMError:
    7.60 +        pass
    7.61 +    except:
    7.62 +        traceback.print_exc(limit=1)
    7.63 +
    7.64 +
    7.65 +def get_domain_label(configfile):
    7.66 +    try:
    7.67 +        # open the domain config file
    7.68 +        fd = None
    7.69 +        file = None
    7.70 +        if configfile[0] == '/':
    7.71 +            fd = open(configfile, "rb")
    7.72 +        else:
    7.73 +            for prefix in [".", "/etc/xen"]:
    7.74 +                file = prefix + "/" + configfile
    7.75 +                if os.path.isfile(file):
    7.76 +                    fd = open(file, "rb")
    7.77 +                    break
    7.78 +        if not fd:
    7.79 +            print "Configuration file '"+configfile+"' not found."
    7.80 +            return
    7.81 +
    7.82 +        # read in the domain config file, finding the label line
    7.83 +        ac_entry_re = re.compile("^access_control\s*=.*", re.IGNORECASE)
    7.84 +        ac_exit_re = re.compile(".*'\].*")
    7.85 +        acline = ""
    7.86 +        record = 0
    7.87 +        for line in fd.readlines():
    7.88 +            if ac_entry_re.match(line):
    7.89 +                record = 1
    7.90 +            if record:
    7.91 +                acline = acline + line
    7.92 +            if record and ac_exit_re.match(line):
    7.93 +                record = 0
    7.94 +        fd.close()
    7.95 +
    7.96 +        # send error message if we didn't find anything
    7.97 +        if acline == "":
    7.98 +            print "Label does not exist in domain configuration file."
    7.99 +            return
   7.100 +
   7.101 +        # print out the label
   7.102 +        (title, data) = acline.split("=", 1)
   7.103 +        data = data.strip()
   7.104 +        data = data.lstrip("[\'")
   7.105 +        data = data.rstrip("\']")
   7.106 +        (p, l) = data.split(",")
   7.107 +        print data
   7.108 +
   7.109 +    except security.ACMError:
   7.110 +        pass
   7.111 +    except:
   7.112 +        traceback.print_exc(limit=1)
   7.113 +
   7.114 +
   7.115 +def main (argv):
   7.116 +    try:
   7.117 +        if len(argv) != 3:
   7.118 +            usage()
   7.119 +            return
   7.120 +
   7.121 +        if argv[1].lower() == "dom":
   7.122 +            configfile = argv[2]
   7.123 +            get_domain_label(configfile)
   7.124 +        elif argv[1].lower() == "res":
   7.125 +            resource = argv[2]
   7.126 +            get_resource_label(resource)
   7.127 +        else:
   7.128 +            usage()
   7.129 +
   7.130 +    except security.ACMError:
   7.131 +        traceback.print_exc(limit=1)
   7.132 +
   7.133 +
   7.134 +if __name__ == '__main__':
   7.135 +    main(sys.argv)
   7.136 +
   7.137 +
     8.1 --- a/tools/python/xen/xm/main.py	Wed Jun 28 13:59:29 2006 +0100
     8.2 +++ b/tools/python/xen/xm/main.py	Wed Jun 28 16:05:27 2006 +0100
     8.3 @@ -30,6 +30,7 @@ import socket
     8.4  import warnings
     8.5  warnings.filterwarnings('ignore', category=FutureWarning)
     8.6  import xmlrpclib
     8.7 +import traceback
     8.8  
     8.9  import xen.xend.XendProtocol
    8.10  
    8.11 @@ -119,7 +120,11 @@ vnet_list_help = "vnet-list [-l|--long] 
    8.12  vnet_create_help = "vnet-create <config>             create a vnet from a config file"
    8.13  vnet_delete_help = "vnet-delete <vnetid>             delete a vnet"
    8.14  vtpm_list_help = "vtpm-list <DomId> [--long]       list virtual TPM devices"
    8.15 -addlabel_help =  "addlabel <ConfigFile> <label>    Add security label to ConfigFile"
    8.16 +addlabel_help =  "addlabel <label> dom <configfile> Add security label to domain\n            <label> res <resource>   or resource"
    8.17 +rmlabel_help =  "rmlabel dom <configfile>         Remove security label from domain\n           res <resource>           or resource"
    8.18 +getlabel_help =  "getlabel dom <configfile>        Show security label for domain\n            res <resource>          or resource"
    8.19 +dry_run_help =  "dry-run <configfile>             Tests if domain can access its resources"
    8.20 +resources_help =  "resources                        Show info for each labeled resource"
    8.21  cfgbootpolicy_help = "cfgbootpolicy <policy>           Add policy to boot configuration "
    8.22  dumppolicy_help = "dumppolicy                       Print hypervisor ACM state information"
    8.23  loadpolicy_help = "loadpolicy <policy>              Load binary policy into hypervisor"
    8.24 @@ -203,6 +208,10 @@ vnet_commands = [
    8.25  acm_commands = [
    8.26      "labels",
    8.27      "addlabel",
    8.28 +    "rmlabel",
    8.29 +    "getlabel",
    8.30 +    "dry-run",
    8.31 +    "resources",
    8.32      "makepolicy",
    8.33      "loadpolicy",
    8.34      "cfgbootpolicy",
    8.35 @@ -992,6 +1001,19 @@ def xm_block_attach(args):
    8.36      if len(args) == 5:
    8.37          vbd.append(['backend', args[4]])
    8.38  
    8.39 +    # verify that policy permits attaching this resource
    8.40 +    try:
    8.41 +        dominfo = server.xend.domain(dom)
    8.42 +        domid = sxp.child_value(dominfo, 'domid')
    8.43 +        (tmp1, label, tmp2, tmp3) = security.get_ssid(domid)
    8.44 +        security.res_security_check(args[1], label)
    8.45 +    except security.ACMError, e:
    8.46 +        print e.value
    8.47 +        sys.exit(1)
    8.48 +    except:
    8.49 +        traceback.print_exc(limit=1)
    8.50 +        sys.exit(1)
    8.51 +
    8.52      server.xend.domain.device_create(dom, vbd)
    8.53  
    8.54  
    8.55 @@ -1124,6 +1146,10 @@ subcommands = [
    8.56      'shutdown',
    8.57      'labels',
    8.58      'addlabel',
    8.59 +    'rmlabel',
    8.60 +    'getlabel',
    8.61 +    'dry-run',
    8.62 +    'resources',
    8.63      'cfgbootpolicy',
    8.64      'makepolicy',
    8.65      'loadpolicy',
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tools/python/xen/xm/resources.py	Wed Jun 28 16:05:27 2006 +0100
     9.3 @@ -0,0 +1,70 @@
     9.4 +#============================================================================
     9.5 +# This library is free software; you can redistribute it and/or
     9.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
     9.7 +# License as published by the Free Software Foundation.
     9.8 +#
     9.9 +# This library is distributed in the hope that it will be useful,
    9.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    9.12 +# Lesser General Public License for more details.
    9.13 +#
    9.14 +# You should have received a copy of the GNU Lesser General Public
    9.15 +# License along with this library; if not, write to the Free Software
    9.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    9.17 +#============================================================================
    9.18 +# Copyright (C) 2006 International Business Machines Corp.
    9.19 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
    9.20 +#============================================================================
    9.21 +
    9.22 +"""List the resource label information from the global resource label file
    9.23 +"""
    9.24 +import sys, os
    9.25 +import string
    9.26 +import traceback
    9.27 +from xml.marshal import generic
    9.28 +from xen.util import security
    9.29 +
    9.30 +def usage():
    9.31 +    print "\nUsage: xm resource\n"
    9.32 +    print "  This program lists information for each resource in the"
    9.33 +    print "  global resource label file\n"
    9.34 +
    9.35 +
    9.36 +def print_resource_data(access_control):
    9.37 +    """Prints out a resource dictionary to stdout
    9.38 +    """
    9.39 +    for resource in access_control:
    9.40 +        (policy, label) = access_control[resource]
    9.41 +        print resource
    9.42 +        print "    policy: "+policy
    9.43 +        print "    label:  "+label
    9.44 +
    9.45 +
    9.46 +def get_resource_data():
    9.47 +    """Returns the resource dictionary.
    9.48 +    """
    9.49 +    file = security.res_label_filename
    9.50 +    if not os.path.isfile(file):
    9.51 +        security.err("Resource file not found.")
    9.52 +
    9.53 +    fd = open(file, "rb")
    9.54 +    access_control = generic.load(fd)
    9.55 +    fd.close()
    9.56 +    return access_control
    9.57 +
    9.58 +
    9.59 +def main (argv):
    9.60 +    try:
    9.61 +        access_control = get_resource_data()
    9.62 +        print_resource_data(access_control)
    9.63 +
    9.64 +    except security.ACMError:
    9.65 +        pass
    9.66 +    except:
    9.67 +        traceback.print_exc(limit=1)
    9.68 +
    9.69 +
    9.70 +if __name__ == '__main__':
    9.71 +    main(sys.argv)
    9.72 +
    9.73 +
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/tools/python/xen/xm/rmlabel.py	Wed Jun 28 16:05:27 2006 +0100
    10.3 @@ -0,0 +1,134 @@
    10.4 +#============================================================================
    10.5 +# This library is free software; you can redistribute it and/or
    10.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public
    10.7 +# License as published by the Free Software Foundation.
    10.8 +#
    10.9 +# This library is distributed in the hope that it will be useful,
   10.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   10.12 +# Lesser General Public License for more details.
   10.13 +#
   10.14 +# You should have received a copy of the GNU Lesser General Public
   10.15 +# License along with this library; if not, write to the Free Software
   10.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   10.17 +#============================================================================
   10.18 +# Copyright (C) 2006 International Business Machines Corp.
   10.19 +# Author: Bryan D. Payne <bdpayne@us.ibm.com>
   10.20 +#============================================================================
   10.21 +
   10.22 +"""Remove a label from a domain configuration file or a resoruce.
   10.23 +"""
   10.24 +import sys, os, re
   10.25 +import string
   10.26 +import traceback
   10.27 +from xml.marshal import generic
   10.28 +from xen.util import security
   10.29 +
   10.30 +def usage():
   10.31 +    print "\nUsage: xm rmlabel dom <configfile>"
   10.32 +    print "       xm rmlabel res <resource>\n"
   10.33 +    print "  This program removes an acm_label entry from the 'configfile'"
   10.34 +    print "  for a domain or from the global resource label file for a"
   10.35 +    print "  resource. If the label does not exist for the given domain or"
   10.36 +    print "  resource, then rmlabel fails.\n"
   10.37 +
   10.38 +
   10.39 +def rm_resource_label(resource):
   10.40 +    """Removes a resource label from the global resource label file.
   10.41 +    """
   10.42 +    try:
   10.43 +        # read in the resource file
   10.44 +        file = security.res_label_filename
   10.45 +        if os.path.isfile(file):
   10.46 +            fd = open(file, "rb")
   10.47 +            access_control = generic.load(fd)
   10.48 +            fd.close()
   10.49 +        else:
   10.50 +            security.err("Resource file not found, cannot remove label!")
   10.51 +
   10.52 +        # remove the entry and update file
   10.53 +        if access_control.has_key(resource):
   10.54 +            del access_control[resource]
   10.55 +            fd = open(file, "wb")
   10.56 +            generic.dump(access_control, fd)
   10.57 +            fd.close()
   10.58 +        else:
   10.59 +            security.err("Label does not exist in resource label file.")
   10.60 +
   10.61 +    except security.ACMError:
   10.62 +        pass
   10.63 +    except:
   10.64 +        traceback.print_exc(limit=1)
   10.65 +
   10.66 +
   10.67 +def rm_domain_label(configfile):
   10.68 +    try:
   10.69 +        # open the domain config file
   10.70 +        fd = None
   10.71 +        file = None
   10.72 +        if configfile[0] == '/':
   10.73 +            fd = open(configfile, "rb")
   10.74 +        else:
   10.75 +            for prefix in [".", "/etc/xen"]:
   10.76 +                file = prefix + "/" + configfile
   10.77 +                if os.path.isfile(file):
   10.78 +                    fd = open(file, "rb")
   10.79 +                    break
   10.80 +        if not fd:
   10.81 +            security.err("Configuration file '"+configfile+"' not found.")
   10.82 +
   10.83 +        # read in the domain config file, removing label
   10.84 +        ac_entry_re = re.compile("^access_control\s*=.*", re.IGNORECASE)
   10.85 +        ac_exit_re = re.compile(".*'\].*")
   10.86 +        file_contents = ""
   10.87 +        comment = 0
   10.88 +        removed = 0
   10.89 +        for line in fd.readlines():
   10.90 +            if ac_entry_re.match(line):
   10.91 +                comment = 1
   10.92 +            if comment:
   10.93 +                removed = 1
   10.94 +                line = "#"+line
   10.95 +            if comment and ac_exit_re.match(line):
   10.96 +                comment = 0
   10.97 +            file_contents = file_contents + line
   10.98 +        fd.close()
   10.99 +
  10.100 +        # send error message if we didn't find anything to remove
  10.101 +        if not removed:
  10.102 +            security.err("Label does not exist in domain configuration file.")
  10.103 +
  10.104 +        # write the data back out to the file
  10.105 +        fd = open(file, "wb")
  10.106 +        fd.writelines(file_contents)
  10.107 +        fd.close()
  10.108 +
  10.109 +    except security.ACMError:
  10.110 +        pass
  10.111 +    except:
  10.112 +        traceback.print_exc(limit=1)
  10.113 +
  10.114 +
  10.115 +def main (argv):
  10.116 +    try:
  10.117 +        if len(argv) != 3:
  10.118 +            usage()
  10.119 +            return
  10.120 +
  10.121 +        if argv[1].lower() == "dom":
  10.122 +            configfile = argv[2]
  10.123 +            rm_domain_label(configfile)
  10.124 +        elif argv[1].lower() == "res":
  10.125 +            resource = argv[2]
  10.126 +            rm_resource_label(resource)
  10.127 +        else:
  10.128 +            usage()
  10.129 +
  10.130 +    except security.ACMError:
  10.131 +        traceback.print_exc(limit=1)
  10.132 +
  10.133 +
  10.134 +if __name__ == '__main__':
  10.135 +    main(sys.argv)
  10.136 +
  10.137 +