[Xen-API] Extension of the Xen-API for managing Xen Security Policies

This patch implements extensions for managing security policies in
xend. The XSPolicy and ACMPolicy classes provide the interface for the
Xen-API and implement functionality for setting, updating and
activating of a Xen security policy as well as labeling of virtual
machines and resources such as block devices. Labeling of network
devices will follow.

The acmpolicy class implements a compiler for translating an XML
policy into their binary format and provides functionality for
comparing a current policy against a new one when changing/updating a
policy.

The xspolicyadmin class administers the policy of the system.

Some of the xend-internal code deals with transforming the labeling
information from the S-Expression format into the new Xen-API
format. This is similar to much of the other code that is already
there.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
This commit is contained in:
kfraser@localhost.localdomain committed 2007-07-09 14:51:44 +01:00
1 parent c1d8b16312
commit 184e1b098b
15 files changed
+3406 -153

No files matched your search

File diff suppressed because it is too large. Load diff
+521
View File
@@ -0,0 +1,521 @@
#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2006,2007 International Business Machines Corp.
# Author: Stefan Berger <stefanb@us.ibm.com>
#============================================================================
import re
import os, stat
import tempfile
import shutil
import threading
from xen.xend.XendLogging import log
__bootloader = None
#
# Functions for modifying entries in the bootloader, i.e. adding
# a module to boot the system with a policy.
#
def get_default_title():
""" See description in Bootloader class below """
return __bootloader.get_default_title()
def get_boot_policies():
""" See description in Bootloader class below """
return __bootloader.get_boot_policies()
def add_boot_policy(index, binpolname):
""" See description in Bootloader class below """
return __bootloader.add_boot_policy(index, binpolname)
def rm_policy_from_boottitle(index, unamelist):
""" See description in Bootloader class below """
return __bootloader.rm_policy_from_boottitle(index, unamelist)
def set_kernel_attval(index, att, val):
""" See description in Bootloader class below """
return __bootloader.set_kernel_attval(index, att, val)
def get_kernel_val(index, att):
""" See description in Bootloader class below """
return __bootloader.get_kernel_val(index, att)
def set_boot_policy(title_idx, filename):
boottitles = get_boot_policies()
if boottitles.has_key(title_idx):
rm_policy_from_boottitle(title_idx, [ boottitles[title_idx] ])
rc = add_boot_policy(title_idx, filename)
return rc
def loads_default_policy(filename):
""" Determine whether the given policy is loaded by the default boot title """
polfile = get_default_policy()
if polfile != None:
if polfile == filename or \
"/"+polfile == filename:
return True
return False
def get_default_policy():
""" Get the name of the policy loaded by the default boot title """
title = get_default_title()
policies = get_boot_policies()
return policies.get(title)
def set_default_boot_policy(filename):
""" Set the boot policy in the default title to the given name. """
title = get_default_title()
return set_boot_policy(title, filename)
def __is_bootdir_mounted():
"""
Determine whether the boot partition /boot is mounted or not
"""
rc = False
file = open("/proc/mounts")
for line in file:
tmp = line.split(" ")
if tmp[1] == "/boot":
rc = True
break
return rc
def get_prefix():
if __is_bootdir_mounted():
return "/"
else:
return "/boot/"
class Bootloader:
""" Bootloader class that real bootloader implementations must overwrite """
def __init__(self):
pass
def probe(self):
""" Test whether this implementation of a bootloader is supported on the
local system """
return True
def get_default_title(self):
""" Get the index (starting with 0) of the default boot title
This number is read from the grub configuration file.
In case of an error '-1' is returned
@rtype: int
@return: the index of the default boot title
"""
return None
def get_boot_policies(self):
""" Get a dictionary of policies that the system is booting with.
@rtype: dict
@return: dictionary of boot titles where the keys are the
indices of the boot titles
"""
return {}
def add_boot_policy(self, index, binpolname):
""" Add the binary policy for automatic loading when
booting the system. Add it to the boot title at index
'index'.
"""
return False
def rm_policy_from_boottitle(self, index, unamelist):
""" Remove a policy from the given title. A list of possible policies
must be given to detect what module to remove
"""
return False
def set_kernel_attval(self, index, att, val):
"""
Append an attribut/value pair to the kernel line.
@param index : The index of the title to modify
@param att : The attribute to add
@param val : The value to add. If no value or the special value
'<>' is given, then the attribute will be removed.
If an empty value is given, then only the attribute
is added in the format "att", otherwise "att=val"
is added.
"""
return False
def get_kernel_val(self, index, att):
"""
Get an attribute's value from the kernel line.
@param index : The index of the title to get the attribute/value from
@param att : The attribute to read the value of
"""
return None
class Grub(Bootloader):
""" Implementation for manipulating bootloader entries in grub according
to the 'Bootloader' class interface """
def __init__(self):
self.__bootfile_lock = threading.RLock()
self.title_re = re.compile("\s*title\s", re.IGNORECASE)
self.module_re = re.compile("\s+module\s", re.IGNORECASE)
self.policy_re = re.compile(".*\.bin", re.IGNORECASE)
self.kernel_re = re.compile("\s*kernel\s", re.IGNORECASE)
Bootloader.__init__(self)
def probe(self):
try:
boot_file = self.__get_bootfile()
except:
return False
return True
def __get_bootfile(self):
""" Get the name of the bootfile """
boot_file = "/boot/grub/grub.conf"
alt_boot_file = "/boot/grub/menu.lst"
if not os.path.isfile(boot_file):
#take alternate boot file instead
boot_file = alt_boot_file
#follow symlink since menue.lst might be linked to grub.conf
if not os.path.exists(boot_file):
raise IOError("Boot file \'%s\' not found." % boot_file)
if stat.S_ISLNK(os.lstat(boot_file)[stat.ST_MODE]):
new_name = os.readlink(boot_file)
if new_name[0] == "/":
boot_file = new_name
else:
path = boot_file.split('/')
path[len(path)-1] = new_name
boot_file = '/'.join(path)
if not os.path.exists(boot_file):
raise IOError("Boot file \'%s\' not found." % boot_file)
return boot_file
def __get_titles(self):
""" Get the names of all boot titles in the grub config file
@rtype: list
@return: list of names of available boot titles
"""
titles = []
try:
boot_file = self.__get_bootfile()
except:
return []
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
for line in grub_fd:
if self.title_re.match(line):
line = line.rstrip().lstrip()
titles.append(line.lstrip('title').lstrip())
finally:
self.__bootfile_lock.release()
return titles
def get_default_title(self):
""" Get the index (starting with 0) of the default boot title
This number is read from the grub configuration file.
In case of an error '-1' is returned
@rtype: int
@return: the index of the default boot title
"""
def_re = re.compile("default", re.IGNORECASE)
default = None
try:
boot_file = self.__get_bootfile()
except:
return default
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
for line in grub_fd:
line = line.rstrip()
if def_re.match(line):
line = line.rstrip()
line = line.lstrip("default=")
default = int(line)
break
finally:
self.__bootfile_lock.release()
return default
def get_boot_policies(self):
""" Get a dictionary of policies that the system is booting with.
@rtype: dict
@return: dictionary of boot titles where the keys are the
indices of the boot titles
"""
policies = {}
within_title = 0
idx = -1
try:
boot_file = self.__get_bootfile()
except:
return policies
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
for line in grub_fd:
if self.title_re.match(line):
within_title = 1
idx = idx + 1
if within_title and self.module_re.match(line):
if self.policy_re.match(line):
start = line.find("module")
pol = line[start+6:]
pol = pol.lstrip().rstrip()
if pol[0] == '/':
pol = pol[1:]
if pol[0:5] == "boot/":
pol = pol[5:]
policies[idx] = pol
finally:
self.__bootfile_lock.release()
return policies
def add_boot_policy(self, index, binpolname):
""" Add the binary policy for automatic loading when
booting the system. Add it to the boot title at index
'index'.
"""
ctr = 0
module_line = ""
within_title = 0
found = False
try:
boot_file = self.__get_bootfile()
except:
return False
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
(tmp_fd, tmp_grub) = tempfile.mkstemp()
for line in grub_fd:
if self.title_re.match(line):
if module_line != "" and not found:
os.write(tmp_fd, module_line)
found = True
if ctr == index:
within_title = 1
else:
within_title = 0
ctr = ctr + 1
elif within_title and self.module_re.match(line):
start = line.find("module")
l = line[start+6:len(line)]
l = l.lstrip()
if l[0] == '/':
prefix = "/"
else:
prefix = ""
prefix = get_prefix()
module_line = "\tmodule %s%s\n" % (prefix,binpolname)
else:
if module_line != "" and not found:
os.write(tmp_fd, module_line)
found = True
os.write(tmp_fd, line)
if module_line != "" and not found:
os.write(tmp_fd, module_line)
found = True
shutil.move(boot_file, boot_file+"_save")
shutil.copyfile(tmp_grub, boot_file)
os.close(tmp_fd)
try:
os.remove(tmp_grub)
except:
pass
finally:
self.__bootfile_lock.release()
return found
def rm_policy_from_boottitle(self, index, unamelist):
""" Remove a policy from the given title. A list of possible policies
must be given to detect what module to remove
"""
found = False
ctr = 0
within_title = 0
prefix = get_prefix()
namelist = [prefix+name for name in unamelist]
try:
boot_file = self.__get_bootfile()
except:
return False
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
(tmp_fd, tmp_grub) = tempfile.mkstemp()
for line in grub_fd:
omit_line = False
if self.title_re.match(line):
if ctr == index:
within_title = 1
else:
within_title = 0
ctr = ctr + 1
if within_title and self.module_re.match(line):
if self.policy_re.match(line):
start = line.find("module")
pol = line[start+6:len(line)]
pol = pol.lstrip().rstrip()
if pol in namelist:
omit_line = True
found = True
if not omit_line:
os.write(tmp_fd, line)
if found:
shutil.move(boot_file, boot_file+"_save")
shutil.copyfile(tmp_grub, boot_file)
os.close(tmp_fd)
try:
os.remove(tmp_grub)
except:
pass
finally:
self.__bootfile_lock.release()
return found
def set_kernel_attval(self, index, att, val):
"""
Append an attribut/value pair to the kernel line.
@param index : The index of the title to modify
@param att : The attribute to add
@param val : The value to add. If no value or the special value
'<>' is given, then the attribute will be removed.
If an empty value is given, then only the attribute
is added in the format "att", otherwise "att=val"
is added.
"""
found = False
ctr = 0
within_title = 0
try:
boot_file = self.__get_bootfile()
except:
False
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
(tmp_fd, tmp_grub) = tempfile.mkstemp()
for line in grub_fd:
if self.title_re.match(line):
if ctr == index:
within_title = 1
else:
within_title = 0
ctr = ctr + 1
if within_title and self.kernel_re.match(line):
nitems = []
items = line.split(" ")
i = 0
while i < len(items):
el = items[i].split("=",1)
if el[0] != att:
nitems.append(items[i].rstrip("\n"))
i += 1
if val == "":
nitems.append("%s" % (att))
elif val != None and val != "<>":
nitems.append("%s=%s" % (att,val))
line = " ".join(nitems) + "\n"
os.write(tmp_fd, line)
shutil.move(boot_file, boot_file+"_save")
shutil.copyfile(tmp_grub, boot_file)
os.close(tmp_fd)
try:
os.remove(tmp_grub)
except:
pass
finally:
self.__bootfile_lock.release()
return found
def get_kernel_val(self, index, att):
"""
Get an attribute's value from the kernel line.
@param index : The index of the title to get the attribute/value from
@param att : The attribute to read the value of
"""
ctr = 0
within_title = 0
try:
boot_file = self.__get_bootfile()
except:
return None
try:
self.__bootfile_lock.acquire()
grub_fd = open(boot_file)
for line in grub_fd:
if self.title_re.match(line):
if ctr == index:
within_title = 1
else:
within_title = 0
ctr = ctr + 1
if within_title and self.kernel_re.match(line):
line = line.rstrip().lstrip()
items = line.split(" ")
i = 0
while i < len(items):
el = items[i].split("=",1)
if el[0] == att:
if len(el) == 1:
return "<>"
return el[1]
i += 1
finally:
self.__bootfile_lock.release()
return None # Not found
__bootloader = Bootloader()
grub = Grub()
if grub.probe() == True:
__bootloader = grub
+684 -91
View File
@@ -15,17 +15,22 @@
# Copyright (C) 2006 International Business Machines Corp.
# Author: Reiner Sailer
# Author: Bryan D. Payne <bdpayne@us.ibm.com>
# Author: Stefan Berger <stefanb@us.ibm.com>
#============================================================================
import commands
import logging
import sys, os, string, re
import traceback
import shutil
import os, string, re
import threading
import struct
import stat
from xen.lowlevel import acm
from xen.xend import sxp
from xen.xend import XendConstants
from xen.xend.XendLogging import log
from xen.util import dictio
from xen.xend.XendError import VmError
from xen.util import dictio, xsconstants
from xen.xend.XendConstants import *
#global directories and tools for security management
policy_dir_prefix = "/etc/xen/acm-security/policies"
@@ -60,6 +65,10 @@ policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE)
#other global variables
NULL_SSIDREF = 0
#general Rlock for map files; only one lock for all mapfiles
__mapfile_lock = threading.RLock()
__resfile_lock = threading.RLock()
log = logging.getLogger("xend.util.security")
# Our own exception definition. It is masked (pass) if raised and
@@ -75,7 +84,6 @@ class ACMError(Exception):
def err(msg):
"""Raise ACM exception.
"""
sys.stderr.write("ACMError: " + msg + "\n")
raise ACMError(msg)
@@ -83,6 +91,13 @@ def err(msg):
active_policy = None
def mapfile_lock():
__mapfile_lock.acquire()
def mapfile_unlock():
__mapfile_lock.release()
def refresh_security_policy():
"""
retrieves security policy
@@ -106,6 +121,39 @@ def on():
return (active_policy not in ['INACTIVE', 'NULL'])
def calc_dom_ssidref_from_info(info):
"""
Calculate a domain's ssidref from the security_label in its
info.
This function is called before the domain is started and
makes sure that:
- the type of the policy is the same as indicated in the label
- the name of the policy is the same as indicated in the label
- calculates an up-to-date ssidref for the domain
The latter is necessary since the domain's ssidref could have
changed due to changes to the policy.
"""
import xen.xend.XendConfig
if isinstance(info, xen.xend.XendConfig.XendConfig):
if info.has_key('security_label'):
seclab = info['security_label']
tmp = seclab.split(":")
if len(tmp) != 3:
raise VmError("VM label '%s' in wrong format." % seclab)
typ, policyname, vmlabel = seclab.split(":")
if typ != xsconstants.ACM_POLICY_ID:
raise VmError("Policy type '%s' not supported." % typ)
refresh_security_policy()
if active_policy != policyname:
raise VmError("Active policy '%s' different than "
"what in VM's label ('%s')." %
(active_policy, policyname))
ssidref = label2ssidref(vmlabel, policyname, "dom")
return ssidref
else:
return 0
raise VmError("security.calc_dom_ssidref_from_info: info of type '%s'"
"not supported." % type(info))
# Assumes a 'security' info [security access_control ...] [ssidref ...]
def get_security_info(info, field):
@@ -146,7 +194,6 @@ def get_security_info(info, field):
return None
def get_security_printlabel(info):
"""retrieves printable security label from self.info['security']),
preferably the label name and otherwise (if label is not specified
@@ -250,32 +297,37 @@ def ssidref2label(ssidref_var):
else:
err("Instance type of ssidref not supported (must be of type 'str' or 'int')")
(primary, secondary, f, pol_exists) = getmapfile(None)
if not f:
if (pol_exists):
err("Mapping file for policy \'" + policyname + "\' not found.\n" +
"Please use makepolicy command to create mapping file!")
else:
err("Policy file for \'" + active_policy + "\' not found.")
try:
mapfile_lock()
#2. get labelnames for both ssidref parts
pri_ssid = ssidref & 0xffff
sec_ssid = ssidref >> 16
pri_null_ssid = NULL_SSIDREF & 0xffff
sec_null_ssid = NULL_SSIDREF >> 16
pri_labels = []
sec_labels = []
labels = []
(primary, secondary, f, pol_exists) = getmapfile(None)
if not f:
if (pol_exists):
err("Mapping file for policy not found.\n" +
"Please use makepolicy command to create mapping file!")
else:
err("Policy file for \'" + active_policy + "\' not found.")
for line in f:
l = line.split()
if (len(l) < 5) or (l[0] != "LABEL->SSID"):
continue
if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid):
pri_labels.append(l[3])
if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid):
sec_labels.append(l[3])
f.close()
#2. get labelnames for both ssidref parts
pri_ssid = ssidref & 0xffff
sec_ssid = ssidref >> 16
pri_null_ssid = NULL_SSIDREF & 0xffff
sec_null_ssid = NULL_SSIDREF >> 16
pri_labels = []
sec_labels = []
labels = []
for line in f:
l = line.split()
if (len(l) < 5) or (l[0] != "LABEL->SSID"):
continue
if primary and (l[2] == primary) and (int(l[4], 16) == pri_ssid):
pri_labels.append(l[3])
if secondary and (l[2] == secondary) and (int(l[4], 16) == sec_ssid):
sec_labels.append(l[3])
f.close()
finally:
mapfile_unlock()
#3. get the label that is in both lists (combination must be a single label)
if (primary == "CHWALL") and (pri_ssid == pri_null_ssid) and (sec_ssid != sec_null_ssid):
@@ -297,7 +349,7 @@ def ssidref2label(ssidref_var):
def label2ssidref(labelname, policyname, type):
def label2ssidref(labelname, policyname, typ):
"""
returns ssidref corresponding to labelname;
maps current policy to default directory
@@ -307,42 +359,51 @@ def label2ssidref(labelname, policyname, type):
err("Cannot translate labels for \'" + policyname + "\' policy.")
allowed_types = ['ANY']
if type == 'dom':
if typ == 'dom':
allowed_types.append('VM')
elif type == 'res':
elif typ == 'res':
allowed_types.append('RES')
else:
err("Invalid type. Must specify 'dom' or 'res'.")
(primary, secondary, f, pol_exists) = getmapfile(policyname)
try:
mapfile_lock()
(primary, secondary, f, pol_exists) = getmapfile(policyname)
#2. get labelnames for ssidref parts and find a common label
pri_ssid = []
sec_ssid = []
for line in f:
l = line.split()
if (len(l) < 5) or (l[0] != "LABEL->SSID"):
continue
if primary and (l[1] in allowed_types) and (l[2] == primary) and (l[3] == labelname):
pri_ssid.append(int(l[4], 16))
if secondary and (l[1] in allowed_types) and (l[2] == secondary) and (l[3] == labelname):
sec_ssid.append(int(l[4], 16))
f.close()
if (type == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0):
pri_ssid.append(NULL_SSIDREF)
elif (type == 'res') and (secondary == "CHWALL") and (len(sec_ssid) == 0):
sec_ssid.append(NULL_SSIDREF)
#3. sanity check and composition of ssidref
if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and (secondary != "NULL")):
err("Label \'" + labelname + "\' not found.")
elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1):
err("Label \'" + labelname + "\' not unique in policy (policy error)")
if secondary == "NULL":
return pri_ssid[0]
else:
return (sec_ssid[0] << 16) | pri_ssid[0]
#2. get labelnames for ssidref parts and find a common label
pri_ssid = []
sec_ssid = []
for line in f:
l = line.split()
if (len(l) < 5) or (l[0] != "LABEL->SSID"):
continue
if primary and (l[1] in allowed_types) and \
(l[2] == primary) and \
(l[3] == labelname):
pri_ssid.append(int(l[4], 16))
if secondary and (l[1] in allowed_types) and \
(l[2] == secondary) and \
(l[3] == labelname):
sec_ssid.append(int(l[4], 16))
f.close()
if (typ == 'res') and (primary == "CHWALL") and (len(pri_ssid) == 0):
pri_ssid.append(NULL_SSIDREF)
elif (typ == 'res') and (secondary == "CHWALL") and \
(len(sec_ssid) == 0):
sec_ssid.append(NULL_SSIDREF)
#3. sanity check and composition of ssidref
if (len(pri_ssid) == 0) or ((len(sec_ssid) == 0) and \
(secondary != "NULL")):
err("Label \'" + labelname + "\' not found.")
elif (len(pri_ssid) > 1) or (len(sec_ssid) > 1):
err("Label \'" + labelname + "\' not unique in policy (policy error)")
if secondary == "NULL":
return pri_ssid[0]
else:
return (sec_ssid[0] << 16) | pri_ssid[0]
finally:
mapfile_unlock()
def refresh_ssidref(config):
@@ -381,8 +442,9 @@ def refresh_ssidref(config):
err("Illegal field in access_control")
#verify policy is correct
if active_policy != policyname:
err("Policy \'" + policyname + "\' in label does not match active policy \'"
+ active_policy +"\'!")
err("Policy \'" + str(policyname) +
"\' in label does not match active policy \'"
+ str(active_policy) +"\'!")
new_ssidref = label2ssidref(labelname, policyname, 'dom')
if not new_ssidref:
@@ -470,6 +532,25 @@ def get_decision(arg1, arg2):
err("Cannot determine decision (Invalid parameter).")
def hv_chg_policy(bin_pol, del_array, chg_array):
"""
Change the binary policy in the hypervisor
The 'del_array' and 'chg_array' give hints about deleted ssidrefs
and changed ssidrefs which can be due to deleted VM labels
or reordered VM labels
"""
rc = -xsconstants.XSERR_GENERAL_FAILURE
errors = ""
if not on():
err("No policy active.")
try:
rc, errors = acm.chgpolicy(bin_pol, del_array, chg_array)
except Exception, e:
pass
if (len(errors) > 0):
rc = -xsconstants.XSERR_HV_OP_FAILED
return rc, errors
def make_policy(policy_name):
policy_file = string.join(string.split(policy_name, "."), "/")
@@ -480,8 +561,6 @@ def make_policy(policy_name):
if ret:
err("Creating policy failed:\n" + output)
def load_policy(policy_name):
global active_policy
policy_file = policy_dir_prefix + "/" + string.join(string.split(policy_name, "."), "/")
@@ -538,8 +617,8 @@ def list_labels(policy_name, condition):
def get_res_label(resource):
"""Returns resource label information (label, policy) if it exists.
Otherwise returns null label and policy.
"""Returns resource label information (policytype, label, policy) if
it exists. Otherwise returns null label and policy.
"""
def default_res_label():
ssidref = NULL_SSIDREF
@@ -547,23 +626,19 @@ def get_res_label(resource):
label = ssidref2label(ssidref)
else:
label = None
return (label, 'NULL')
return (xsconstants.ACM_POLICY_ID, 'NULL', label)
(label, policy) = default_res_label()
# load the resource label file
res_label_cache = {}
try:
res_label_cache = dictio.dict_read("resources", res_label_filename)
except:
log.info("Resource label file not found.")
return default_res_label()
tmp = get_resource_label(resource)
if len(tmp) == 2:
policytype = xsconstants.ACM_POLICY_ID
policy, label = tmp
elif len(tmp) == 3:
policytype, policy, label = tmp
else:
policytype, policy, label = default_res_label()
# find the resource information
if res_label_cache.has_key(resource):
(policy, label) = res_label_cache[resource]
return (label, policy)
return (policytype, label, policy)
def get_res_security_details(resource):
@@ -582,7 +657,7 @@ def get_res_security_details(resource):
(label, ssidref, policy) = default_security_details()
# find the entry associated with this resource
(label, policy) = get_res_label(resource)
(policytype, label, policy) = get_res_label(resource)
if policy == 'NULL':
log.info("Resource label for "+resource+" not in file, using DEFAULT.")
return default_security_details()
@@ -596,8 +671,29 @@ def get_res_security_details(resource):
return (label, ssidref, policy)
def security_label_to_details(seclab):
""" Convert a Xen-API type of security label into details """
def default_security_details():
ssidref = NULL_SSIDREF
if on():
label = ssidref2label(ssidref)
else:
label = None
policy = active_policy
return (label, ssidref, policy)
def unify_resname(resource):
(policytype, policy, label) = seclab.split(":")
# is this resource label for the running policy?
if policy == active_policy:
ssidref = label2ssidref(label, policy, 'res')
else:
log.info("Resource label not for active policy, using DEFAULT.")
return default_security_details()
return (label, ssidref, policy)
def unify_resname(resource, mustexist=True):
"""Makes all resource locations absolute. In case of physical
resources, '/dev/' is added to local file names"""
@@ -606,28 +702,53 @@ def unify_resname(resource):
# sanity check on resource name
try:
(type, resfile) = resource.split(":", 1)
(typ, resfile) = resource.split(":", 1)
except:
err("Resource spec '%s' contains no ':' delimiter" % resource)
if type == "tap":
if typ == "tap":
try:
(subtype, resfile) = resfile.split(":")
except:
err("Resource spec '%s' contains no tap subtype" % resource)
if type in ["phy", "tap"]:
import os
if typ in ["phy", "tap"]:
if not resfile.startswith("/"):
resfile = "/dev/" + resfile
if mustexist:
stats = os.lstat(resfile)
if stat.S_ISLNK(stats[stat.ST_MODE]):
resolved = os.readlink(resfile)
if resolved[0] != "/":
resfile = os.path.join(os.path.dirname(resfile), resolved)
resfile = os.path.abspath(resfile)
else:
resfile = resolved
stats = os.lstat(resfile)
if not (stat.S_ISBLK(stats[stat.ST_MODE])):
err("Invalid resource")
if typ in [ "file", "tap" ]:
if mustexist:
stats = os.lstat(resfile)
if stat.S_ISLNK(stats[stat.ST_MODE]):
resfile = os.readlink(resfile)
stats = os.lstat(resfile)
if not stat.S_ISREG(stats[stat.ST_MODE]):
err("Invalid resource")
#file: resources must specified with absolute path
if (not resfile.startswith("/")) or (not os.path.exists(resfile)):
err("Invalid resource.")
#vlan resources don't start with '/'
if typ != "vlan":
if (not resfile.startswith("/")) or \
(mustexist and not os.path.exists(resfile)):
err("Invalid resource.")
# from here on absolute file names with resources
if type == "tap":
type = type + ":" + subtype
resource = type + ":" + resfile
if typ == "tap":
typ = typ + ":" + subtype
resource = typ + ":" + resfile
return resource
@@ -662,9 +783,481 @@ def res_security_check(resource, domain_label):
else:
# Note, we can't canonicalise the resource here, because people using
# xm without ACM are free to use relative paths.
(label, policy) = get_res_label(resource)
(policytype, label, policy) = get_res_label(resource)
if policy != 'NULL':
raise ACMError("Security is off, but '"+resource+"' is labeled")
rtnval = 0
return rtnval
def res_security_check_xapi(rlabel, rssidref, rpolicy, xapi_dom_label):
"""Checks if the given resource can be used by the given domain
label. Returns 1 if the resource can be used, otherwise 0.
"""
rtnval = 1
# if security is on, ask the hypervisor for a decision
if on():
typ, dpolicy, domain_label = xapi_dom_label.split(":")
if not dpolicy or not domain_label:
raise VmError("VM security label in wrong format.")
if active_policy != rpolicy:
raise VmError("Resource's policy '%s' != active policy '%s'" %
(rpolicy, active_policy))
domac = ['access_control']
domac.append(['policy', active_policy])
domac.append(['label', domain_label])
domac.append(['type', 'dom'])
decision = get_decision(domac, ['ssidref', str(rssidref)])
log.info("Access Control Decision : %s" % decision)
# provide descriptive error messages
if decision == 'DENIED':
if rlabel == ssidref2label(NULL_SSIDREF):
#raise ACMError("Resource is not labeled")
rtnval = 0
else:
#raise ACMError("Permission denied for resource because label '"+rlabel+"' is not allowed")
rtnval = 0
# security is off, make sure resource isn't labeled
else:
# Note, we can't canonicalise the resource here, because people using
# xm without ACM are free to use relative paths.
if rpolicy != 'NULL':
#raise ACMError("Security is off, but resource is labeled")
rtnval = 0
return rtnval
def set_resource_label_xapi(resource, reslabel_xapi, oldlabel_xapi):
"""Assign a resource label to a resource
@param resource: The name of a resource, i.e., "phy:/dev/hda", or
"tap:qcow:/path/to/file.qcow"
@param reslabel_xapi: A resource label foramtted as in all other parts of
the Xen-API, i.e., ACM:xm-test:blue"
@rtype: int
@return Success (0) or failure value (< 0)
"""
olabel = ""
if reslabel_xapi == "":
return rm_resource_label(resource, oldlabel_xapi)
typ, policyref, label = reslabel_xapi.split(":")
if typ != xsconstants.ACM_POLICY_ID:
return -xsconstants.XSERR_WRONG_POLICY_TYPE
if not policyref or not label:
return -xsconstants.XSERR_BAD_LABEL_FORMAT
if oldlabel_xapi not in [ "" ]:
tmp = oldlabel_xapi.split(":")
if len(tmp) != 3:
return -xsconstants.XSERR_BAD_LABEL_FORMAT
otyp, opolicyref, olabel = tmp
# Only ACM is supported
if otyp != xsconstants.ACM_POLICY_ID:
return -xsconstants.XSERR_WRONG_POLICY_TYPE
return set_resource_label(resource, typ, policyref, label, olabel)
def is_resource_in_use(resource):
""" Investigate all running domains whether they use this device """
from xen.xend import XendDomain
dominfos = XendDomain.instance().list('all')
lst = []
for dominfo in dominfos:
if is_resource_in_use_by_dom(dominfo, resource):
lst.append(dominfo)
return lst
def devices_equal(res1, res2):
""" Determine whether two devices are equal """
return (unify_resname(res1) == unify_resname(res2))
def is_resource_in_use_by_dom(dominfo, resource):
""" Determine whether a resources is in use by a given domain
@return True or False
"""
if not dominfo.domid:
return False
if dominfo._stateGet() not in [ DOM_STATE_RUNNING ]:
return False
devs = dominfo.info['devices']
uuids = devs.keys()
for uuid in uuids:
dev = devs[uuid]
if len(dev) >= 2 and dev[1].has_key('uname'):
# dev[0] is type, i.e. 'vbd'
if devices_equal(dev[1]['uname'], resource):
log.info("RESOURCE IN USE: Domain %d uses %s." %
(dominfo.domid, resource))
return True
return False
def get_domain_resources(dominfo):
""" Collect all resources of a domain in a map where each entry of
the map is a list.
Entries are strored in the following formats:
tap:qcow:/path/xyz.qcow
"""
resources = { 'vbd' : [], 'tap' : []}
devs = dominfo.info['devices']
uuids = devs.keys()
for uuid in uuids:
dev = devs[uuid]
typ = dev[0]
if typ in [ 'vbd', 'tap' ]:
resources[typ].append(dev[1]['uname'])
return resources
def resources_compatible_with_vmlabel(xspol, dominfo, vmlabel):
"""
Check whether the resources' labels are compatible with the
given VM label. This is a function to be used when for example
a running domain is to get the new label 'vmlabel'
"""
if not xspol:
return False
try:
__resfile_lock.acquire()
try:
access_control = dictio.dict_read("resources",
res_label_filename)
except:
return False
return __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
access_control)
finally:
__resfile_lock.release()
return False
def __resources_compatible_with_vmlabel(xspol, dominfo, vmlabel,
access_control):
"""
Check whether the resources' labels are compatible with the
given VM label. The access_control parameter provides a
dictionary of the resource name to resource label mappings
under which the evaluation should be done.
"""
resources = get_domain_resources(dominfo)
reslabels = [] # all resource labels
polname = xspol.get_name()
for key in resources.keys():
for res in resources[key]:
try:
tmp = access_control[res]
if len(tmp) != 3:
return False
if polname != tmp[1]:
return False
label = tmp[2]
if not label in reslabels:
reslabels.append(label)
except:
return False
# Check that all resource labes have a common STE type with the
# vmlabel
rc = xspol.policy_check_vmlabel_against_reslabels(vmlabel, reslabels)
return rc;
def set_resource_label(resource, policytype, policyref, reslabel, \
oreslabel = None):
"""Assign a label to a resource
If the old label (oreslabel) is given, then the resource must have
that old label.
A resource label may be changed if
- the resource is not in use
@param resource : The name of a resource, i.e., "phy:/dev/hda"
@param policyref : The name of the policy
@param reslabel : the resource label within the policy
@param oreslabel : optional current resource label
@rtype: int
@return Success (0) or failure value (< 0)
"""
try:
resource = unify_resname(resource, mustexist=False)
except Exception:
return -xsconstants.XSERR_BAD_RESOURCE_FORMAT
domains = is_resource_in_use(resource)
if len(domains) > 0:
return -xsconstants.XSERR_RESOURCE_IN_USE
try:
__resfile_lock.acquire()
access_control = {}
try:
access_control = dictio.dict_read("resources", res_label_filename)
except:
pass
if oreslabel:
if not access_control.has_key(resource):
return -xsconstants.XSERR_BAD_LABEL
tmp = access_control[resource]
if len(tmp) != 3:
return -xsconstants.XSERR_BAD_LABEL
if tmp[2] != oreslabel:
return -xsconstants.XSERR_BAD_LABEL
if reslabel != "":
new_entry = { resource : tuple([policytype, policyref, reslabel])}
access_control.update(new_entry)
else:
if access_control.has_key(resource):
del access_control[resource]
dictio.dict_write(access_control, "resources", res_label_filename)
finally:
__resfile_lock.release()
return xsconstants.XSERR_SUCCESS
def rm_resource_label(resource, oldlabel_xapi):
"""Remove a resource label from a physical resource
@param resource: The name of a resource, i.e., "phy:/dev/hda"
@rtype: int
@return Success (0) or failure value (< 0)
"""
tmp = oldlabel_xapi.split(":")
if len(tmp) != 3:
return -xsconstants.XSERR_BAD_LABEL_FORMAT
otyp, opolicyref, olabel = tmp
# Only ACM is supported
if otyp != xsconstants.ACM_POLICY_ID and \
otyp != xsconstants.INVALID_POLICY_PREFIX + xsconstants.ACM_POLICY_ID:
return -xsconstants.XSERR_WRONG_POLICY_TYPE
return set_resource_label(resource, "", "", "", olabel)
def get_resource_label_xapi(resource):
"""Get the assigned resource label of a physical resource
in the format used by then Xen-API, i.e., "ACM:xm-test:blue"
@rtype: string
@return the string representing policy type, policy name and label of
the resource
"""
res = get_resource_label(resource)
return format_resource_label(res)
def format_resource_label(res):
if res:
if len(res) == 2:
return xsconstants.ACM_POLICY_ID + ":" + res[0] + ":" + res[1]
if len(res) == 3:
return ":".join(res)
return ""
def get_resource_label(resource):
"""Get the assigned resource label of a given resource
@param resource: The name of a resource, i.e., "phy:/dev/hda"
@rtype: list
@return tuple of (policy name, resource label), i.e., (xm-test, blue)
"""
try:
resource = unify_resname(resource, mustexist=False)
except Exception:
return []
reslabel_map = get_labeled_resources()
if reslabel_map.has_key(resource):
return list(reslabel_map[resource])
else:
#Try to resolve each label entry
for key, value in reslabel_map.items():
try:
if resource == unify_resname(key):
return list(value)
except:
pass
return []
def get_labeled_resources_xapi():
""" Get a map of all labeled resource with the labels formatted in the
xen-api resource label format.
"""
reslabel_map = get_labeled_resources()
for key, labeldata in reslabel_map.items():
reslabel_map[key] = format_resource_label(labeldata)
return reslabel_map
def get_labeled_resources():
"""Get a map of all labeled resources
@rtype: list
@return list of labeled resources
"""
try:
__resfile_lock.acquire()
try:
access_control = dictio.dict_read("resources", res_label_filename)
except:
return {}
finally:
__resfile_lock.release()
return access_control
def relabel_domains(relabel_list):
"""
Relabel the given domains to have a new ssidref.
@param relabel_list: a list containing tuples of domid, ssidref
example: [ [0, 0x00020002] ]
"""
rel_rules = ""
for r in relabel_list:
log.info("Relabeling domain with domid %d to new ssidref 0x%08x",
r[0], r[1])
rel_rules += struct.pack("ii", r[0], r[1])
try:
rc, errors = acm.relabel_domains(rel_rules)
except Exception, e:
log.info("Error after relabel_domains: %s" % str(e))
rc = -xsconstants.XSERR_GENERAL_FAILURE
errors = ""
if (len(errors) > 0):
rc = -xsconstants.XSERR_HV_OP_FAILED
return rc, errors
def change_acm_policy(bin_pol, del_array, chg_array,
vmlabel_map, reslabel_map, cur_acmpol, new_acmpol):
"""
Change the ACM policy of the system by relabeling
domains and resources first and doing some access checks.
Then update the policy in the hypervisor. If this is all successful,
relabel the domains permanently and commit the relabed resources.
Need to do / check the following:
- relabel all resources where there is a 'from' field in
the policy. [ NOT DOING THIS: and mark those as unlabeled where the label
does not appear in the new policy anymore (deletion) ]
- relabel all VMs where there is a 'from' field in the
policy and mark those as unlabeled where the label
does not appear in the new policy anymore; no running
or paused VM may be unlabeled through this
- check that under the new labeling conditions the VMs
still have access to their resources as before. Unlabeled
resources are inaccessible. If this check fails, the
update failed.
- Attempt changes in the hypervisor; if this step fails,
roll back the relabeling of resources and VMs
- Make the relabeling of resources and VMs permanent
"""
rc = xsconstants.XSERR_SUCCESS
domain_label_map = {}
new_policyname = new_acmpol.get_name()
new_policytype = new_acmpol.get_type_name()
cur_policyname = cur_acmpol.get_name()
cur_policytype = cur_acmpol.get_type_name()
polnew_reslabels = new_acmpol.policy_get_resourcelabel_names()
errors=""
try:
__resfile_lock.acquire()
mapfile_lock()
# Get all domains' dominfo.
from xen.xend import XendDomain
dominfos = XendDomain.instance().list('all')
log.info("----------------------------------------------")
# relabel resources
access_control = {}
try:
access_control = dictio.dict_read("resources", res_label_filename)
finally:
pass
for key, labeldata in access_control.items():
if len(labeldata) == 2:
policy, label = labeldata
policytype = xsconstants.ACM_POLICY_ID
elif len(labeldata) == 3:
policytype, policy, label = labeldata
else:
return -xsconstants.XSERR_BAD_LABEL_FORMAT, ""
if policytype != cur_policytype or \
policy != cur_policyname:
continue
# label been renamed or deleted?
if reslabel_map.has_key(label) and cur_policyname == policy:
label = reslabel_map[label]
elif label not in polnew_reslabels:
policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
# Update entry
access_control[key] = \
tuple([ policytype, new_policyname, label ])
# All resources have new labels in the access_control map
# There may still be labels in there that are invalid now.
# Do this in memory without writing to disk:
# - Relabel all domains independent of whether they are running
# or not
# - later write back to config files
polnew_vmlabels = new_acmpol.policy_get_virtualmachinelabel_names()
for dominfo in dominfos:
sec_lab = dominfo.get_security_label()
if not sec_lab:
continue
policytype, policy, vmlabel = sec_lab.split(":")
name = dominfo.getName()
if policytype != cur_policytype or \
policy != cur_policyname:
continue
new_vmlabel = vmlabel
if vmlabel_map.has_key(vmlabel):
new_vmlabel = vmlabel_map[vmlabel]
if new_vmlabel not in polnew_vmlabels:
policytype = xsconstants.INVALID_POLICY_PREFIX + policytype
new_seclab = "%s:%s:%s" % \
(policytype, new_policyname, new_vmlabel)
domain_label_map[dominfo] = [ sec_lab, new_seclab ]
if dominfo._stateGet() in (DOM_STATE_PAUSED, DOM_STATE_RUNNING):
compatible = __resources_compatible_with_vmlabel(new_acmpol,
dominfo,
new_vmlabel,
access_control)
log.info("Domain %s with new label '%s' can access its "
"resources? : %s" %
(name, new_vmlabel, str(compatible)))
log.info("VM labels in new domain: %s" %
new_acmpol.policy_get_virtualmachinelabel_names())
if not compatible:
return (-xsconstants.XSERR_RESOURCE_ACCESS, "")
rc, errors = hv_chg_policy(bin_pol, del_array, chg_array)
if rc == 0:
# Write the relabeled resources back into the file
dictio.dict_write(access_control, "resources", res_label_filename)
# Properly update all VMs to their new labels
for dominfo, labels in domain_label_map.items():
sec_lab, new_seclab = labels
if sec_lab != new_seclab:
log.info("Updating domain %s to new label '%s'." % \
(new_seclab, sec_lab))
# This better be working!
dominfo.set_security_label(new_seclab,
sec_lab,
new_acmpol)
finally:
log.info("----------------------------------------------")
mapfile_unlock()
__resfile_lock.release()
return rc, errors
+104
View File
@@ -0,0 +1,104 @@
#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2007 International Business Machines Corp.
# Author: Stefan Berger <stefanb@us.ibm.com>
#============================================================================
XS_INST_NONE = 0
XS_INST_BOOT = (1 << 0)
XS_INST_LOAD = (1 << 1)
XS_POLICY_NONE = 0
XS_POLICY_ACM = (1 << 0)
# Some internal variables used by the Xen-API
ACM_LABEL_VM = (1 << 0)
ACM_LABEL_RES = (1 << 1)
# Base for XS error codes for collision avoidance with other error codes
XSERR_BASE = 0x1000
# XS error codes as used by the Xen-API
XSERR_SUCCESS = 0
XSERR_GENERAL_FAILURE = 1 + XSERR_BASE
XSERR_BAD_XML = 2 + XSERR_BASE # XML is wrong (not according to schema)
XSERR_XML_PROCESSING = 3 + XSERR_BASE
XSERR_POLICY_INCONSISTENT = 4 + XSERR_BASE # i.e., bootstrap name not a VM label
XSERR_FILE_ERROR = 5 + XSERR_BASE
XSERR_BAD_RESOURCE_FORMAT = 6 + XSERR_BASE # badly formatted resource
XSERR_BAD_LABEL_FORMAT = 7 + XSERR_BASE
XSERR_RESOURCE_NOT_LABELED = 8 + XSERR_BASE
XSERR_RESOURCE_ALREADY_LABELED = 9 + XSERR_BASE
XSERR_WRONG_POLICY_TYPE = 10 + XSERR_BASE
XSERR_BOOTPOLICY_INSTALLED = 11 + XSERR_BASE
XSERR_NO_DEFAULT_BOOT_TITLE = 12 + XSERR_BASE
XSERR_POLICY_LOAD_FAILED = 13 + XSERR_BASE
XSERR_POLICY_LOADED = 14 + XSERR_BASE
XSERR_POLICY_TYPE_UNSUPPORTED = 15 + XSERR_BASE
XSERR_BAD_CONFLICTSET = 16 + XSERR_BASE
XSERR_RESOURCE_IN_USE = 17 + XSERR_BASE
XSERR_BAD_POLICY_NAME = 18 + XSERR_BASE
XSERR_VERSION_PREVENTS_UPDATE = 19 + XSERR_BASE
XSERR_BAD_LABEL = 20 + XSERR_BASE
XSERR_VM_WRONG_STATE = 21 + XSERR_BASE
XSERR_POLICY_NOT_LOADED = 22 + XSERR_BASE
XSERR_RESOURCE_ACCESS = 23 + XSERR_BASE
XSERR_HV_OP_FAILED = 24 + XSERR_BASE
XSERR_BOOTPOLICY_INSTALL_ERROR = 25 + XSERR_BASE
XSERR_LAST = 25 + XSERR_BASE ## KEEP LAST
XSERR_MESSAGES = [
'',
'General Failure',
'XML is malformed',
'Error while processing XML',
'Policy has inconsistencies',
'A file access error occurred',
'The resource format is not valid',
'The label format is not valid',
'The resource is not labeld',
'The resource is already labeld',
'The policy type is wrong',
'The system boot policy is installed',
'Could not find the default boot title',
'Loading of the policy failed',
'The policy is loaded',
'The policy type is unsupported',
'There is a bad conflict set',
'The resource is in use',
'The policy has an invalid name',
'The version of the policy prevents an update',
'The label is bad',
'Operation not premittend - the VM is in the wrong state',
'The policy is not loaded',
'Error accessing resource',
'Operation failed in hypervisor',
'Boot policy installation error'
]
def xserr2string(err):
if err == XSERR_SUCCESS:
return "Success"
if err >= XSERR_GENERAL_FAILURE and \
err <= XSERR_LAST:
return XSERR_MESSAGES[err - XSERR_BASE]
return "Unknown XSERR code '%s'." % (hex(err))
# Policy identifiers used in labels
ACM_POLICY_ID = "ACM"
INVALID_POLICY_PREFIX = "INV_"
INVALID_SSIDREF = 0xFFFFFFFF
+66
View File
@@ -0,0 +1,66 @@
#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2006,2007 International Business Machines Corp.
# Author: Stefan Berger <stefanb@us.ibm.com>
#============================================================================
import threading
import xsconstants
class XSPolicy:
"""
The base policy class for all policies administered through
XSPolicyAdmin.
"""
def __init__(self, name=None, ref=None):
self.lock = threading.Lock()
self.ref = ref
self.name = name
if ref:
from xen.xend.XendXSPolicy import XendXSPolicy
self.xendxspolicy = XendXSPolicy(self, {}, ref)
else:
self.xendxspolicy = None
def grab_lock(self):
self.lock.acquire()
def unlock(self):
self.lock.release()
def get_ref(self):
return self.ref
def destroy(self):
if self.xendxspolicy:
self.xendxspolicy.destroy()
# All methods below should be overwritten by the inheriting class
def isloaded(self):
return False
def loadintohv(self):
return xsconstants.XSERR_POLICY_LOAD_FAILED
def get_type(self):
return xsconstants.XS_POLICY_NONE
def get_type_name(self):
return ""
def update(self, repr_new):
return -xsconstants.XSERR_GENERAL_FAILURE, ""
+59 -7
View File
@@ -40,11 +40,13 @@ from XendPIFMetrics import XendPIFMetrics
from XendVMMetrics import XendVMMetrics
from XendPIF import XendPIF
from XendPBD import XendPBD
from XendXSPolicy import XendXSPolicy, XendACMPolicy
from XendAPIConstants import *
from xen.util.xmlrpclib2 import stringify
from xen.util.blkif import blkdev_name_to_number
from xen.util import xsconstants
AUTH_NONE = 'none'
@@ -467,6 +469,8 @@ classes = {
'console' : valid_console,
'SR' : valid_sr,
'task' : valid_task,
'XSPolicy' : valid_object("XSPolicy"),
'ACMPolicy' : valid_object("ACMPolicy"),
'debug' : valid_debug,
'network' : valid_object("network"),
'PIF' : valid_object("PIF"),
@@ -481,6 +485,8 @@ autoplug_classes = {
'VM_metrics' : XendVMMetrics,
'PBD' : XendPBD,
'PIF_metrics' : XendPIFMetrics,
'XSPolicy' : XendXSPolicy,
'ACMPolicy' : XendACMPolicy,
}
class XendAPI(object):
@@ -1170,7 +1176,8 @@ class XendAPI(object):
'HVM_boot_params',
'platform',
'PCI_bus',
'other_config']
'other_config',
'security_label']
VM_methods = [('clone', 'VM'),
('start', None),
@@ -1230,7 +1237,8 @@ class XendAPI(object):
'HVM_boot_params',
'platform',
'PCI_bus',
'other_config']
'other_config',
'security_label']
def VM_get(self, name, session, vm_ref):
return xen_api_success(
@@ -1601,7 +1609,22 @@ class XendAPI(object):
if dom:
return xen_api_success([dom.get_uuid()])
return xen_api_success([])
def VM_get_security_label(self, session, vm_ref):
dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
label = dom.get_security_label()
return xen_api_success(label)
def VM_set_security_label(self, session, vm_ref, sec_label, old_label):
dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
(rc, errors, oldlabel, new_ssidref) = \
dom.set_security_label(sec_label, old_label)
if rc != xsconstants.XSERR_SUCCESS:
return xen_api_error(['SECURITY_ERROR', rc])
if rc == 0:
rc = new_ssidref
return xen_api_success(rc)
def VM_create(self, session, vm_struct):
xendom = XendDomain.instance()
domuuid = XendTask.log_progress(0, 100,
@@ -1655,6 +1678,7 @@ class XendAPI(object):
'domid': domid is None and -1 or domid,
'is_control_domain': xeninfo.info['is_control_domain'],
'metrics': xeninfo.get_metrics(),
'security_label': xeninfo.get_security_label(),
'crash_dumps': []
}
return xen_api_success(record)
@@ -1952,7 +1976,8 @@ class XendAPI(object):
'runtime_properties']
VIF_attr_rw = ['device',
'MAC',
'MTU']
'MTU',
'security_label']
VIF_attr_inst = VIF_attr_rw
@@ -2054,7 +2079,10 @@ class XendAPI(object):
except Exception, exn:
log.exception(exn)
return xen_api_success({})
def VIF_get_security_label(self, session, vif_ref):
return self._VIF_get(vif_ref, 'security_label')
# Xen API: Class VIF_metrics
# ----------------------------------------------------------------
@@ -2098,7 +2126,8 @@ class XendAPI(object):
'virtual_size',
'sharable',
'read_only',
'other_config']
'other_config',
'security_label']
VDI_attr_inst = VDI_attr_ro + VDI_attr_rw
VDI_methods = [('destroy', None)]
@@ -2206,13 +2235,24 @@ class XendAPI(object):
xennode = XendNode.instance()
return xen_api_success(xennode.get_vdi_by_name_label(name))
def VDI_set_security_label(self, session, vdi_ref, sec_lab, old_lab):
vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref)
rc = vdi.set_security_label(sec_lab, old_lab)
if rc < 0:
return xen_api_error(['SECURITY_ERROR', rc])
return xen_api_success(rc)
def VDI_get_security_label(self, session, vdi_ref):
vdi = XendNode.instance().get_vdi_by_uuid(vdi_ref)
return xen_api_success(vdi.get_security_label())
# Xen API: Class VTPM
# ----------------------------------------------------------------
VTPM_attr_rw = [ ]
VTPM_attr_ro = ['VM',
'backend']
'backend',
'runtime_properties' ]
VTPM_attr_inst = VTPM_attr_rw
@@ -2290,6 +2330,18 @@ class XendAPI(object):
vtpms = reduce(lambda x, y: x + y, vtpms)
return xen_api_success(vtpms)
def VTPM_get_runtime_properties(self, _, vtpm_ref):
xendom = XendDomain.instance()
dominfo = xendom.get_vm_with_dev_uuid('vtpm', vtpm_ref)
device = dominfo.get_dev_config_by_uuid('vtpm', vtpm_ref)
try:
device_sxps = dominfo.getDeviceSxprs('vtpm')
device_dict = dict(device_sxps[0][1])
return xen_api_success(device_dict)
except:
return xen_api_success({})
# Xen API: Class console
# ----------------------------------------------------------------
+29 -7
View File
@@ -30,6 +30,8 @@ from xen.xend.PrettyPrint import prettyprintstring
from xen.xend.XendConstants import DOM_STATE_HALTED
from xen.xend.server.netif import randomMAC
from xen.util.blkif import blkdev_name_to_number
from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
from xen.util import xsconstants
log = logging.getLogger("xend.XendConfig")
log.setLevel(logging.WARN)
@@ -160,6 +162,7 @@ XENAPI_CFG_TYPES = {
'platform': dict,
'tools_version': dict,
'other_config': dict,
'security_label': str,
}
# List of legacy configuration keys that have no equivalent in the
@@ -168,7 +171,6 @@ XENAPI_CFG_TYPES = {
LEGACY_UNSUPPORTED_BY_XENAPI_CFG = [
# roundtripped (dynamic, unmodified)
'shadow_memory',
'security',
'vcpu_avail',
'cpu_weight',
'cpu_cap',
@@ -319,7 +321,6 @@ class XendConfig(dict):
'memory_static_max': 0,
'memory_dynamic_max': 0,
'devices': {},
'security': None,
'on_xend_start': 'ignore',
'on_xend_stop': 'ignore',
'cpus': [],
@@ -425,9 +426,10 @@ class XendConfig(dict):
self._memory_sanity_check()
self['cpu_time'] = dominfo['cpu_time']/1e9
# TODO: i don't know what the security stuff expects here
if dominfo.get('ssidref'):
self['security'] = [['ssidref', dominfo['ssidref']]]
ssidref = int(dominfo.get('ssidref'))
self['security_label'] = XSPolicyAdminInstance().ssidref_to_vmlabel(ssidref)
self['shutdown_reason'] = dominfo['shutdown_reason']
# parse state into Xen API states
@@ -634,8 +636,26 @@ class XendConfig(dict):
except ValueError, e:
raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
if 'security' in cfg and isinstance(cfg['security'], str):
cfg['security'] = sxp.from_string(cfg['security'])
if 'security' in cfg and not cfg.get('security_label'):
secinfo = cfg['security']
if isinstance(secinfo, list):
# The xm command sends a list formatted like this:
# [['access_control', ['policy', 'xm-test'],['label', 'red']],
# ['ssidref', 196611]]
policy = ""
label = ""
policytype = xsconstants.ACM_POLICY_ID
for idx in range(0, len(secinfo)):
if secinfo[idx][0] == "access_control":
for aidx in range(1, len(secinfo[idx])):
if secinfo[idx][aidx][0] == "policy":
policy = secinfo[idx][aidx][1]
if secinfo[idx][aidx][0] == "label":
label = secinfo[idx][aidx][1]
if label != "" and policy != "":
cfg['security_label'] = "%s:%s:%s" % \
(policytype, policy, label)
del cfg['security']
old_state = sxp.child_value(sxp_cfg, 'state')
if old_state:
@@ -778,7 +798,6 @@ class XendConfig(dict):
self[sxp_arg] = val
_set_cfg_if_exists('shadow_memory')
_set_cfg_if_exists('security')
_set_cfg_if_exists('features')
_set_cfg_if_exists('on_xend_stop')
_set_cfg_if_exists('on_xend_start')
@@ -891,6 +910,9 @@ class XendConfig(dict):
if self.has_key(legacy) and self[legacy] not in (None, []):
sxpr.append([legacy, self[legacy]])
if self.has_key('security_label'):
sxpr.append(['security_label', self['security_label']])
sxpr.append(['image', self.image_sxpr()])
sxpr.append(['status', domain._stateGet()])
+1 -2
View File
@@ -49,7 +49,7 @@ from xen.xend.XendAPIConstants import *
from xen.xend.xenstore.xstransact import xstransact
from xen.xend.xenstore.xswatch import xswatch
from xen.util import mkdir, security
from xen.util import mkdir
from xen.xend import uuid
xc = xen.lowlevel.xc.xc()
@@ -486,7 +486,6 @@ class XendDomain:
"""
self.domains_lock.acquire()
try:
security.refresh_ssidref(config)
dominfo = XendDomainInfo.restore(config)
return dominfo
finally:
+144 -36
View File
@@ -830,6 +830,9 @@ class XendDomainInfo:
else:
f('image/%s' % n, v)
if self.info.has_key('security_label'):
f('security_label', self.info['security_label'])
to_store.update(self._vcpuDomDetails())
log.debug("Storing domain details: %s", scrub_password(to_store))
@@ -1000,9 +1003,6 @@ class XendDomainInfo:
log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
vcpus)
def getLabel(self):
return security.get_security_info(self.info, 'label')
def getMemoryTarget(self):
"""Get this domain's target memory size, in KB."""
return self.info['memory_dynamic_max'] / 1024
@@ -1446,11 +1446,20 @@ class XendDomainInfo:
# allocation of 1MB. We free up 2MB here to be on the safe side.
balloon.free(2*1024) # 2MB should be plenty
self.domid = xc.domain_create(
domid = 0,
ssidref = security.get_security_info(self.info, 'ssidref'),
handle = uuid.fromString(self.info['uuid']),
hvm = int(hvm))
ssidref = security.calc_dom_ssidref_from_info(self.info)
if ssidref == 0 and security.on():
raise VmError('VM is not properly labeled.')
try:
self.domid = xc.domain_create(
domid = 0,
ssidref = ssidref,
handle = uuid.fromString(self.info['uuid']),
hvm = int(hvm))
except Exception, e:
# may get here if due to ACM the operation is not permitted
if security.on():
raise VmError('Domain in conflict set with running domain?')
if self.domid < 0:
raise VmError('Creating domain failed: name=%s' %
@@ -1966,24 +1975,6 @@ class XendDomainInfo:
if image_sxpr:
to_store['image'] = sxp.to_string(image_sxpr)
if self._infoIsSet('security'):
secinfo = self.info['security']
to_store['security'] = sxp.to_string(secinfo)
for idx in range(0, len(secinfo)):
if secinfo[idx][0] == 'access_control':
to_store['security/access_control'] = sxp.to_string(
[secinfo[idx][1], secinfo[idx][2]])
for aidx in range(1, len(secinfo[idx])):
if secinfo[idx][aidx][0] == 'label':
to_store['security/access_control/label'] = \
secinfo[idx][aidx][1]
if secinfo[idx][aidx][0] == 'policy':
to_store['security/access_control/policy'] = \
secinfo[idx][aidx][1]
if secinfo[idx][0] == 'ssidref':
to_store['security/ssidref'] = str(secinfo[idx][1])
if not self._readVm('xend/restart_count'):
to_store['xend/restart_count'] = str(0)
@@ -2101,15 +2092,6 @@ class XendDomainInfo:
info["maxmem_kb"] = XendNode.instance() \
.physinfo_dict()['total_memory'] * 1024
#manually update ssidref / security fields
if security.on() and info.has_key('ssidref'):
if (info['ssidref'] != 0) and self.info.has_key('security'):
security_field = self.info['security']
if not security_field:
#create new security element
self.info.update({'security':
[['ssidref', str(info['ssidref'])]]})
#ssidref field not used any longer
if 'ssidref' in info:
info.pop('ssidref')
@@ -2193,7 +2175,133 @@ class XendDomainInfo:
return self.info.get('tools_version', {})
def get_metrics(self):
return self.metrics.get_uuid();
def get_security_label(self):
domid = self.getDomid()
from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
xspol = XSPolicyAdminInstance().get_loaded_policy()
if domid == 0:
if xspol:
label = xspol.policy_get_domain_label_formatted(domid)
else:
label = ""
else:
label = self.info.get('security_label', '')
return label
def set_security_label(self, seclab, old_seclab, xspol=None):
"""
Set the security label of a domain from its old to
a new value.
@param seclab New security label formatted in the form
<policy type>:<policy name>:<vm label>
@param old_seclab The current security label that the
VM must have.
@param xspol An optional policy under which this
update should be done. If not given,
then the current active policy is used.
@return Returns return code, a string with errors from
the hypervisor's operation, old label of the
domain
"""
rc = 0
errors = ""
old_label = ""
new_ssidref = 0
domid = self.getDomid()
res_labels = None
from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
from xen.util import xsconstants
state = self._stateGet()
# Relabel only HALTED or RUNNING or PAUSED domains
if domid != 0 and \
state not in \
[ DOM_STATE_HALTED, DOM_STATE_RUNNING, DOM_STATE_PAUSED, \
DOM_STATE_SUSPENDED ]:
log.warn("Relabeling domain not possible in state '%s'" %
DOM_STATES[state])
return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
# Remove security label. Works only for halted domains
if not seclab or seclab == "":
if state not in [ DOM_STATE_HALTED ]:
return (-xsconstants.XSERR_VM_WRONG_STATE, "", "", 0)
if self.info.has_key('security_label'):
old_label = self.info['security_label']
# Check label against expected one.
if old_label != old_seclab:
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
del self.info['security_label']
xen.xend.XendDomain.instance().managed_config_save(self)
return (xsconstants.XSERR_SUCCESS, "", "", 0)
tmp = seclab.split(":")
if len(tmp) != 3:
return (-xsconstants.XSERR_BAD_LABEL_FORMAT, "", "", 0)
typ, policy, label = tmp
poladmin = XSPolicyAdminInstance()
if not xspol:
xspol = poladmin.get_policy_by_name(policy)
if state in [ DOM_STATE_RUNNING, DOM_STATE_PAUSED ]:
#if domain is running or paused try to relabel in hypervisor
if not xspol:
return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
if typ != xspol.get_type_name() or \
policy != xspol.get_name():
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
if typ == xsconstants.ACM_POLICY_ID:
new_ssidref = xspol.vmlabel_to_ssidref(label)
if new_ssidref == xsconstants.INVALID_SSIDREF:
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
# Check that all used resources are accessible under the
# new label
if not security.resources_compatible_with_vmlabel(xspol,
self, label):
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
#Check label against expected one.
old_label = self.get_security_label()
if old_label != old_seclab:
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
# relabel domain in the hypervisor
rc, errors = security.relabel_domains([[domid, new_ssidref]])
log.info("rc from relabeling in HV: %d" % rc)
else:
return (-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED, "", "", 0)
if rc == 0:
# HALTED, RUNNING or PAUSED
if domid == 0:
if xspol:
ssidref = poladmin.set_domain0_bootlabel(xspol, label)
else:
return (-xsconstants.XSERR_POLICY_NOT_LOADED, "", "", 0)
else:
if self.info.has_key('security_label'):
old_label = self.info['security_label']
# Check label against expected one, unless wildcard
if old_label != old_seclab:
return (-xsconstants.XSERR_BAD_LABEL, "", "", 0)
self.info['security_label'] = seclab
try:
xen.xend.XendDomain.instance().managed_config_save(self)
except:
pass
return (rc, errors, old_label, new_ssidref)
def get_on_shutdown(self):
after_shutdown = self.info.get('actions_after_shutdown')
if not after_shutdown or after_shutdown not in XEN_API_ON_NORMAL_EXIT:
+18
View File
@@ -174,6 +174,23 @@ class NetworkError(XendAPIError):
def __str__(self):
return 'NETWORK_ERROR: %s %s' % (self.error, self.network)
from xen.util.xsconstants import xserr2string
class SecurityError(XendAPIError):
def __init__(self, error, message=None):
XendAPIError.__init__(self)
self.error = error
if not message:
self.message = xserr2string(-error)
else:
self.message = message
def get_api_error(self):
return ['SECURITY_ERROR', self.error, self.message]
def __str__(self):
return 'SECURITY_ERROR: %s:%s' % (self.error, self.message)
XEND_ERROR_AUTHENTICATION_FAILED = ('ELUSER', 'Authentication Failed')
XEND_ERROR_SESSION_INVALID = ('EPERMDENIED', 'Session Invalid')
@@ -188,4 +205,5 @@ XEND_ERROR_VIF_INVALID = ('EVIFINVALID', 'VIF Invalid')
XEND_ERROR_VTPM_INVALID = ('EVTPMINVALID', 'VTPM Invalid')
XEND_ERROR_VDI_INVALID = ('EVDIINVALID', 'VDI Invalid')
XEND_ERROR_SR_INVALID = ('ESRINVALID', 'SR Invalid')
XEND_ERROR_XSPOLICY_INVALID = ('EXSPOLICYINVALID', 'XS Invalid')
XEND_ERROR_TODO = ('ETODO', 'Lazy Programmer Error')
+12
View File
@@ -23,6 +23,7 @@ import os
from xen.util.xmlrpclib2 import stringify
from xmlrpclib import dumps, loads
from xen.util import security, xsconstants
KB = 1024
MB = 1024 * 1024
@@ -160,6 +161,17 @@ class XendVDI(AutoSaveObject):
def get_location(self):
raise NotImplementedError()
def set_security_label(self, sec_lab, old_lab):
image = self.get_location()
rc = security.set_resource_label_xapi(image, sec_lab, old_lab)
if rc != xsconstants.XSERR_SUCCESS:
raise SecurityError(rc)
return rc
def get_security_label(self):
image = self.get_location()
return security.get_resource_label_xapi(image)
class XendQCoWVDI(XendVDI):
+222
View File
@@ -0,0 +1,222 @@
#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (c) 2007 IBM Corporation
# Copyright (c) 2006 Xensource
#============================================================================
import logging
from xen.xend.XendBase import XendBase
from xen.xend.XendError import *
from xen.xend.XendXSPolicyAdmin import XSPolicyAdminInstance
from xen.util import xsconstants, security
import base64
log = logging.getLogger("xend.XendXSPolicy")
log.setLevel(logging.TRACE)
class XendXSPolicy(XendBase):
""" Administration class for an XSPolicy. """
def getClass(self):
return "XSPolicy"
def getMethods(self):
methods = ['activate_xspolicy']
return XendBase.getMethods() + methods
def getFuncs(self):
funcs = [ 'get_xstype',
'set_xspolicy',
'get_xspolicy',
'rm_xsbootpolicy',
'get_resource_label',
'set_resource_label',
'get_labeled_resources' ]
return XendBase.getFuncs() + funcs
getClass = classmethod(getClass)
getMethods = classmethod(getMethods)
getFuncs = classmethod(getFuncs)
def __init__(self, xspol, record, uuid):
""" xspol = actual XSPolicy object """
self.xspol = xspol
XendBase.__init__(self, uuid, record)
def get_record(self):
xspol_record = {
'uuid' : self.get_uuid(),
'flags' : XSPolicyAdminInstance().get_policy_flags(self.xspol),
'repr' : self.xspol.toxml(),
'type' : self.xspol.get_type(),
}
return xspol_record
def get_xstype(self):
return XSPolicyAdminInstance().isXSEnabled()
def set_xspolicy(self, xstype, xml, flags, overwrite):
ref = ""
xstype = int(xstype)
flags = int(flags)
polstate = { 'xs_ref': "", 'repr' : "", 'type' : 0,
'flags' : 0 , 'version': 0 , 'errors' : "", 'xserr' : 0 }
if xstype == xsconstants.XS_POLICY_ACM:
poladmin = XSPolicyAdminInstance()
try:
(xspol, rc, errors) = poladmin.add_acmpolicy_to_system(
xml, flags,
overwrite)
if rc != 0:
polstate.update( { 'xserr' : rc,
'errors': base64.b64encode(errors) } )
else:
ref = xspol.get_ref()
polstate = {
'xs_ref' : ref,
'flags' : poladmin.get_policy_flags(xspol),
'type' : xstype,
'repr' : "",
'version': xspol.get_version(),
'errors' : base64.b64encode(errors),
'xserr' : rc,
}
except Exception, e:
raise
else:
raise SecurityError(-xsconstants.XSERR_POLICY_TYPE_UNSUPPORTED)
return polstate
def activate_xspolicy(self, flags):
flags = int(flags)
rc = -xsconstants.XSERR_GENERAL_FAILURE
poladmin = XSPolicyAdminInstance()
try:
rc = poladmin.activate_xspolicy(self.xspol, flags)
except Exception, e:
log.info("Activate_policy: %s" % str(e))
if rc != flags:
raise SecurityError(rc)
return flags
def get_xspolicy(self):
polstate = { 'xs_ref' : "",
'repr' : "",
'type' : 0,
'flags' : 0,
'version': "",
'errors' : "",
'xserr' : 0 }
poladmin = XSPolicyAdminInstance()
refs = poladmin.get_policies_refs()
# Will return one or no policy
if refs and len(refs) > 0:
ref = refs[0]
xspol = XSPolicyAdminInstance().policy_from_ref(ref)
try:
xspol.grab_lock()
polstate = {
'xs_ref' : ref,
'repr' : xspol.toxml(),
'type' : xspol.get_type(),
'flags' : poladmin.get_policy_flags(xspol),
'version': xspol.get_version(),
'errors' : "",
'xserr' : 0,
}
finally:
if xspol:
xspol.unlock()
return polstate
def rm_xsbootpolicy(self):
rc = XSPolicyAdminInstance().rm_bootpolicy()
if rc != xsconstants.XSERR_SUCCESS:
raise SecurityError(rc)
def get_labeled_resources(self):
return security.get_labeled_resources_xapi()
def set_resource_label(self, resource, sec_lab, old_lab):
rc = security.set_resource_label_xapi(resource, sec_lab, old_lab)
if rc != xsconstants.XSERR_SUCCESS:
raise SecurityError(rc)
def get_resource_label(self, resource):
res = security.get_resource_label_xapi(resource)
return res
get_xstype = classmethod(get_xstype)
get_xspolicy = classmethod(get_xspolicy)
set_xspolicy = classmethod(set_xspolicy)
rm_xsbootpolicy = classmethod(rm_xsbootpolicy)
set_resource_label = classmethod(set_resource_label)
get_resource_label = classmethod(get_resource_label)
get_labeled_resources = classmethod(get_labeled_resources)
class XendACMPolicy(XendXSPolicy):
""" Administration class of an ACMPolicy """
def getClass(self):
return "ACMPolicy"
def getAttrRO(self):
attrRO = [ 'xml',
'map',
'binary',
'header' ]
return XendXSPolicy.getAttrRO() + attrRO
getClass = classmethod(getClass)
getAttrRO = classmethod(getAttrRO)
def __init__(self, acmpol, record, uuid):
""" acmpol = actual ACMPolicy object """
self.acmpol = acmpol
XendXSPolicy.__init__(self, acmpol, record, uuid)
def get_record(self):
polstate = {
'uuid' : self.get_uuid(),
'flags' : XSPolicyAdminInstance().get_policy_flags(self.acmpol),
'repr' : self.acmpol.toxml(),
'type' : self.acmpol.get_type(),
}
return polstate
def get_header(self):
header = {
'policyname' : "", 'policyurl' : "", 'reference' : "",
'date' : "", 'namespaceurl' : "", 'version' : "",
}
try:
header = self.acmpol.get_header_fields_map()
except:
pass
return header
def get_xml(self):
return self.acmpol.toxml()
def get_map(self):
return self.acmpol.get_map()
def get_binary(self):
polbin = self.acmpol.get_bin()
return base64.b64encode(polbin)
+313
View File
@@ -0,0 +1,313 @@
#============================================================================
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#============================================================================
# Copyright (C) 2006,2007 International Business Machines Corp.
# Author: Stefan Berger <stefanb@us.ibm.com>
#============================================================================
import os
import shutil
from xml.dom import minidom, Node
from xen.xend.XendLogging import log
from xen.xend import uuid
from xen.util import security, xsconstants, dictio, bootloader
from xen.util.xspolicy import XSPolicy
from xen.util.acmpolicy import ACMPolicy
from xen.xend.XendError import SecurityError
XS_MANAGED_POLICIES_FILE = "/etc/xen/acm-security/policies/managed_policies"
class XSPolicyAdmin:
""" The class that handles the managed policies in the system.
Handles adding and removing managed policies. All managed
policies are handled using a reference (UUID) which is
assigned to the policy by this class.
"""
def __init__(self, maxpolicies):
""" Create a management class for managing the system's
policies.
@param maxpolicies: The max. number of policies allowed
on the system (currently '1')
"""
self.maxpolicies = maxpolicies
try:
self.policies = dictio.dict_read("managed_policies",
XS_MANAGED_POLICIES_FILE)
except Exception, e:
self.policies = {}
self.xsobjs = {}
for ref, data in self.policies.items():
name = data[0]
typ = data[1]
try:
if typ == xsconstants.ACM_POLICY_ID:
self.xsobjs[ref] = ACMPolicy(name=name, ref=ref)
else:
del self.policies[ref]
except Exception, e:
log.error("XSPolicyAdmin: Could not find policy '%s': %s" %
(name, str(e)))
del self.policies[ref]
log.debug("XSPolicyAdmin: Known policies: %s" % self.policies)
def isXSEnabled(self):
""" Check whether 'security' is enabled on this system.
This currently only checks for ACM-enablement.
"""
rc = 0
if security.on():
rc |= xsconstants.XS_POLICY_ACM
return rc
def add_acmpolicy_to_system(self, xmltext, flags, overwrite):
""" Add an ACM policy's xml representation to the system. The
policy will automatically be compiled
flags:
XS_INST_BOOT : make policy the one to boot the system with
by default; if there's a policy already installed,
refuse to install this policy unless its one with
the same name
XS_INST_LOAD : load the policy immediately; if this does not work
refuse to install this policy
overwrite:
If any policy is installed and this is False, refuse to install
this policy
If flags is True, then any existing policy will be removed from
the system and the new one will be installed
"""
errors = ""
loadedpol = self.get_loaded_policy()
if loadedpol:
# This is meant as an update to a currently loaded policy
if flags & xsconstants.XS_INST_LOAD == 0:
raise SecurityError(-xsconstants.XSERR_POLICY_LOADED)
rc, errors = loadedpol.update(xmltext)
if rc == 0:
self.rm_bootpolicy()
irc = self.activate_xspolicy(loadedpol, flags)
return (loadedpol, rc, errors)
try:
dom = minidom.parseString(xmltext.encode("utf-8"))
except:
raise SecurityError(-xsconstants.XSERR_BAD_XML)
ref = uuid.createString()
acmpol = ACMPolicy(dom=dom, ref=ref)
#First some basic tests that do not modify anything:
if flags & xsconstants.XS_INST_BOOT and not overwrite:
filename = acmpol.get_filename(".bin","",dotted=True)
if bootloader.get_default_policy != None and \
not bootloader.loads_default_policy(filename):
raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED)
if not overwrite and len(self.policies) >= self.maxpolicies:
raise SecurityError(-xsconstants.XSERR_BOOTPOLICY_INSTALLED)
if overwrite:
#This should only give one key since only one policy is
#allowed.
keys = self.policies.keys()
for k in keys:
self.rm_bootpolicy()
rc = self.rm_policy_from_system(k, force=overwrite)
if rc != xsconstants.XSERR_SUCCESS:
raise SecurityError(rc)
rc = acmpol.compile()
if rc != 0:
raise SecurityError(rc)
if flags & xsconstants.XS_INST_LOAD:
rc = acmpol.loadintohv()
if rc != 0:
raise SecurityError(rc)
if flags & xsconstants.XS_INST_BOOT:
rc = self.make_boot_policy(acmpol)
if rc != 0:
# If it cannot be installed due to unsupported
# bootloader, let it be ok.
pass
if dom:
new_entry = { ref : tuple([acmpol.get_name(),
xsconstants.ACM_POLICY_ID]) }
self.policies.update(new_entry)
self.xsobjs[ref] = acmpol
dictio.dict_write(self.policies,
"managed_policies",
XS_MANAGED_POLICIES_FILE)
return (acmpol, xsconstants.XSERR_SUCCESS, errors)
def make_boot_policy(self, acmpol):
spolfile = acmpol.get_filename(".bin")
dpolfile = "/boot/" + acmpol.get_filename(".bin","",dotted=True)
if not os.path.isfile(spolfile):
log.error("binary policy file does not exist.")
return -xsconstants.XSERR_FILE_ERROR
try:
shutil.copyfile(spolfile, dpolfile)
except:
return -xsconstants.XSERR_FILE_ERROR
try:
filename = acmpol.get_filename(".bin","",dotted=True)
if bootloader.set_default_boot_policy(filename) != True:
return xsconstants.XSERR_BOOTPOLICY_INSTALL_ERROR
except:
return xsconstants.XSERR_FILE_ERROR
return xsconstants.XSERR_SUCCESS
def activate_xspolicy(self, xspol, flags):
rc = xsconstants.XSERR_SUCCESS
if flags & xsconstants.XS_INST_LOAD:
rc = xspol.loadintohv()
if rc == xsconstants.XSERR_SUCCESS and \
flags & xsconstants.XS_INST_BOOT:
rc = self.make_boot_policy(xspol)
if rc == xsconstants.XSERR_SUCCESS:
rc = flags
return rc
def rm_policy_from_system(self, ref, force=False):
if self.policies.has_key(ref):
acmpol = self.xsobjs[ref]
rc = acmpol.destroy()
if rc == xsconstants.XSERR_SUCCESS or force:
del self.policies[ref]
del self.xsobjs[ref]
dictio.dict_write(self.policies,
"managed_policies",
XS_MANAGED_POLICIES_FILE)
rc = xsconstants.XSERR_SUCCESS
return rc
def rm_bootpolicy(self):
""" Remove any (ACM) boot policy from the grub configuration file
"""
rc = 0
title = bootloader.get_default_title()
if title != None:
polnames = []
for (k, v) in self.xsobjs.items():
polnames.append(v.get_filename(".bin","",dotted=True))
bootloader.rm_policy_from_boottitle(title, polnames)
else:
rc = -xsconstants.XSERR_NO_DEFAULT_BOOT_TITLE
return rc
def get_policy_flags(self, acmpol):
""" Get the currently active flags of a policy, i.e., whether the
system is using this policy as its boot policy for the default
boot title.
"""
flags = 0
filename = acmpol.get_filename(".bin","", dotted=True)
if bootloader.loads_default_policy(filename):
flags |= xsconstants.XS_INST_BOOT
if acmpol.isloaded():
flags |= xsconstants.XS_INST_LOAD
return flags
def get_policies(self):
""" Get all managed policies. """
return self.xsobjs.values()
def get_policies_refs(self):
""" Get all managed policies' references. """
return self.xsobjs.keys()
def has_ref(self, ref):
""" Check whether there is a policy with the given reference """
return self.xsobjs.has_key(ref)
def policy_from_ref(self, ref):
""" Get the policy's object given its reference """
if ref in self.xsobjs.keys():
return self.xsobjs[ref]
return None
def ref_from_polname(self, polname):
""" Get the reference of the policy given its name """
ref = None
for (k, v) in self.xsobjs.items():
if v.get_name() == polname:
ref = k
break
return ref
def lock_policy(self, ref):
""" get exclusive access to a policy """
self.xsobjs[ref].grab_lock()
def unlock_policy(self, ref):
""" release exclusive access to a policy """
self.xsobjs[ref].unlock()
def get_loaded_policy(self):
for pol in self.xsobjs.values():
if pol.isloaded():
return pol
return None
def get_policy_by_name(self, name):
for pol in self.xsobjs.values():
if pol.get_name() == name:
return pol
return None
def get_domain0_bootlabel(self):
""" Get the domain0 bootlabel from the default boot title """
title = ""
def_title = bootloader.get_default_title()
line = bootloader.get_kernel_val(def_title, "ssidref")
if line:
parms = line.split(":",1)
if len(parms) > 1:
title = parms[1]
return title
def set_domain0_bootlabel(self, xspol, label):
""" Set the domain-0 bootlabel under the given policy """
return xspol.set_vm_bootlabel(label)
def rm_domain0_bootlabel(self):
""" Remove the domain-0 bootlabel from the default boot title """
def_title = bootloader.get_default_title()
return bootloader.set_kernel_attval(def_title, "ssidref", None)
def ssidref_to_vmlabel(self, ssidref):
""" Given an ssidref, return the vmlabel under the current policy """
vmlabel = ""
pol = self.get_loaded_policy()
if pol:
vmlabel = pol.policy_get_domain_label_by_ssidref_formatted(ssidref)
return vmlabel
poladmin = None
def XSPolicyAdminInstance(maxpolicies=1):
global poladmin
if poladmin == None:
poladmin = XSPolicyAdmin(maxpolicies)
+11 -4
View File
@@ -73,10 +73,17 @@ class BlkifController(DevController):
back['uuid'] = uuid
if security.on():
(label, ssidref, policy) = security.get_res_security_details(uname)
back.update({'acm_label' : label,
'acm_ssidref': str(ssidref),
'acm_policy' : policy})
(label, ssidref, policy) = \
security.get_res_security_details(uname)
domain_label = self.vm.get_security_label()
if domain_label:
rc = security.res_security_check_xapi(label, ssidref, policy,
domain_label)
if rc == 0:
raise VmError("VM's access to block device '%s' denied." %
uname)
else:
raise VmError("VM must have a security label.")
devid = blkif.blkdev_name_to_number(dev)
if devid is None:
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.