From 5d59bd7a0556e1d10cec3548cd3cf85e2fa7530c Mon Sep 17 00:00:00 2001 From: Evan Broder Date: Tue, 15 Dec 2009 22:10:20 -0500 Subject: [PATCH] Move XVM's locker authorization code into a separate xvm-authz-locker package (and corresponding xvm.authz.locker Python package). This makes it possible to install invirt-base without needing the authz code installed as well, and also separates some very XVM-specific logic from the Invirt packages. svn path=/trunk/packages/xvm-authz-locker/; revision=2602 --- debian/changelog | 5 ++ debian/compat | 1 + debian/control | 15 +++++ debian/copyright | 16 ++++++ debian/pycompat | 1 + debian/pyversions | 1 + debian/rules | 9 +++ python/xvm/authz/locker.py | 132 ++++++++++++++++++++++++++++++++++++++++++++ setup.py | 28 ++++++++++ 9 files changed, 208 insertions(+) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/pycompat create mode 100644 debian/pyversions create mode 100755 debian/rules create mode 100644 python/xvm/__init__.py create mode 100644 python/xvm/authz/__init__.py create mode 100644 python/xvm/authz/locker.py create mode 100755 setup.py diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..cd5a06b --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +xvm-authz-locker (0.0.1) unstable; urgency=low + + * Initial release. + + -- Evan Broder Tue, 15 Dec 2009 19:09:02 -0500 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..b8626c4 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +4 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..a236774 --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: xvm-authz-locker +Section: base +Priority: extra +Maintainer: Invirt project +Build-Depends: cdbs (>= 0.4.23-1.1), debhelper (>= 4.1.0), python-all-dev, python-support, python-setuptools, python-debian, python-apt +Standards-Version: 3.8.0 + +Package: xvm-authz-locker +Architecture: all +Depends: ${python:Depends}, ${misc:Depends}, invirt-base, python-afs +Provides: ${python:Provides}, invirt-authz +XB-Python-Version: ${python:Versions} +Description: Authorization module for XVM + This package contains an authorization module for XVM. It supports + the locker authorization scheme. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..361ca8f --- /dev/null +++ b/debian/copyright @@ -0,0 +1,16 @@ +This software was written as part of the Invirt project . + +Copyright : + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + +On Debian systems, the complete text of the GNU General Public License +can be found in the file /usr/share/common-licenses/GPL. diff --git a/debian/pycompat b/debian/pycompat new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/debian/pycompat @@ -0,0 +1 @@ +2 diff --git a/debian/pyversions b/debian/pyversions new file mode 100644 index 0000000..b3dc41e --- /dev/null +++ b/debian/pyversions @@ -0,0 +1 @@ +2.5- diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..ac32732 --- /dev/null +++ b/debian/rules @@ -0,0 +1,9 @@ +#!/usr/bin/make -f + +DEB_PYTHON_SYSTEM=pysupport + +include /usr/share/cdbs/1/rules/debhelper.mk +include /usr/share/cdbs/1/class/python-distutils.mk + +clean:: + rm -rf python/xvm.authz.locker.egg-info diff --git a/python/xvm/__init__.py b/python/xvm/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/xvm/authz/__init__.py b/python/xvm/authz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/xvm/authz/locker.py b/python/xvm/authz/locker.py new file mode 100644 index 0000000..cbfc28a --- /dev/null +++ b/python/xvm/authz/locker.py @@ -0,0 +1,132 @@ +import errno + +from afs import acl +from afs import fs +from afs import pts + +from invirt import common +from invirt.config import structs as config +from invirt import remctl + + +# +# expandOwner and expandAdmin form the API that needs to be exported +# for all authz modules. +# + + +def expandOwner(name): + """Expand an owner to a list of authorized users. + + For the locker authz module, an owner is an Athena locker. Those + users who have been given the administrator ('a') bit on the root + of a locker are given access to any VM owned by that locker, + unless they also have been given a negative administrator bit. + + If a locker doesn't exist, or we can't access the permissions, we + assume the ACL is empty. + """ + try: + path = _lockerPath(name) + cell = fs.whichcell(path) + auth = _authenticate(cell) + a = acl.ACL.retrieve(path) + + allowed = set() + for ent in a.pos: + if a.pos[ent] & acl.ADMINISTER: + allowed.update(_expandGroup(ent, cell=cell, auth=auth)) + for ent in a.neg: + if a.neg[ent] & acl.ADMINISTER: + allowed.difference_update(_expandGroup(ent, cell=cell, auth=auth)) + + return allowed + except OSError, e: + if e.errno in (errno.ENOENT, errno.EACCES): + return [] + else: + raise + + +def expandAdmin(name, owner): + """Expand an administrator to a list of authorized users. + + Because the interpretation of an administrator might depend on the + owner, the owner is passed in as an argument. + + However, in the case of locker-based authentication, the + administrator is always interpreted as an AFS entry (either a user + or a group) in the home cell (athena.mit.edu for XVM). + """ + cell = config.authz.afs.cells[0].cell + auth = _authenticate(cell) + return _expandGroup(name, cell=cell, auth=auth) + + +# +# These are helper functions, and aren't part of the authz API +# + + +def _authenticate(cell): + """Acquire AFS tokens for a cell if encryption is required by config. + + If the Invirt configuration requires connections to this cell to + be encrypted, acquires tokens and returns True. Otherwise, returns + False. Consumers of this function must still be sure to encrypt + their own connections if necessary. + + Cells not listed in the Invirt configuration default to requiring + encryption in order to maintain security by default. + + Due to AFS's cross-realm auto-PTS-creation mechanism, using + authenticated connections by default should only fail for cells + which authenticate directly against the machine's home realm and + cells distantly related to the machine's home realm. + """ + for c in config.authz.afs.cells: + if c.cell == cell and not c.auth: + return False + + remctl.checkKinit() + common.captureOutput(['aklog', '-c', cell]) + return True + + +def _expandGroup(name, cell=None, auth=False): + """Expand an AFS group into a list of its members. + + Because groups are not global, but can vary from cell to cell, + this function accepts as an optional argument the cell in which + this group should be resolved. + + If no cell is specified, it is assumed that the default cell (or + ThisCell) should be used. + + If the name is a user, not a group, then a single-element set with + the same name is returned. + + As with expandOwner, if a group doesn't exist or if we're unable + to retrieve its membership, we assume it's empty. + """ + try: + ent = pts.PTS(cell, pts.PTS_ENCRYPT if auth else pts.PTS_UNAUTH).\ + getEntry(name) + if ent.id > 0: + return set([ent.name]) + else: + return set([x.name for x in ent.members]) + except OSError, e: + if e.errno in (errno.ENOENT, errno.EACCESS): + return set() + else: + raise + + +def _lockerPath(owner): + """Given the name of a locker, return a path to that locker. + + This turns out to be pretty simple, thanks to the /mit + automounter. + """ + return '/mit/%s' % owner diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..080e5b1 --- /dev/null +++ b/setup.py @@ -0,0 +1,28 @@ +#!/usr/bin/python + +from os import path +from email.utils import parseaddr +from glob import glob +from setuptools import setup, find_packages + +try: + from debian_bundle.changelog import Changelog + from debian_bundle.deb822 import Deb822 + version = Changelog(open(path.join(path.dirname(__file__), 'debian/changelog')).read()).\ + get_version().full_version + + maintainer_full = Deb822(open(path.join(path.dirname(__file__), 'debian/control')))['Maintainer'] + maintainer, maintainer_email = parseaddr(maintainer_full) +except: + version = '0.0.0' + maintainer, maintainer_email = parseaddr('Invirt project ') + +setup( + name='xvm.authz.locker', + version=version, + maintainer=maintainer, + maintainer_email=maintainer_email, + + packages=find_packages('python'), + package_dir = {'': 'python'}, +) -- 1.7.9.5