oslo.privsep-1.27.0/0000775000175100017510000000000013236475136014302 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/0000775000175100017510000000000013236475136016773 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/0000775000175100017510000000000013236475136020273 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/pike.rst0000666000175100017510000000021713236474711021755 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike oslo.privsep-1.27.0/releasenotes/source/_templates/0000775000175100017510000000000013236475136022430 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/_templates/.placeholder0000666000175100017510000000000013236474711024701 0ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/newton.rst0000666000175100017510000000021613236474711022336 0ustar zuulzuul00000000000000============================= Newton Series Release Notes ============================= .. release-notes:: :branch: origin/stable/newton oslo.privsep-1.27.0/releasenotes/source/unreleased.rst0000666000175100017510000000014413236474711023153 0ustar zuulzuul00000000000000========================== Unreleased Release Notes ========================== .. release-notes:: oslo.privsep-1.27.0/releasenotes/source/index.rst0000666000175100017510000000024413236474726022142 0ustar zuulzuul00000000000000============================ oslo.privsep Release Notes ============================ .. toctree:: :maxdepth: 1 unreleased pike ocata newton oslo.privsep-1.27.0/releasenotes/source/locale/0000775000175100017510000000000013236475136021532 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/locale/en_GB/0000775000175100017510000000000013236475136022504 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013236475136024271 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000666000175100017510000000206113236474711027321 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.privsep Release Notes 1.23.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-09-20 20:59+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-10-07 09:29+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "1.13.0" msgstr "1.13.0" msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Switch to reno for managing release notes." msgstr "Switch to Reno for managing release notes." msgid "Unreleased Release Notes" msgstr "Unreleased Release Notes" msgid "oslo.privsep Release Notes" msgstr "oslo.privsep Release Notes" oslo.privsep-1.27.0/releasenotes/source/locale/fr/0000775000175100017510000000000013236475136022141 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/locale/fr/LC_MESSAGES/0000775000175100017510000000000013236475136023726 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po0000666000175100017510000000171313236474711026761 0ustar zuulzuul00000000000000# Gérald LONLAS , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.privsep Release Notes 1.14.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-10-23 20:38+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-10-22 06:04+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "1.13.0" msgstr "1.13.0" msgid "Newton Series Release Notes" msgstr "Note de release pour Newton" msgid "Other Notes" msgstr "Autres notes" msgid "Switch to reno for managing release notes." msgstr "Commence à utiliser reno pour la gestion des notes de release" msgid "Unreleased Release Notes" msgstr "Note de release pour les changements non déployées" msgid "oslo.privsep Release Notes" msgstr "Note de release pour oslo.privsep" oslo.privsep-1.27.0/releasenotes/source/conf.py0000666000175100017510000002155713236474711021604 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'openstack/oslo.privsep' bug_project = 'oslo.privsep' bug_tag = '' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'oslo.privsep Release Notes' copyright = u'2016, oslo.privsep Developers' # Release notes do not need a version in the title, they span # multiple versions. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'oslo.privsepReleaseNotesDoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'oslo.privsepReleaseNotes.tex', u'oslo.privsep Release Notes Documentation', u'oslo.privsep Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'oslo.privsepReleaseNotes', u'oslo.privsep Release Notes Documentation', [u'oslo.privsep Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'oslo.privsepReleaseNotes', u'oslo.privsep Release Notes Documentation', u'oslo.privsep Developers', 'oslo.privsepReleaseNotes', 'OpenStack library for privilege separation.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] oslo.privsep-1.27.0/releasenotes/source/ocata.rst0000666000175100017510000000023013236474711022107 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata oslo.privsep-1.27.0/releasenotes/source/_static/0000775000175100017510000000000013236475136021721 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/source/_static/.placeholder0000666000175100017510000000000013236474711024172 0ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/notes/0000775000175100017510000000000013236475136020123 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml0000666000175100017510000000007113236474711025004 0ustar zuulzuul00000000000000--- other: - Switch to reno for managing release notes.oslo.privsep-1.27.0/ChangeLog0000664000175100017510000001226113236475135016055 0ustar zuulzuul00000000000000CHANGES ======= 1.27.0 ------ * msgpack-python has been renamed to msgpack * Update .gitreview for stable/queens 1.26.0 ------ * Updated from global requirements 1.25.0 ------ * Expose caps values/names as int enum * add bandit to pep8 job 1.24.0 ------ * Remove -U from pip install * Avoid tox\_install.sh for constraints support * Updated from global requirements * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Updated from global requirements 1.23.0 ------ * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Update reno for stable/pike * Updated from global requirements * Update capabilities from current kernel source 1.22.0 ------ * Updated from global requirements * Update URLs in documents according to document migration * add sphinx instructions to build API reference docs * switch from oslosphinx to openstackdocstheme * rearrange existing documentation to follow the new standard layout 1.21.1 ------ * Enable some off-by-default checks 1.21.0 ------ * Updated from global requirements * Updated from global requirements * Remove pbr warnerrors in favor of sphinx check * Updated from global requirements * Using assertIsNone(xxx) instead of assertEqual(None, xxx) * Updated from global requirements * Updated from global requirements * Updated from global requirements 1.20.0 ------ * Updated from global requirements * Add test to verify log record can be formatted * Updated from global requirements 1.19.0 ------ * Remove log translations 1.18.0 ------ * Use iterable object for 'args' in log record * Updated from global requirements 1.17.0 ------ * Updated from global requirements * [Fix gate]Update test requirement * Updated from global requirements * pbr.version.VersionInfo needs package name (oslo.xyz and not oslo\_xyz) * Update reno for stable/ocata * Remove references to Python 3.4 1.16.0 ------ * Add Constraints support * Show team and repo badges on README 1.15.0 ------ * Updated from global requirements * Don't use deprecated method logger.warn * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements 1.14.0 ------ * Enable release notes translation * Updated from global requirements * Updated from global requirements * modify the home-page info with the developer documentation * Ignore timeout error when receiving message from sockect * Update reno for stable/newton * Deal with CONF.config\_dir correctly * Preserve all LogRecord fields from privileged side 1.13.0 ------ * Report underlying integer for unknown capabilities * Updated from global requirements 1.12.0 ------ * Add Python 3.5 classifier and venv * Fixes unit tests on Windows * More sophisticated logging on privileged side 1.11.0 ------ * Updated from global requirements * Use default value for undefined caps in fmt\_caps 1.10.0 ------ * Updated from global requirements * Add reno for release notes management * Updated from global requirements * Updated from global requirements 1.9.0 ----- * Provide way to "initialise" oslo.privsep * PrivContext: Sets client\_mode to False on Windows * Imported Translations from Zanata 1.8.0 ----- * Updated from global requirements * Drop python3.3 support in classifier 1.7.0 ----- 1.6.0 ----- * Imported Translations from Zanata * Remove unused py27 socketpair/makefile workaround * Remove direct dependency on babel * Updated from global requirements 1.5.0 ----- * Updated from global requirements * Updated from global requirements * Switch to msgpack for serialization * Updated from global requirements 1.3.0 ----- * Updated from global requirements 1.2.0 ----- * Updated from global requirements * fdopen: Use better "is using eventlet" test * Ensure fdopen uses greenio object under eventlet 1.1.0 ----- * UnprivilegedPrivsepFixture: Clear capabilities config * Change name of privsep\_helper to match code 1.0.0 ----- * Ignore --config-dir when value is None * Add version and download badges to README * Update translation setup * Updated from global requirements * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata * Updated from global requirements * Update/make better the README.rst long description 0.3.0 ----- * Improve \`helper\_command' config default * Updated from global requirements * Replace deprecated LOG.warn with LOG.warning * Updated from global requirements * Use logging intead of oslo\_log * Remove unused file openstack-common.conf * remove python 2.6 trove classifier 0.2.0 ----- * Updated from global requirements * Updated from global requirements * Removes MANIFEST.in as it is not needed explicitely by PBR * Remove python 2.6 and tox.ini cleanup * Don't fail badly on windows 0.1.0 ----- * Updated from global requirements * Updated from global requirements * Imported Translations from Zanata * Add support for Linux capabilities * Enable translations * Initial basic privsep functionality * Tell git to ignore /.eggs dir too * Updated from global requirements * oslo.i18n boilerplate * Initial cookiecutter project * Added .gitreview oslo.privsep-1.27.0/oslo_privsep/0000775000175100017510000000000013236475136017026 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/__init__.py0000666000175100017510000000000013236474711021125 0ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/comm.py0000666000175100017510000001324113236474711020334 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Serialization/Deserialization for privsep. The wire format is a stream of msgpack objects encoding primitive python datatypes. Msgpack 'raw' is assumed to be a valid utf8 string (msgpack 2.0 'bin' type is used for bytes). Python lists are converted to tuples during serialization/deserialization. """ import logging import socket import threading import msgpack import six from oslo_privsep._i18n import _ LOG = logging.getLogger(__name__) try: import greenlet def _get_thread_ident(): # This returns something sensible, even if the current thread # isn't a greenthread return id(greenlet.getcurrent()) except ImportError: def _get_thread_ident(): return threading.current_thread().ident class Serializer(object): def __init__(self, writesock): self.writesock = writesock def send(self, msg): buf = msgpack.packb(msg, use_bin_type=True) self.writesock.sendall(buf) def close(self): # Hilarious. `socket._socketobject.close()` doesn't actually # call `self._sock.close()`. Oh well, we really wanted a half # close anyway. self.writesock.shutdown(socket.SHUT_WR) class Deserializer(six.Iterator): def __init__(self, readsock): self.readsock = readsock self.unpacker = msgpack.Unpacker(use_list=False, encoding='utf-8') def __iter__(self): return self def __next__(self): while True: try: return next(self.unpacker) except StopIteration: try: buf = self.readsock.recv(4096) if not buf: raise self.unpacker.feed(buf) except socket.timeout: pass class Future(object): """A very simple object to track the return of a function call""" def __init__(self, lock): self.condvar = threading.Condition(lock) self.error = None self.data = None def set_result(self, data): """Must already be holding lock used in constructor""" self.data = data self.condvar.notify() def set_exception(self, exc): """Must already be holding lock used in constructor""" self.error = exc self.condvar.notify() def result(self): """Must already be holding lock used in constructor""" self.condvar.wait() if self.error is not None: raise self.error return self.data class ClientChannel(object): def __init__(self, sock): self.writer = Serializer(sock) self.lock = threading.Lock() self.reader_thread = threading.Thread( name='privsep_reader', target=self._reader_main, args=(Deserializer(sock),), ) self.reader_thread.daemon = True self.outstanding_msgs = {} self.reader_thread.start() def _reader_main(self, reader): """This thread owns and demuxes the read channel""" for msg in reader: msgid, data = msg if msgid is None: self.out_of_band(data) else: with self.lock: if msgid not in self.outstanding_msgs: raise AssertionError("msgid should in " "outstanding_msgs.") self.outstanding_msgs[msgid].set_result(data) # EOF. Perhaps the privileged process exited? # Send an IOError to any oustanding waiting readers. Assuming # the write direction is also closed, any new writes should # get an immediate similar error. LOG.debug('EOF on privsep read channel') exc = IOError(_('Premature eof waiting for privileged process')) with self.lock: for mbox in self.outstanding_msgs.values(): mbox.set_exception(exc) def out_of_band(self, msg): """Received OOB message. Subclasses might want to override this.""" pass def send_recv(self, msg): myid = _get_thread_ident() future = Future(self.lock) with self.lock: if myid in self.outstanding_msgs: raise AssertionError("myid shoudn't be in outstanding_msgs.") self.outstanding_msgs[myid] = future try: self.writer.send((myid, msg)) reply = future.result() finally: del self.outstanding_msgs[myid] return reply def close(self): with self.lock: self.writer.close() self.reader_thread.join() class ServerChannel(six.Iterator): """Server-side twin to ClientChannel""" def __init__(self, sock): self.rlock = threading.Lock() self.reader_iter = iter(Deserializer(sock)) self.wlock = threading.Lock() self.writer = Serializer(sock) def __iter__(self): return self def __next__(self): with self.rlock: return next(self.reader_iter) def send(self, msg): with self.wlock: self.writer.send(msg) oslo.privsep-1.27.0/oslo_privsep/priv_context.py0000666000175100017510000002014713236474711022130 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import enum import functools import logging import shlex import sys from oslo_config import cfg from oslo_config import types from oslo_utils import importutils from oslo_privsep._i18n import _ from oslo_privsep import capabilities from oslo_privsep import daemon LOG = logging.getLogger(__name__) def CapNameOrInt(value): value = str(value).strip() try: return capabilities.CAPS_BYNAME[value] except KeyError: return int(value) OPTS = [ cfg.StrOpt('user', help=_('User that the privsep daemon should run as.')), cfg.StrOpt('group', help=_('Group that the privsep daemon should run as.')), cfg.Opt('capabilities', type=types.List(CapNameOrInt), default=[], help=_('List of Linux capabilities retained by the privsep ' 'daemon.')), cfg.StrOpt('helper_command', help=_('Command to invoke to start the privsep daemon if ' 'not using the "fork" method. ' 'If not specified, a default is generated using ' '"sudo privsep-helper" and arguments designed to ' 'recreate the current configuration. ' 'This command must accept suitable --privsep_context ' 'and --privsep_sock_path arguments.')), ] _ENTRYPOINT_ATTR = 'privsep_entrypoint' _HELPER_COMMAND_PREFIX = ['sudo'] @enum.unique class Method(enum.Enum): FORK = 1 ROOTWRAP = 2 def init(root_helper=None): """Initialise oslo.privsep library. This function should be called at the top of main(), after the command line is parsed, oslo.config is initialised and logging is set up, but before calling any privileged entrypoint, changing user id, forking, or anything else "odd". :param root_helper: List of command and arguments to prefix privsep-helper with, in order to run helper as root. Note, ignored if context's helper_command config option is set. """ if root_helper: global _HELPER_COMMAND_PREFIX _HELPER_COMMAND_PREFIX = root_helper class PrivContext(object): def __init__(self, prefix, cfg_section='privsep', pypath=None, capabilities=None): # Note that capabilities=[] means retaining no capabilities # and leaves even uid=0 with no powers except being able to # read/write to the filesystem as uid=0. This might be what # you want, but probably isn't. # # There is intentionally no way to say "I want all the # capabilities." if capabilities is None: raise ValueError('capabilities is a required parameter') self.pypath = pypath self.prefix = prefix self.cfg_section = cfg_section # NOTE(claudiub): oslo.privsep is not currently supported on Windows, # as it uses Linux-specific functionality (os.fork, socker.AF_UNIX). # The client_mode should be set to False on Windows. self.client_mode = sys.platform != 'win32' self.channel = None cfg.CONF.register_opts(OPTS, group=cfg_section) cfg.CONF.set_default('capabilities', group=cfg_section, default=capabilities) @property def conf(self): """Return the oslo.config section object as lazily as possible.""" # Need to avoid looking this up before oslo_config has been # properly initialized. return cfg.CONF[self.cfg_section] def __repr__(self): return 'PrivContext(cfg_section=%s)' % self.cfg_section def helper_command(self, sockpath): # We need to be able to reconstruct the context object in the new # python process we'll get after rootwrap/sudo. This means we # need to construct the context object and store it somewhere # globally accessible, and then use that python name to find it # again in the new python interpreter. Yes, it's all a bit # clumsy, and none of it is required when using the fork-based # alternative above. # These asserts here are just attempts to catch errors earlier. # TODO(gus): Consider replacing with setuptools entry_points. if self.pypath is None: raise AssertionError('helper_command requires priv_context ' 'pypath to be specified') if importutils.import_class(self.pypath) is not self: raise AssertionError('helper_command requires priv_context ' 'pypath for context object') # Note order is important here. Deployments will (hopefully) # have the exact arguments in sudoers/rootwrap configs and # reordering args will break configs! if self.conf.helper_command: cmd = shlex.split(self.conf.helper_command) else: cmd = _HELPER_COMMAND_PREFIX + ['privsep-helper'] try: for cfg_file in cfg.CONF.config_file: cmd.extend(['--config-file', cfg_file]) except cfg.NoSuchOptError: pass try: if cfg.CONF.config_dir is not None: for cfg_dir in cfg.CONF.config_dir: cmd.extend(['--config-dir', cfg_dir]) except cfg.NoSuchOptError: pass cmd.extend( ['--privsep_context', self.pypath, '--privsep_sock_path', sockpath]) return cmd def set_client_mode(self, enabled): if enabled and sys.platform == 'win32': raise RuntimeError( "Enabling the client_mode is not currently " "supported on Windows.") self.client_mode = enabled def entrypoint(self, func): """This is intended to be used as a decorator.""" if not func.__module__.startswith(self.prefix): raise AssertionError('%r entrypoints must be below "%s"' % (self, self.prefix)) # Right now, we only track a single context in # _ENTRYPOINT_ATTR. This could easily be expanded into a set, # but that will increase the memory overhead. Revisit if/when # someone has a need to associate the same entrypoint with # multiple contexts. if getattr(func, _ENTRYPOINT_ATTR, None) is not None: raise AssertionError('%r is already associated with another ' 'PrivContext' % func) f = functools.partial(self._wrap, func) setattr(f, _ENTRYPOINT_ATTR, self) return f def is_entrypoint(self, func): return getattr(func, _ENTRYPOINT_ATTR, None) is self def _wrap(self, func, *args, **kwargs): if self.client_mode: name = '%s.%s' % (func.__module__, func.__name__) if self.channel is None: self.start() return self.channel.remote_call(name, args, kwargs) else: return func(*args, **kwargs) def start(self, method=Method.ROOTWRAP): if self.channel is not None: LOG.warning('privsep daemon already running') return if method is Method.ROOTWRAP: channel = daemon.RootwrapClientChannel(context=self) elif method is Method.FORK: channel = daemon.ForkingClientChannel(context=self) else: raise ValueError('Unknown method: %s' % method) self.channel = channel def stop(self): if self.channel is not None: self.channel.close() self.channel = None oslo.privsep-1.27.0/oslo_privsep/capabilities.py0000666000175100017510000001227313236474711022036 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Hosting # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import enum import os import platform import sys import cffi class Capabilities(enum.IntEnum): # Generated with: # awk '/^#define CAP_[A-Z_]+[ \t]+[0-9]+/ {print $2,"=",$3}' \ # include/uapi/linux/capability.h # From the 4.11.11 kernel and the kernel git SHA:235b84fc862 # Will need to be refreshed as new capabilites are added to the kernel CAP_CHOWN = 0 CAP_DAC_OVERRIDE = 1 CAP_DAC_READ_SEARCH = 2 CAP_FOWNER = 3 CAP_FSETID = 4 CAP_KILL = 5 CAP_SETGID = 6 CAP_SETUID = 7 CAP_SETPCAP = 8 CAP_LINUX_IMMUTABLE = 9 CAP_NET_BIND_SERVICE = 10 CAP_NET_BROADCAST = 11 CAP_NET_ADMIN = 12 CAP_NET_RAW = 13 CAP_IPC_LOCK = 14 CAP_IPC_OWNER = 15 CAP_SYS_MODULE = 16 CAP_SYS_RAWIO = 17 CAP_SYS_CHROOT = 18 CAP_SYS_PTRACE = 19 CAP_SYS_PACCT = 20 CAP_SYS_ADMIN = 21 CAP_SYS_BOOT = 22 CAP_SYS_NICE = 23 CAP_SYS_RESOURCE = 24 CAP_SYS_TIME = 25 CAP_SYS_TTY_CONFIG = 26 CAP_MKNOD = 27 CAP_LEASE = 28 CAP_AUDIT_WRITE = 29 CAP_AUDIT_CONTROL = 30 CAP_SETFCAP = 31 CAP_MAC_OVERRIDE = 32 CAP_MAC_ADMIN = 33 CAP_SYSLOG = 34 CAP_WAKE_ALARM = 35 CAP_BLOCK_SUSPEND = 36 CAP_AUDIT_READ = 37 CAPS_BYNAME = {} CAPS_BYVALUE = {} module = sys.modules[__name__] # Convenience dicts for human readable values # module attributes for backwards compat/convenience for c in Capabilities: CAPS_BYNAME[c.name] = c.value CAPS_BYVALUE[c.value] = c.name setattr(module, c.name, c.value) CDEF = ''' /* Edited highlights from `echo '#include ' | gcc -E -` */ #define _LINUX_CAPABILITY_VERSION_2 0x20071026 #define _LINUX_CAPABILITY_U32S_2 2 typedef unsigned int __u32; typedef struct __user_cap_header_struct { __u32 version; int pid; } *cap_user_header_t; typedef struct __user_cap_data_struct { __u32 effective; __u32 permitted; __u32 inheritable; } *cap_user_data_t; int capset(cap_user_header_t header, const cap_user_data_t data); int capget(cap_user_header_t header, cap_user_data_t data); /* Edited highlights from `echo '#include ' | gcc -E -` */ #define PR_GET_KEEPCAPS 7 #define PR_SET_KEEPCAPS 8 int prctl (int __option, ...); ''' ffi = cffi.FFI() crt = ffi.dlopen(None) ffi.cdef(CDEF) if platform.system() == 'Linux': # mock.patching crt.* directly seems to upset cffi. Use an # indirection point here for easier testing. _prctl = crt.prctl _capget = crt.capget _capset = crt.capset else: _prctl = None _capget = None _capset = None def set_keepcaps(enable): """Set/unset thread's "keep capabilities" flag - see prctl(2)""" ret = _prctl(crt.PR_SET_KEEPCAPS, ffi.cast('unsigned long', bool(enable))) if ret != 0: errno = ffi.errno raise OSError(errno, os.strerror(errno)) def drop_all_caps_except(effective, permitted, inheritable): """Set (effective, permitted, inheritable) to provided list of caps""" eff = _caps_to_mask(effective) prm = _caps_to_mask(permitted) inh = _caps_to_mask(inheritable) header = ffi.new('cap_user_header_t', {'version': crt._LINUX_CAPABILITY_VERSION_2, 'pid': 0}) data = ffi.new('struct __user_cap_data_struct[2]') data[0].effective = eff & 0xffffffff data[1].effective = eff >> 32 data[0].permitted = prm & 0xffffffff data[1].permitted = prm >> 32 data[0].inheritable = inh & 0xffffffff data[1].inheritable = inh >> 32 ret = _capset(header, data) if ret != 0: errno = ffi.errno raise OSError(errno, os.strerror(errno)) def _mask_to_caps(mask): """Convert bitmask to list of set bit offsets""" return [i for i in range(64) if (1 << i) & mask] def _caps_to_mask(caps): """Convert list of bit offsets to bitmask""" mask = 0 for cap in caps: mask |= 1 << cap return mask def get_caps(): """Return (effective, permitted, inheritable) as lists of caps""" header = ffi.new('cap_user_header_t', {'version': crt._LINUX_CAPABILITY_VERSION_2, 'pid': 0}) data = ffi.new('struct __user_cap_data_struct[2]') ret = _capget(header, data) if ret != 0: errno = ffi.errno raise OSError(errno, os.strerror(errno)) return ( _mask_to_caps(data[0].effective | (data[1].effective << 32)), _mask_to_caps(data[0].permitted | (data[1].permitted << 32)), _mask_to_caps(data[0].inheritable | (data[1].inheritable << 32)), ) oslo.privsep-1.27.0/oslo_privsep/_i18n.py0000666000175100017510000000200713236474711020315 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """oslo.i18n integration module. See https://docs.openstack.org/oslo.i18n/latest/user/index.html """ import oslo_i18n _translators = oslo_i18n.TranslatorFactory(domain='oslo_privsep') # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" _C = _translators.contextual_form # The plural translation function using the name "_P" _P = _translators.plural_form oslo.privsep-1.27.0/oslo_privsep/locale/0000775000175100017510000000000013236475136020265 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/locale/en_GB/0000775000175100017510000000000013236475136021237 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013236475136023024 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/locale/en_GB/LC_MESSAGES/oslo_privsep.po0000666000175100017510000000436013236474711026113 0ustar zuulzuul00000000000000# Andi Chandler , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.privsep 1.7.1.dev1\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2016-06-10 16:43+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-06-09 11:12+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "" "Command to invoke to start the privsep daemon if not using the \"fork\" " "method. If not specified, a default is generated using \"sudo privsep-helper" "\" and arguments designed to recreate the current configuration. This " "command must accept suitable --privsep_context and --privsep_sock_path " "arguments." msgstr "" "Command to invoke to start the privsep daemon if not using the \"fork\" " "method. If not specified, a default is generated using \"sudo privsep-helper" "\" and arguments designed to recreate the current configuration. This " "command must accept suitable --privsep_context and --privsep_sock_path " "arguments." msgid "Failed to remove supplemental groups" msgstr "Failed to remove supplemental groups" #, python-format msgid "Failed to set gid %s" msgstr "Failed to set gid %s" #, python-format msgid "Failed to set uid %s" msgstr "Failed to set uid %s" msgid "Group that the privsep daemon should run as." msgstr "Group that the privsep daemon should run as." #, python-format msgid "Invalid privsep function: %s not exported" msgstr "Invalid privsep function: %s not exported" msgid "List of Linux capabilities retained by the privsep daemon." msgstr "List of Linux capabilities retained by the privsep daemon." msgid "Premature eof waiting for privileged process" msgstr "Premature EOF waiting for privileged process" msgid "Privsep daemon failed to start" msgstr "Privsep daemon failed to start" #, python-format msgid "Unexpected response: %r" msgstr "Unexpected response: %r" #, python-format msgid "Unknown privsep cmd: %s" msgstr "Unknown privsep cmd: %s" msgid "User that the privsep daemon should run as." msgstr "User that the privsep daemon should run as." oslo.privsep-1.27.0/oslo_privsep/locale/de/0000775000175100017510000000000013236475136020655 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/locale/de/LC_MESSAGES/0000775000175100017510000000000013236475136022442 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/locale/de/LC_MESSAGES/oslo_privsep.po0000666000175100017510000000330413236474711025526 0ustar zuulzuul00000000000000# Andreas Jaeger , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: oslo.privsep 1.7.1.dev1\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2016-06-10 16:43+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-06-09 01:20+0000\n" "Last-Translator: Andreas Jaeger \n" "Language-Team: German\n" "Language: de\n" "X-Generator: Zanata 3.7.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "Failed to remove supplemental groups" msgstr "Fehler beim Entfernen zusätzlicher Gruppen" #, python-format msgid "Failed to set gid %s" msgstr "Fehler beim Festlegen von GID %s" #, python-format msgid "Failed to set uid %s" msgstr "Fehler beim Festlegen von Benutzer-ID %s" msgid "Group that the privsep daemon should run as." msgstr "Gruppe als die der Privsep Dämon laufen soll." #, python-format msgid "Invalid privsep function: %s not exported" msgstr "Invalide Privsep Funktion: %s ist nicht exportiert." msgid "List of Linux capabilities retained by the privsep daemon." msgstr "Liste von Linux Capabilities, die der Privsep Dämon behält." msgid "Premature eof waiting for privileged process" msgstr "Vorzeitiges Dateiende beim Warten auf den priviligierten Prozeß" msgid "Privsep daemon failed to start" msgstr "Der Privsep Dämon konnte nicht gestartet werden." #, python-format msgid "Unexpected response: %r" msgstr "Unerwartete Antwort: %r" #, python-format msgid "Unknown privsep cmd: %s" msgstr "Unbekanntes Privsep Kommando: %s" msgid "User that the privsep daemon should run as." msgstr "User als der der Privsep Dämon laufen soll." oslo.privsep-1.27.0/oslo_privsep/version.py0000666000175100017510000000126413236474711021070 0ustar zuulzuul00000000000000# Copyright 2016 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version version_info = pbr.version.VersionInfo('oslo.privsep') oslo.privsep-1.27.0/oslo_privsep/tests/0000775000175100017510000000000013236475136020170 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/tests/__init__.py0000666000175100017510000000000013236474711022267 0ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo_privsep/tests/test_priv_context.py0000666000175100017510000001443113236474711024330 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import os import pipes import platform import sys import tempfile import mock import testtools from oslo_privsep import daemon from oslo_privsep import priv_context from oslo_privsep.tests import testctx LOG = logging.getLogger(__name__) @testctx.context.entrypoint def priv_getpid(): return os.getpid() @testctx.context.entrypoint def add1(arg): return arg + 1 class CustomError(Exception): def __init__(self, code, msg): super(CustomError, self).__init__(code, msg) self.code = code self.msg = msg def __str__(self): return 'Code %s: %s' % (self.code, self.msg) @testctx.context.entrypoint def fail(custom=False): if custom: raise CustomError(42, 'omg!') else: raise RuntimeError("I can't let you do that Dave") @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class PrivContextTest(testctx.TestContextTestCase): @mock.patch.object(priv_context, 'sys') def test_init_windows(self, mock_sys): mock_sys.platform = 'win32' context = priv_context.PrivContext('test', capabilities=[]) self.assertFalse(context.client_mode) @mock.patch.object(priv_context, 'sys') def test_set_client_mode(self, mock_sys): context = priv_context.PrivContext('test', capabilities=[]) self.assertTrue(context.client_mode) context.set_client_mode(False) self.assertFalse(context.client_mode) # client_mode should remain to False on win32. mock_sys.platform = 'win32' self.assertRaises(RuntimeError, context.set_client_mode, True) def test_helper_command(self): self.privsep_conf.privsep.helper_command = 'foo --bar' _, temp_path = tempfile.mkstemp() cmd = testctx.context.helper_command(temp_path) expected = [ 'foo', '--bar', '--privsep_context', testctx.context.pypath, '--privsep_sock_path', temp_path, ] self.assertEqual(expected, cmd) def test_helper_command_default(self): self.privsep_conf.config_file = ['/bar.conf'] _, temp_path = tempfile.mkstemp() cmd = testctx.context.helper_command(temp_path) expected = [ 'sudo', 'privsep-helper', '--config-file', '/bar.conf', # --config-dir arg should be skipped '--privsep_context', testctx.context.pypath, '--privsep_sock_path', temp_path, ] self.assertEqual(expected, cmd) def test_helper_command_default_dirtoo(self): self.privsep_conf.config_file = ['/bar.conf', '/baz.conf'] self.privsep_conf.config_dir = ['/foo.d'] _, temp_path = tempfile.mkstemp() cmd = testctx.context.helper_command(temp_path) expected = [ 'sudo', 'privsep-helper', '--config-file', '/bar.conf', '--config-file', '/baz.conf', '--config-dir', '/foo.d', '--privsep_context', testctx.context.pypath, '--privsep_sock_path', temp_path, ] self.assertEqual(expected, cmd) def test_init_known_contexts(self): self.assertEqual(testctx.context.helper_command('/sock')[:2], ['sudo', 'privsep-helper']) priv_context.init(root_helper=['sudo', 'rootwrap']) self.assertEqual(testctx.context.helper_command('/sock')[:3], ['sudo', 'rootwrap', 'privsep-helper']) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class SeparationTest(testctx.TestContextTestCase): def test_getpid(self): # Verify that priv_getpid() was executed in another process. priv_pid = priv_getpid() self.assertNotMyPid(priv_pid) def test_client_mode(self): self.assertNotMyPid(priv_getpid()) self.addCleanup(testctx.context.set_client_mode, True) testctx.context.set_client_mode(False) # priv_getpid() should now run locally (and return our pid) self.assertEqual(os.getpid(), priv_getpid()) testctx.context.set_client_mode(True) # priv_getpid() should now run remotely again self.assertNotMyPid(priv_getpid()) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class RootwrapTest(testctx.TestContextTestCase): def setUp(self): super(RootwrapTest, self).setUp() testctx.context.stop() # Generate a command that will run daemon.helper_main without # requiring it to be properly installed. cmd = [ 'env', 'PYTHON_PATH=%s' % os.path.pathsep.join(sys.path), sys.executable, daemon.__file__, ] if LOG.isEnabledFor(logging.DEBUG): cmd.append('--debug') self.privsep_conf.set_override( 'helper_command', ' '.join(map(pipes.quote, cmd)), group=testctx.context.cfg_section) testctx.context.start(method=priv_context.Method.ROOTWRAP) def test_getpid(self): # Verify that priv_getpid() was executed in another process. priv_pid = priv_getpid() self.assertNotMyPid(priv_pid) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class SerializationTest(testctx.TestContextTestCase): def test_basic_functionality(self): self.assertEqual(43, add1(42)) def test_raises_standard(self): self.assertRaisesRegexp( RuntimeError, "I can't let you do that Dave", fail) def test_raises_custom(self): exc = self.assertRaises(CustomError, fail, custom=True) self.assertEqual(exc.code, 42) self.assertEqual(exc.msg, 'omg!') oslo.privsep-1.27.0/oslo_privsep/tests/test_daemon.py0000666000175100017510000001337713236474711023057 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import fixtures import functools import logging as pylogging import mock import platform import time from oslo_log import formatters from oslo_log import log as logging from oslotest import base import testtools from oslo_privsep import capabilities from oslo_privsep import daemon from oslo_privsep.tests import testctx LOG = logging.getLogger(__name__) def undecorated(): pass class TestException(Exception): pass @testctx.context.entrypoint def logme(level, msg, exc_info=False): # We want to make sure we log everything from the priv side for # the purposes of this test, so force loglevel. LOG.logger.setLevel(logging.DEBUG) if exc_info: try: raise TestException('with arg') except TestException: LOG.log(level, msg, exc_info=True) else: LOG.log(level, msg) class LogRecorder(pylogging.Formatter): def __init__(self, logs, *args, **kwargs): super(LogRecorder, self).__init__(*args, **kwargs) self.logs = logs def format(self, record): self.logs.append(copy.deepcopy(record)) return super(LogRecorder, self).format(record) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class LogTest(testctx.TestContextTestCase): def setUp(self): super(LogTest, self).setUp() def test_priv_loglevel(self): logger = self.useFixture(fixtures.FakeLogger( level=logging.INFO)) # These write to the log on the priv side logme(logging.DEBUG, u'test@DEBUG') logme(logging.WARN, u'test@WARN') time.sleep(0.1) # Hack to give logging thread a chance to run # logger.output is the resulting log on the unpriv side. # This should have been filtered based on (unpriv) loglevel. self.assertNotIn(u'test@DEBUG', logger.output) self.assertIn(u'test@WARN', logger.output) def test_record_data(self): logs = [] self.useFixture(fixtures.FakeLogger( level=logging.INFO, format='dummy', # fixtures.FakeLogger accepts only a formatter # class/function, not an instance :( formatter=functools.partial(LogRecorder, logs))) logme(logging.WARN, u'test with exc', exc_info=True) time.sleep(0.1) # Hack to give logging thread a chance to run self.assertEqual(1, len(logs)) record = logs[0] self.assertIn(u'test with exc', record.getMessage()) self.assertIsNone(record.exc_info) self.assertIn(u'TestException: with arg', record.exc_text) self.assertEqual('PrivContext(cfg_section=privsep)', record.processName) self.assertIn(u'test_daemon.py', record.exc_text) self.assertEqual(logging.WARN, record.levelno) self.assertEqual('logme', record.funcName) def test_format_record(self): logs = [] self.useFixture(fixtures.FakeLogger( level=logging.INFO, format='dummy', # fixtures.FakeLogger accepts only a formatter # class/function, not an instance :( formatter=functools.partial(LogRecorder, logs))) logme(logging.WARN, u'test with exc', exc_info=True) time.sleep(0.1) # Hack to give logging thread a chance to run self.assertEqual(1, len(logs)) record = logs[0] # Verify the log record can be formatted by ContextFormatter fake_config = mock.Mock( logging_default_format_string="NOCTXT: %(message)s") formatter = formatters.ContextFormatter(config=fake_config) formatter.format(record) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class DaemonTest(base.BaseTestCase): @mock.patch('os.setuid') @mock.patch('os.setgid') @mock.patch('os.setgroups') @mock.patch('oslo_privsep.capabilities.set_keepcaps') @mock.patch('oslo_privsep.capabilities.drop_all_caps_except') def test_drop_privs(self, mock_dropcaps, mock_keepcaps, mock_setgroups, mock_setgid, mock_setuid): channel = mock.NonCallableMock() context = mock.NonCallableMock() context.conf.user = 42 context.conf.group = 84 context.conf.capabilities = [ capabilities.CAP_SYS_ADMIN, capabilities.CAP_NET_ADMIN] d = daemon.Daemon(channel, context) d._drop_privs() mock_setuid.assert_called_once_with(42) mock_setgid.assert_called_once_with(84) mock_setgroups.assert_called_once_with([]) self.assertItemsEqual( [mock.call(True), mock.call(False)], mock_keepcaps.mock_calls) mock_dropcaps.assert_called_once_with( set((capabilities.CAP_SYS_ADMIN, capabilities.CAP_NET_ADMIN)), set((capabilities.CAP_SYS_ADMIN, capabilities.CAP_NET_ADMIN)), []) @testtools.skipIf(platform.system() != 'Linux', 'works only on Linux platform.') class WithContextTest(testctx.TestContextTestCase): def test_unexported(self): self.assertRaisesRegexp( NameError, 'undecorated not exported', testctx.context._wrap, undecorated) oslo.privsep-1.27.0/oslo_privsep/tests/testctx.py0000666000175100017510000000275413236474711022250 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from oslotest import base from oslo_privsep import priv_context import oslo_privsep.tests from oslo_privsep.tests import fixture context = priv_context.PrivContext( # This context allows entrypoints anywhere below oslo_privsep.tests. oslo_privsep.tests.__name__, pypath=__name__ + '.context', # This is one of the rare cases where we actually want zero powers: capabilities=[], ) class TestContextTestCase(base.BaseTestCase): def setUp(self): super(TestContextTestCase, self).setUp() privsep_fixture = self.useFixture( fixture.UnprivilegedPrivsepFixture(context)) self.privsep_conf = privsep_fixture.conf def assertNotMyPid(self, pid): # Verify that `pid` is some positive integer, that isn't our pid self.assertIsInstance(pid, int) self.assertTrue(pid > 0) self.assertNotEqual(os.getpid(), pid) oslo.privsep-1.27.0/oslo_privsep/tests/fixture.py0000666000175100017510000000342413236474711022233 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import fixtures import logging import os import sys from oslo_config import fixture as cfg_fixture from oslo_privsep import priv_context LOG = logging.getLogger(__name__) class UnprivilegedPrivsepFixture(fixtures.Fixture): def __init__(self, context): self.context = context def setUp(self): super(UnprivilegedPrivsepFixture, self).setUp() self.conf = self.useFixture(cfg_fixture.Config()).conf self.conf.set_override('capabilities', [], group=self.context.cfg_section) for k in ('user', 'group'): self.conf.set_override( k, None, group=self.context.cfg_section) orig_pid = os.getpid() try: self.context.start(method=priv_context.Method.FORK) except Exception as e: # py3 unittest/testtools/something catches fatal # exceptions from child processes and tries to treat them # like regular non-fatal test failures. Here we attempt # to undo that. if os.getpid() == orig_pid: raise LOG.exception(e) sys.exit(1) self.addCleanup(self.context.stop) oslo.privsep-1.27.0/oslo_privsep/tests/test_capabilities.py0000666000175100017510000000650713236474711024242 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslotest import base from oslo_privsep import capabilities class TestCapabilities(base.BaseTestCase): @mock.patch('oslo_privsep.capabilities._prctl') def test_set_keepcaps_error(self, mock_prctl): mock_prctl.return_value = -1 self.assertRaises(OSError, capabilities.set_keepcaps, True) @mock.patch('oslo_privsep.capabilities._prctl') def test_set_keepcaps(self, mock_prctl): mock_prctl.return_value = 0 capabilities.set_keepcaps(True) # Disappointingly, ffi.cast(type, 1) != ffi.cast(type, 1) # so can't just use assert_called_once_with :-( self.assertEqual(1, mock_prctl.call_count) self.assertItemsEqual( [8, 1], # [PR_SET_KEEPCAPS, true] [int(x) for x in mock_prctl.call_args[0]]) @mock.patch('oslo_privsep.capabilities._capset') def test_drop_all_caps_except_error(self, mock_capset): mock_capset.return_value = -1 self.assertRaises( OSError, capabilities.drop_all_caps_except, [0], [0], [0]) @mock.patch('oslo_privsep.capabilities._capset') def test_drop_all_caps_except(self, mock_capset): mock_capset.return_value = 0 # Somewhat arbitrary bit patterns to exercise _caps_to_mask capabilities.drop_all_caps_except( (17, 24, 49), (8, 10, 35, 56), (24, 31, 40)) self.assertEqual(1, mock_capset.call_count) hdr, data = mock_capset.call_args[0] self.assertEqual(0x20071026, # _LINUX_CAPABILITY_VERSION_2 hdr.version) self.assertEqual(0x01020000, data[0].effective) self.assertEqual(0x00020000, data[1].effective) self.assertEqual(0x00000500, data[0].permitted) self.assertEqual(0x01000008, data[1].permitted) self.assertEqual(0x81000000, data[0].inheritable) self.assertEqual(0x00000100, data[1].inheritable) @mock.patch('oslo_privsep.capabilities._capget') def test_get_caps_error(self, mock_capget): mock_capget.return_value = -1 self.assertRaises(OSError, capabilities.get_caps) @mock.patch('oslo_privsep.capabilities._capget') def test_get_caps(self, mock_capget): def impl(hdr, data): # Somewhat arbitrary bit patterns to exercise _mask_to_caps data[0].effective = 0x01020000 data[1].effective = 0x00020000 data[0].permitted = 0x00000500 data[1].permitted = 0x01000008 data[0].inheritable = 0x81000000 data[1].inheritable = 0x00000100 return 0 mock_capget.side_effect = impl self.assertItemsEqual( ([17, 24, 49], [8, 10, 35, 56], [24, 31, 40]), capabilities.get_caps()) oslo.privsep-1.27.0/oslo_privsep/tests/test_comm.py0000666000175100017510000000521213236474711022534 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six from oslotest import base from oslo_privsep import comm class BufSock(object): def __init__(self): self.readpos = 0 self.buf = six.BytesIO() def recv(self, bufsize): if self.buf.closed: return b'' self.buf.seek(self.readpos, 0) data = self.buf.read(bufsize) self.readpos += len(data) return data def sendall(self, data): self.buf.seek(0, 2) self.buf.write(data) def shutdown(self, _flag): self.buf.close() class TestSerialization(base.BaseTestCase): def setUp(self): super(TestSerialization, self).setUp() sock = BufSock() self.input = comm.Serializer(sock) self.output = iter(comm.Deserializer(sock)) def send(self, data): self.input.send(data) return next(self.output) def assertSendable(self, value): self.assertEqual(value, self.send(value)) def test_none(self): self.assertSendable(None) def test_bool(self): self.assertSendable(True) self.assertSendable(False) def test_int(self): self.assertSendable(42) self.assertSendable(-84) def test_bytes(self): data = b'\x00\x01\x02\xfd\xfe\xff' self.assertSendable(data) def test_unicode(self): data = u'\u4e09\u9df9' self.assertSendable(data) def test_tuple(self): self.assertSendable((1, 'foo')) def test_list(self): # NB! currently lists get converted to tuples by serialization. self.assertEqual((1, 'foo'), self.send([1, 'foo'])) def test_dict(self): self.assertSendable( { 'a': 'b', 1: 2, None: None, (1, 2): (3, 4), } ) def test_badobj(self): class UnknownClass(object): pass obj = UnknownClass() self.assertRaises(TypeError, self.send, obj) def test_eof(self): self.input.close() self.assertRaises(StopIteration, next, self.output) oslo.privsep-1.27.0/oslo_privsep/daemon.py0000666000175100017510000004041213236474711020644 0ustar zuulzuul00000000000000# Copyright 2015 Rackspace Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. '''Privilege separation ("privsep") daemon. To ease transition this supports 2 alternative methods of starting the daemon, all resulting in a helper process running with elevated privileges and open socket(s) to the original process: 1. Start via fork() Assumes process currently has all required privileges and is about to drop them (perhaps by setuid to an unprivileged user). If the the initial environment is secure and `PrivContext.start(Method.FORK)` is called early in `main()`, then this is the most secure and simplest. In particular, if the initial process is already running as non-root (but with sufficient capabilities, via eg suitable systemd service files), then no part needs to involve uid=0 or sudo. 2. Start via sudo/rootwrap This starts the privsep helper on first use via sudo and rootwrap, and communicates via a temporary Unix socket passed on the command line. The communication channel is briefly exposed in the filesystem, but is protected with file permissions and connecting to it only grants access to the unprivileged process. Requires a suitable entry in sudoers or rootwrap.conf filters. The privsep daemon exits when the communication channel is closed, (which usually occurs when the unprivileged process exits). ''' import enum import errno import io import logging as pylogging import os import platform import socket import subprocess import sys import tempfile import threading if platform.system() == 'Linux': import fcntl import grp import pwd import eventlet from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from oslo_privsep._i18n import _ from oslo_privsep import capabilities from oslo_privsep import comm LOG = logging.getLogger(__name__) @enum.unique class StdioFd(enum.IntEnum): # NOTE(gus): We can't use sys.std*.fileno() here. sys.std* # objects may be random file-like objects that may not match the # true system std* fds - and indeed may not even have a file # descriptor at all (eg: test fixtures that monkey patch # fixtures.StringStream onto sys.stdout). Below we always want # the _real_ well-known 0,1,2 Unix fds during os.dup2 # manipulation. STDIN = 0 STDOUT = 1 STDERR = 2 @enum.unique class Message(enum.IntEnum): """Types of messages sent across the communication channel""" PING = 1 PONG = 2 CALL = 3 RET = 4 ERR = 5 LOG = 6 class FailedToDropPrivileges(Exception): pass class ProtocolError(Exception): pass def set_cloexec(fd): flags = fcntl.fcntl(fd, fcntl.F_GETFD) if (flags & fcntl.FD_CLOEXEC) == 0: flags |= fcntl.FD_CLOEXEC fcntl.fcntl(fd, fcntl.F_SETFD, flags) def setuid(user_id_or_name): try: new_uid = int(user_id_or_name) except (TypeError, ValueError): new_uid = pwd.getpwnam(user_id_or_name).pw_uid if new_uid != 0: try: os.setuid(new_uid) except OSError: msg = _('Failed to set uid %s') % new_uid LOG.critical(msg) raise FailedToDropPrivileges(msg) def setgid(group_id_or_name): try: new_gid = int(group_id_or_name) except (TypeError, ValueError): new_gid = grp.getgrnam(group_id_or_name).gr_gid if new_gid != 0: try: os.setgid(new_gid) except OSError: msg = _('Failed to set gid %s') % new_gid LOG.critical(msg) raise FailedToDropPrivileges(msg) class PrivsepLogHandler(pylogging.Handler): def __init__(self, channel, processName=None): super(PrivsepLogHandler, self).__init__() self.channel = channel self.processName = processName def emit(self, record): # Vaguely based on pylogging.handlers.SocketHandler.makePickle if self.processName: record.processName = self.processName data = dict(record.__dict__) if record.exc_info: if not record.exc_text: fmt = self.formatter or pylogging.Formatter() data['exc_text'] = fmt.formatException(record.exc_info) data['exc_info'] = None # drop traceback in favor of exc_text # serialise msg now so we can drop (potentially unserialisable) args data['msg'] = record.getMessage() data['args'] = () self.channel.send((None, (Message.LOG, data))) class _ClientChannel(comm.ClientChannel): """Our protocol, layered on the basic primitives in comm.ClientChannel""" def __init__(self, sock): super(_ClientChannel, self).__init__(sock) self.exchange_ping() def exchange_ping(self): try: # exchange "ready" messages reply = self.send_recv((Message.PING.value,)) success = reply[0] == Message.PONG except Exception as e: LOG.exception('Error while sending initial PING to privsep: %s', e) success = False if not success: msg = _('Privsep daemon failed to start') LOG.critical(msg) raise FailedToDropPrivileges(msg) def remote_call(self, name, args, kwargs): result = self.send_recv((Message.CALL.value, name, args, kwargs)) if result[0] == Message.RET: # (RET, return value) return result[1] elif result[0] == Message.ERR: # (ERR, exc_type, args) # # TODO(gus): see what can be done to preserve traceback # (without leaking local values) exc_type = importutils.import_class(result[1]) raise exc_type(*result[2]) else: raise ProtocolError(_('Unexpected response: %r') % result) def out_of_band(self, msg): if msg[0] == Message.LOG: # (LOG, LogRecord __dict__) record = pylogging.makeLogRecord(msg[1]) if LOG.isEnabledFor(record.levelno): LOG.logger.handle(record) else: LOG.warning('Ignoring unexpected OOB message from privileged ' 'process: %r', msg) def fdopen(fd, *args, **kwargs): # NOTE(gus): We can't just use os.fdopen() here and allow the # regular (optional) monkey_patching to do its thing. Turns out # that regular file objects (as returned by os.fdopen) on python2 # are broken in lots of ways regarding blocking behaviour. We # *need* the newer io.* objects on py2 (doesn't matter on py3, # since the old file code has been replaced with io.*) if eventlet.patcher.is_monkey_patched('socket'): return eventlet.greenio.GreenPipe(fd, *args, **kwargs) else: return io.open(fd, *args, **kwargs) def _fd_logger(level=logging.WARN): """Helper that returns a file object that is asynchronously logged""" read_fd, write_fd = os.pipe() read_end = fdopen(read_fd, 'r', 1) write_end = fdopen(write_fd, 'w', 1) def logger(f): for line in f: LOG.log(level, 'privsep log: %s', line.rstrip()) t = threading.Thread( name='fd_logger', target=logger, args=(read_end,) ) t.daemon = True t.start() return write_end def replace_logging(handler, log_root=None): if log_root is None: log_root = logging.getLogger(None).logger # root logger for h in log_root.handlers: log_root.removeHandler(h) log_root.addHandler(handler) class ForkingClientChannel(_ClientChannel): def __init__(self, context): """Start privsep daemon using fork() Assumes we already have required privileges. """ sock_a, sock_b = socket.socketpair() for s in (sock_a, sock_b): s.setblocking(True) # Important that these sockets don't get leaked set_cloexec(s) # Try to prevent any buffered output from being written by both # parent and child. for f in (sys.stdout, sys.stderr): f.flush() if os.fork() == 0: # child channel = comm.ServerChannel(sock_b) sock_a.close() # Replace root logger early (to capture any errors during setup) replace_logging(PrivsepLogHandler(channel, processName=str(context))) Daemon(channel, context=context).run() LOG.debug('privsep daemon exiting') os._exit(0) # parent sock_b.close() super(ForkingClientChannel, self).__init__(sock_a) class RootwrapClientChannel(_ClientChannel): def __init__(self, context): """Start privsep daemon using exec() Uses sudo/rootwrap to gain privileges. """ listen_sock = socket.socket(socket.AF_UNIX) # Note we listen() on the unprivileged side, and connect to it # from the privileged process. This means there is no exposed # attack point on the privileged side. # NB: Permissions on sockets are not checked on some (BSD) Unices # so create socket in a private directory for safety. Privsep # daemon will (initially) be running as root, so will still be # able to connect to sock path. tmpdir = tempfile.mkdtemp() # NB: created with 0700 perms try: sockpath = os.path.join(tmpdir, 'privsep.sock') listen_sock.bind(sockpath) listen_sock.listen(1) cmd = context.helper_command(sockpath) LOG.info('Running privsep helper: %s', cmd) proc = subprocess.Popen(cmd, shell=False, stderr=_fd_logger()) if proc.wait() != 0: msg = ('privsep helper command exited non-zero (%s)' % proc.returncode) LOG.critical(msg) raise FailedToDropPrivileges(msg) LOG.info('Spawned new privsep daemon via rootwrap') sock, _addr = listen_sock.accept() LOG.debug('Accepted privsep connection to %s', sockpath) finally: # Don't need listen_sock anymore, so clean up. listen_sock.close() try: os.unlink(sockpath) except OSError as e: if e.errno != errno.ENOENT: raise os.rmdir(tmpdir) super(RootwrapClientChannel, self).__init__(sock) class Daemon(object): """NB: This doesn't fork() - do that yourself before calling run()""" def __init__(self, channel, context): self.channel = channel self.context = context self.user = context.conf.user self.group = context.conf.group self.caps = set(context.conf.capabilities) def run(self): """Run request loop. Sets up environment, then calls loop()""" os.chdir("/") os.umask(0) self._drop_privs() self._close_stdio() self.loop() def _close_stdio(self): with open(os.devnull, 'w+') as devnull: os.dup2(devnull.fileno(), StdioFd.STDIN) os.dup2(devnull.fileno(), StdioFd.STDOUT) # stderr is left untouched def _drop_privs(self): try: # Keep current capabilities across setuid away from root. capabilities.set_keepcaps(True) if self.group is not None: try: os.setgroups([]) except OSError: msg = _('Failed to remove supplemental groups') LOG.critical(msg) raise FailedToDropPrivileges(msg) if self.user is not None: setuid(self.user) if self.group is not None: setgid(self.group) finally: capabilities.set_keepcaps(False) LOG.info('privsep process running with uid/gid: %(uid)s/%(gid)s', {'uid': os.getuid(), 'gid': os.getgid()}) capabilities.drop_all_caps_except(self.caps, self.caps, []) def fmt_caps(capset): if not capset: return 'none' fc = [capabilities.CAPS_BYVALUE.get(c, str(c)) for c in capset] fc.sort() return '|'.join(fc) eff, prm, inh = capabilities.get_caps() LOG.info( 'privsep process running with capabilities ' '(eff/prm/inh): %(eff)s/%(prm)s/%(inh)s', { 'eff': fmt_caps(eff), 'prm': fmt_caps(prm), 'inh': fmt_caps(inh), }) def _process_cmd(self, cmd, *args): if cmd == Message.PING: return (Message.PONG.value,) elif cmd == Message.CALL: name, f_args, f_kwargs = args func = importutils.import_class(name) if not self.context.is_entrypoint(func): msg = _('Invalid privsep function: %s not exported') % name raise NameError(msg) ret = func(*f_args, **f_kwargs) return (Message.RET.value, ret) raise ProtocolError(_('Unknown privsep cmd: %s') % cmd) def loop(self): """Main body of daemon request loop""" LOG.info('privsep daemon running as pid %s', os.getpid()) # We *are* this context now - any calls through it should be # executed locally. self.context.set_client_mode(False) for msgid, msg in self.channel: LOG.debug('privsep: request[%(msgid)s]: %(req)s', {'msgid': msgid, 'req': msg}) try: reply = self._process_cmd(*msg) except Exception as e: LOG.debug( 'privsep: Exception during request[%(msgid)s]: %(err)s', {'msgid': msgid, 'err': e}, exc_info=True) cls = e.__class__ cls_name = '%s.%s' % (cls.__module__, cls.__name__) reply = (Message.ERR.value, cls_name, e.args) try: LOG.debug('privsep: reply[%(msgid)s]: %(reply)s', {'msgid': msgid, 'reply': reply}) self.channel.send((msgid, reply)) except IOError as e: if e.errno == errno.EPIPE: # Write stream closed, exit loop break raise LOG.debug('Socket closed, shutting down privsep daemon') def helper_main(): """Start privileged process, serving requests over a Unix socket.""" cfg.CONF.register_cli_opts([ cfg.StrOpt('privsep_context', required=True), cfg.StrOpt('privsep_sock_path', required=True), ]) logging.register_options(cfg.CONF) cfg.CONF(args=sys.argv[1:], project='privsep') logging.setup(cfg.CONF, 'privsep') # note replace_logging call below context = importutils.import_class(cfg.CONF.privsep_context) from oslo_privsep import priv_context # Avoid circular import if not isinstance(context, priv_context.PrivContext): LOG.fatal('--privsep_context must be the (python) name of a ' 'PrivContext object') sock = socket.socket(socket.AF_UNIX) sock.connect(cfg.CONF.privsep_sock_path) set_cloexec(sock) channel = comm.ServerChannel(sock) # Channel is set up, so fork off daemon "in the background" and exit if os.fork() != 0: # parent return # child # Note we don't move into a new process group/session like a # regular daemon might, since we _want_ to remain associated with # the originating (unprivileged) process. # Channel is set up now, so move to in-band logging replace_logging(PrivsepLogHandler(channel)) LOG.info('privsep daemon starting') try: Daemon(channel, context).run() except Exception as e: LOG.exception(e) sys.exit(str(e)) LOG.debug('privsep daemon exiting') sys.exit(0) if __name__ == '__main__': helper_main() oslo.privsep-1.27.0/setup.cfg0000666000175100017510000000264013236475136016127 0ustar zuulzuul00000000000000[metadata] name = oslo.privsep summary = OpenStack library for privilege separation description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/oslo.privsep/latest/ classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = oslo_privsep [pbr] autodoc_index_modules = True api_doc_dir = reference/api autodoc_exclude_modules = oslo_privsep.tests.* oslo_privsep._* [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 warning-is-error = 1 [entry_points] console_scripts = privsep-helper = oslo_privsep.daemon:helper_main [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = oslo.privsep/locale domain = oslo_privsep [update_catalog] domain = oslo_privsep output_dir = oslo_privsep/locale input_file = oslo_privsep/locale/oslo_privsep.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = oslo_privsep/locale/oslo_privsep.pot [wheel] universal = true [egg_info] tag_build = tag_date = 0 oslo.privsep-1.27.0/doc/0000775000175100017510000000000013236475136015047 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/0000775000175100017510000000000013236475136016347 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/index.rst0000666000175100017510000000045613236474711020215 0ustar zuulzuul00000000000000============== oslo.privsep ============== OpenStack library for privilege separation Contents ======== .. toctree:: :maxdepth: 2 install/index user/index contributor/index reference/index Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` oslo.privsep-1.27.0/doc/source/reference/0000775000175100017510000000000013236475136020305 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/reference/index.rst0000666000175100017510000000010113236474711022136 0ustar zuulzuul00000000000000===== API ===== .. toctree:: :maxdepth: 2 api/autoindex oslo.privsep-1.27.0/doc/source/user/0000775000175100017510000000000013236475136017325 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/user/index.rst0000666000175100017510000000012413236474711021163 0ustar zuulzuul00000000000000======= Usage ======= To use oslo.privsep in a project:: import oslo_privsep oslo.privsep-1.27.0/doc/source/conf.py0000777000175100017510000000510513236474711017652 0ustar zuulzuul00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'openstackdocstheme' ] # openstackdocstheme options repository_name = 'openstack/oslo.privsep' bug_project = 'oslo.privsep' bug_tag = '' html_last_updated_fmt = '%Y-%m-%d %H:%M' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'oslo.privsep' copyright = u'2014, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] html_theme = 'openstackdocs' # html_static_path = ['static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} oslo.privsep-1.27.0/doc/source/install/0000775000175100017510000000000013236475136020015 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/install/index.rst0000666000175100017510000000014313236474711021654 0ustar zuulzuul00000000000000============== Installation ============== At the command line:: $ pip install oslo.privsep oslo.privsep-1.27.0/doc/source/contributor/0000775000175100017510000000000013236475136020721 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/doc/source/contributor/index.rst0000666000175100017510000000016513236474711022564 0ustar zuulzuul00000000000000=================== Contributor Guide =================== .. toctree:: :maxdepth: 2 contributing history oslo.privsep-1.27.0/doc/source/contributor/contributing.rst0000666000175100017510000000012413236474711024157 0ustar zuulzuul00000000000000============== Contributing ============== .. include:: ../../../CONTRIBUTING.rst oslo.privsep-1.27.0/doc/source/contributor/history.rst0000666000175100017510000000004013236474711023146 0ustar zuulzuul00000000000000.. include:: ../../../ChangeLog oslo.privsep-1.27.0/requirements.txt0000666000175100017510000000104213236474726017571 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. oslo.log>=3.30.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.config>=5.1.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 enum34>=1.0.4;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD cffi>=1.7.0 # MIT eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 # MIT greenlet>=0.4.10 # MIT msgpack>=0.4.0 # Apache-2.0 oslo.privsep-1.27.0/HACKING.rst0000666000175100017510000000025113236474711016076 0ustar zuulzuul00000000000000oslo.privsep Style Commandments ====================================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ oslo.privsep-1.27.0/PKG-INFO0000664000175100017510000000473113236475136015404 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: oslo.privsep Version: 1.27.0 Summary: OpenStack library for privilege separation Home-page: https://docs.openstack.org/oslo.privsep/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/oslo.privsep.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============ oslo.privsep ============ .. image:: https://img.shields.io/pypi/v/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Downloads OpenStack library for privilege separation This library helps applications perform actions which require more or less privileges than they were started with in a safe, easy to code and easy to use manner. For more information on why this is generally a good idea please read over the `principle of least privilege`_ and the `specification`_ which created this library. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.privsep/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.privsep * Bugs: https://bugs.launchpad.net/oslo.privsep .. _principle of least privilege: https://en.wikipedia.org/wiki/\ Principle_of_least_privilege .. _specification: https://specs.openstack.org/openstack/\ oslo-specs/specs/liberty/privsep.html Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 oslo.privsep-1.27.0/AUTHORS0000664000175100017510000000216213236475135015352 0ustar zuulzuul00000000000000Alexander Tsamutali Andreas Jaeger Angus Lees Bogdan Teleaga ChangBo Guo(gcb) Claudiu Belu Davanum Srinivas Dirk Mueller Doug Hellmann Eric Brown Flavio Percoco Hongbin Lu John Garbutt Joshua Harlow Joshua Harlow Kirill Bespalov OpenStack Release Bot Swapnil Kulkarni (coolsvap) TommyLike Tony Breeds Vu Cong Tuan Walter A. Boring IV Zhihai Song Zuul avnish liangcui loooosy ricolin sonu.kumar xgwang5843 oslo.privsep-1.27.0/.testr.conf0000666000175100017510000000051413236474711016370 0ustar zuulzuul00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ ./oslo_privsep $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list oslo.privsep-1.27.0/tox.ini0000666000175100017510000000232113236474711015613 0ustar zuulzuul00000000000000[tox] minversion = 2.0 envlist = py35,py27,pypy,pep8 [testenv] install_command = pip install {opts} {packages} whitelist_externals = /bin/sh deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:pep8] deps = -r{toxinidir}/test-requirements.txt commands = flake8 # Run security linter bandit -r oslo_privsep tests -n5 --skip B404,B603 [testenv:venv] commands = {posargs} [testenv:docs] commands = python setup.py build_sphinx [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [flake8] # E123, E125 skipped as they are invalid PEP-8. # [H106] Don’t put vim configuration in source files # [H203] Use assertIs(Not)None to check for None show-source = True ignore = E123,E125 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build enable-extensions = H106,H203 [hacking] import_exceptions = oslo_privsep._i18n [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html oslo.privsep-1.27.0/oslo.privsep.egg-info/0000775000175100017510000000000013236475136020437 5ustar zuulzuul00000000000000oslo.privsep-1.27.0/oslo.privsep.egg-info/dependency_links.txt0000664000175100017510000000000113236475135024504 0ustar zuulzuul00000000000000 oslo.privsep-1.27.0/oslo.privsep.egg-info/pbr.json0000664000175100017510000000005613236475135022115 0ustar zuulzuul00000000000000{"git_version": "2221752", "is_release": true}oslo.privsep-1.27.0/oslo.privsep.egg-info/PKG-INFO0000664000175100017510000000473113236475135021540 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: oslo.privsep Version: 1.27.0 Summary: OpenStack library for privilege separation Home-page: https://docs.openstack.org/oslo.privsep/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/oslo.privsep.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============ oslo.privsep ============ .. image:: https://img.shields.io/pypi/v/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Downloads OpenStack library for privilege separation This library helps applications perform actions which require more or less privileges than they were started with in a safe, easy to code and easy to use manner. For more information on why this is generally a good idea please read over the `principle of least privilege`_ and the `specification`_ which created this library. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.privsep/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.privsep * Bugs: https://bugs.launchpad.net/oslo.privsep .. _principle of least privilege: https://en.wikipedia.org/wiki/\ Principle_of_least_privilege .. _specification: https://specs.openstack.org/openstack/\ oslo-specs/specs/liberty/privsep.html Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 oslo.privsep-1.27.0/oslo.privsep.egg-info/SOURCES.txt0000664000175100017510000000315413236475136022326 0ustar zuulzuul00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/source/conf.py doc/source/index.rst doc/source/contributor/contributing.rst doc/source/contributor/history.rst doc/source/contributor/index.rst doc/source/install/index.rst doc/source/reference/index.rst doc/source/user/index.rst oslo.privsep.egg-info/PKG-INFO oslo.privsep.egg-info/SOURCES.txt oslo.privsep.egg-info/dependency_links.txt oslo.privsep.egg-info/entry_points.txt oslo.privsep.egg-info/not-zip-safe oslo.privsep.egg-info/pbr.json oslo.privsep.egg-info/requires.txt oslo.privsep.egg-info/top_level.txt oslo_privsep/__init__.py oslo_privsep/_i18n.py oslo_privsep/capabilities.py oslo_privsep/comm.py oslo_privsep/daemon.py oslo_privsep/priv_context.py oslo_privsep/version.py oslo_privsep/locale/de/LC_MESSAGES/oslo_privsep.po oslo_privsep/locale/en_GB/LC_MESSAGES/oslo_privsep.po oslo_privsep/tests/__init__.py oslo_privsep/tests/fixture.py oslo_privsep/tests/test_capabilities.py oslo_privsep/tests/test_comm.py oslo_privsep/tests/test_daemon.py oslo_privsep/tests/test_priv_context.py oslo_privsep/tests/testctx.py releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.pooslo.privsep-1.27.0/oslo.privsep.egg-info/top_level.txt0000664000175100017510000000001513236475135023164 0ustar zuulzuul00000000000000oslo_privsep oslo.privsep-1.27.0/oslo.privsep.egg-info/entry_points.txt0000664000175100017510000000010413236475135023727 0ustar zuulzuul00000000000000[console_scripts] privsep-helper = oslo_privsep.daemon:helper_main oslo.privsep-1.27.0/oslo.privsep.egg-info/requires.txt0000664000175100017510000000025613236475135023041 0ustar zuulzuul00000000000000oslo.log>=3.30.0 oslo.i18n>=3.15.3 oslo.config>=5.1.0 oslo.utils>=3.33.0 enum34>=1.0.4 cffi>=1.7.0 eventlet!=0.18.3,!=0.20.1,<0.21.0,>=0.18.2 greenlet>=0.4.10 msgpack>=0.4.0 oslo.privsep-1.27.0/oslo.privsep.egg-info/not-zip-safe0000664000175100017510000000000113236475074022666 0ustar zuulzuul00000000000000 oslo.privsep-1.27.0/CONTRIBUTING.rst0000666000175100017510000000122013236474711016736 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html If you already have a good understanding of how the system works and your OpenStack accounts are set up, you can skip to the development workflow section of this documentation to learn how changes to OpenStack should be submitted for review via the Gerrit tool: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/oslo.privsep oslo.privsep-1.27.0/.mailmap0000666000175100017510000000013113236474711015716 0ustar zuulzuul00000000000000# Format is: # # oslo.privsep-1.27.0/setup.py0000666000175100017510000000200613236474711016012 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) oslo.privsep-1.27.0/README.rst0000666000175100017510000000256213236474711015776 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/oslo.privsep.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on ============ oslo.privsep ============ .. image:: https://img.shields.io/pypi/v/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/oslo.privsep.svg :target: https://pypi.python.org/pypi/oslo.privsep/ :alt: Downloads OpenStack library for privilege separation This library helps applications perform actions which require more or less privileges than they were started with in a safe, easy to code and easy to use manner. For more information on why this is generally a good idea please read over the `principle of least privilege`_ and the `specification`_ which created this library. * Free software: Apache license * Documentation: https://docs.openstack.org/oslo.privsep/latest/ * Source: https://git.openstack.org/cgit/openstack/oslo.privsep * Bugs: https://bugs.launchpad.net/oslo.privsep .. _principle of least privilege: https://en.wikipedia.org/wiki/\ Principle_of_least_privilege .. _specification: https://specs.openstack.org/openstack/\ oslo-specs/specs/liberty/privsep.html oslo.privsep-1.27.0/.coveragerc0000666000175100017510000000015713236474711016426 0ustar zuulzuul00000000000000[run] branch = True source = privsep omit = privsep/tests/*,privsep/openstack/* [report] ignore-errors = True oslo.privsep-1.27.0/LICENSE0000666000175100017510000002363713236474711015322 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. oslo.privsep-1.27.0/test-requirements.txt0000666000175100017510000000101013236474726020541 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 mock>=2.0.0 # BSD fixtures>=3.0.0 # Apache-2.0/BSD # These are needed for docs generation openstackdocstheme>=1.17.0 # Apache-2.0 sphinx>=1.6.2 # BSD reno>=2.5.0 # Apache-2.0 # Bandit security code scanner bandit>=1.1.0 # Apache-2.0 oslo.privsep-1.27.0/babel.cfg0000666000175100017510000000002113236474711016021 0ustar zuulzuul00000000000000[python: **.py]