mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
[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:
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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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, ""
|
||||
@@ -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
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -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()])
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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:
|
||||
|
||||