├── .gitignore ├── MANIFEST.in ├── HACKING ├── .github └── workflows │ ├── gh-pages.yml │ └── python-publish.yml ├── securebits.h ├── README ├── COPYING ├── setup.py ├── docs ├── Makefile ├── conf.py └── index.rst ├── prctl.py ├── test_prctl.py └── _prctlmodule.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | MANIFEST 4 | docs/_build 5 | *.pyc 6 | __pycache__ 7 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include securebits.h 2 | include test_prctl.py 3 | include docs/* 4 | include MANIFEST.in 5 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | How to roll a release: 2 | 3 | Source preparation: 4 | - Bump version number in setup.py 5 | - Bump version number in debian/changelog 6 | - Tag the release 7 | - Push to github 8 | 9 | Packaging: 10 | - ./setup.py sdist 11 | - dpkg-buildpackage -rfakeroot 12 | - Upload tarballs and packages to kaarsemaker.net 13 | 14 | pypi: 15 | - dh clean 16 | - cd docs/_build/html 17 | - rm -f docs.zip && zip -r docs.zip * 18 | - Upload docs.zip to pypi 19 | - ./setup.py register 20 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: github pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-18.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Build 15 | uses: ammaraskar/sphinx-action@master 16 | with: 17 | pre-build-command: python -mpip install sphinx-rtd-theme 18 | docs-folder: "docs/" 19 | 20 | - name: Deploy 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./docs/_build/html 25 | force_orphan: true 26 | -------------------------------------------------------------------------------- /securebits.h: -------------------------------------------------------------------------------- 1 | /* Constants as defined in the kernel headers */ 2 | #define SECURE_NOROOT 0 3 | #define SECURE_NOROOT_LOCKED 1 4 | #define SECURE_NO_SETUID_FIXUP 2 5 | #define SECURE_NO_SETUID_FIXUP_LOCKED 3 6 | #define SECURE_KEEP_CAPS 4 7 | #define SECURE_KEEP_CAPS_LOCKED 5 8 | 9 | #define issecure_mask(X) (1 << (X)) 10 | #define SECUREBITS_DEFAULT 0x00000000 11 | #define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT)) 12 | #define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED)) 13 | #define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP)) 14 | #define SECBIT_NO_SETUID_FIXUP_LOCKED \ 15 | (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)) 16 | #define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS)) 17 | #define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED)) 18 | #define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ 19 | issecure_mask(SECURE_NO_SETUID_FIXUP) | \ 20 | issecure_mask(SECURE_KEEP_CAPS)) 21 | #define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) 22 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | sudo apt-get install libcap-dev linux-headers-$(uname -r) 24 | python -m pip install --upgrade pip 25 | pip install setuptools wheel twine 26 | - name: Build and publish 27 | env: 28 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 29 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 30 | run: | 31 | python setup.py sdist 32 | twine upload dist/* 33 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | python-prctl -- Control process attributes through prctl 2 | ======================================================== 3 | 4 | The linux prctl function allows you to control specific characteristics of a 5 | process' behaviour. Usage of the function is fairly messy though, due to 6 | limitations in C and linux. This module provides a nice non-messy python(ic) 7 | interface. 8 | 9 | Besides prctl, this library also wraps libcap for complete capability handling 10 | and allows you to set the process name as seen in ps and top. 11 | 12 | See docs/index.rst for the documentation. An HTML version can be found on 13 | http://packages.python.org/python-prctl/ 14 | 15 | Quick install instructions 16 | ========================== 17 | Before installing python-prctl, you need GCC, libc headers and libcap headers. 18 | On Debian/Ubuntu: 19 | 20 | $ sudo apt-get install build-essential libcap-dev 21 | 22 | On fedora: 23 | 24 | $ sudo yum install gcc glibc-devel libcap-devel 25 | 26 | Stable version: 27 | 28 | $ sudo easy_install python-prctl 29 | 30 | Latest code: 31 | 32 | $ git clone http://github.com/seveas/python-prctl 33 | $ cd python-prctl 34 | $ python setup.py build 35 | $ sudo python setup.py install 36 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | python-prctl - A python(ic) interface to the prctl syscall 2 | Copyright (C) 2010-2020 Dennis Kaarsemaker 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . 16 | 17 | --------------------------------------------------------------------- 18 | 19 | The documentation is heavily based on the linux manpages prctl and 20 | capabilities, which is covered by the following license: 21 | 22 | Permission is granted to make and distribute verbatim copies of this 23 | manual provided the copyright notice and this permission notice are 24 | preserved on all copies. 25 | 26 | Permission is granted to copy and distribute modified versions of this 27 | manual under the conditions for verbatim copying, provided that the 28 | entire resulting derived work is distributed under the terms of a 29 | permission notice identical to this one 30 | 31 | Since the Linux kernel and libraries are constantly changing, this 32 | manual page may be incorrect or out-of-date. The author(s) assume no 33 | responsibility for errors or omissions, or for damages resulting from 34 | the use of the information contained herein. The author(s) may not 35 | have taken the same level of care in the production of this manual, 36 | which is licensed free of charge, as they might when working 37 | professionally. 38 | 39 | Formatted or processed versions of this manual, if unaccompanied by 40 | the source, must acknowledge the copyright and authors of this work. 41 | License. 42 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from setuptools import setup, Extension 4 | import glob 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | # Check our environment 10 | # - Need to be on linux 11 | # - Need kernel 2.6.18+ 12 | # - Need python 2.4+ 13 | # - Need gcc 14 | # - Need C headers 15 | # - Need libcap headers 16 | if not sys.platform.startswith('linux'): 17 | sys.stderr.write("This module only works on linux\n") 18 | sys.exit(1) 19 | 20 | kvers = os.uname()[2] 21 | if kvers < '2.6.18' and not os.environ.get("PRCTL_SKIP_KERNEL_CHECK",False): 22 | sys.stderr.write("This module requires linux 2.6.18 or newer\n") 23 | sys.exit(1) 24 | 25 | if sys.version_info[:2] < (2,4): 26 | sys.stderr.write("This module requires python 2.4 or newer\n") 27 | sys.exit(1) 28 | 29 | exit = False 30 | try: 31 | subprocess.call(['gcc','-v'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 32 | except: 33 | sys.stderr.write("You need to install gcc to build this module\n") 34 | sys.exit(1) 35 | 36 | sp = subprocess.Popen(['cpp'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ) 37 | sp.communicate('#include \n'.encode()) 38 | if sp.returncode: 39 | sys.stderr.write("You need to install libc development headers to build this module\n") 40 | exit = True 41 | 42 | sp = subprocess.Popen(['cpp'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=os.environ) 43 | sp.communicate('#include \n'.encode()) 44 | if sp.returncode: 45 | sys.stderr.write("You need to install libcap development headers to build this module\n") 46 | exit = True 47 | 48 | if exit: 49 | sys.exit(1) 50 | 51 | _prctl = Extension("_prctl", 52 | sources = ['_prctlmodule.c'], 53 | depends = ['securebits.h'], 54 | libraries = ['cap']) 55 | 56 | setup(name = "python-prctl", 57 | version = "1.8.1", 58 | author = "Dennis Kaarsemaker", 59 | author_email = "dennis@kaarsemaker.net", 60 | url = "http://github.com/seveas/python-prctl", 61 | description = "Python(ic) interface to the linux prctl syscall", 62 | py_modules = ["prctl"], 63 | ext_modules = [_prctl], 64 | classifiers = [ 65 | 'Development Status :: 5 - Production/Stable', 66 | 'Intended Audience :: Developers', 67 | 'License :: OSI Approved :: GNU General Public License (GPL)', 68 | 'Operating System :: POSIX :: Linux', 69 | 'Programming Language :: C', 70 | 'Programming Language :: Python', 71 | 'Programming Language :: Python :: 3', 72 | 'Topic :: Security', 73 | ] 74 | ) 75 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " pickle to make pickle files" 22 | @echo " json to make JSON files" 23 | @echo " htmlhelp to make HTML files and a HTML help project" 24 | @echo " qthelp to make HTML files and a qthelp project" 25 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 26 | @echo " changes to make an overview of all changed/added/deprecated items" 27 | @echo " linkcheck to check all external links for integrity" 28 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 29 | 30 | clean: 31 | -rm -rf $(BUILDDIR)/* 32 | 33 | html: 34 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 35 | @echo 36 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 37 | 38 | dirhtml: 39 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 40 | @echo 41 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 42 | 43 | pickle: 44 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 45 | @echo 46 | @echo "Build finished; now you can process the pickle files." 47 | 48 | json: 49 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 50 | @echo 51 | @echo "Build finished; now you can process the JSON files." 52 | 53 | htmlhelp: 54 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 55 | @echo 56 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 57 | ".hhp project file in $(BUILDDIR)/htmlhelp." 58 | 59 | qthelp: 60 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 61 | @echo 62 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 63 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 64 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/python-prctl.qhcp" 65 | @echo "To view the help file:" 66 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/python-prctl.qhc" 67 | 68 | latex: 69 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 70 | @echo 71 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 72 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 73 | "run these through (pdf)latex." 74 | 75 | changes: 76 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 77 | @echo 78 | @echo "The overview file is in $(BUILDDIR)/changes." 79 | 80 | linkcheck: 81 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 82 | @echo 83 | @echo "Link check complete; look for any errors in the above output " \ 84 | "or in $(BUILDDIR)/linkcheck/output.txt." 85 | 86 | doctest: 87 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 88 | @echo "Testing of doctests in the sources finished, look at the " \ 89 | "results in $(BUILDDIR)/doctest/output.txt." 90 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # python-prctl documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Mar 10 23:08:02 2010. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.append(os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # Add any Sphinx extension module names here, as strings. They can be extensions 24 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 25 | extensions = ['sphinx.ext.todo'] 26 | 27 | # Add any paths that contain templates here, relative to this directory. 28 | templates_path = ['_templates'] 29 | 30 | # The suffix of source filenames. 31 | source_suffix = '.rst' 32 | 33 | # The encoding of source files. 34 | #source_encoding = 'utf-8' 35 | 36 | # The master toctree document. 37 | master_doc = 'index' 38 | 39 | # General information about the project. 40 | project = u'python-prctl' 41 | copyright = u'2010-2020, Dennis Kaarsemaker' 42 | 43 | # The version info for the project you're documenting, acts as replacement for 44 | # |version| and |release|, also used in various other places throughout the 45 | # built documents. 46 | # 47 | # The short X.Y version. 48 | version = '1.8' 49 | # The full version, including alpha/beta/rc tags. 50 | release = '1.8.1' 51 | 52 | # The language for content autogenerated by Sphinx. Refer to documentation 53 | # for a list of supported languages. 54 | #language = None 55 | 56 | # There are two options for replacing |today|: either, you set today to some 57 | # non-false value, then it is used: 58 | #today = '' 59 | # Else, today_fmt is used as the format for a strftime call. 60 | #today_fmt = '%B %d, %Y' 61 | 62 | # List of documents that shouldn't be included in the build. 63 | #unused_docs = [] 64 | 65 | # List of directories, relative to source directory, that shouldn't be searched 66 | # for source files. 67 | exclude_trees = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. Major themes that come with 93 | # Sphinx are currently 'default' and 'sphinxdoc'. 94 | html_theme = 'default' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | #html_theme_path = [] 103 | 104 | # The name for this set of Sphinx documents. If None, it defaults to 105 | # " v documentation". 106 | #html_title = None 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | html_static_path = [] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_use_modindex = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, an OpenSearch description file will be output, and all pages will 153 | # contain a tag referring to it. The value of this option must be the 154 | # base URL from which the finished HTML is served. 155 | #html_use_opensearch = '' 156 | 157 | # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). 158 | #html_file_suffix = '' 159 | 160 | # Output file base name for HTML help builder. 161 | htmlhelp_basename = 'python-prctldoc' 162 | 163 | 164 | # -- Options for LaTeX output -------------------------------------------------- 165 | 166 | # The paper size ('letter' or 'a4'). 167 | #latex_paper_size = 'letter' 168 | 169 | # The font size ('10pt', '11pt' or '12pt'). 170 | #latex_font_size = '10pt' 171 | 172 | # Grouping the document tree into LaTeX files. List of tuples 173 | # (source start file, target name, title, author, documentclass [howto/manual]). 174 | latex_documents = [ 175 | ('README', 'python-prctl.tex', u'python-prctl Documentation', 176 | u'Dennis Kaarsemaker', 'manual'), 177 | ] 178 | 179 | # The name of an image file (relative to this directory) to place at the top of 180 | # the title page. 181 | #latex_logo = None 182 | 183 | # For "manual" documents, if this is true, then toplevel headings are parts, 184 | # not chapters. 185 | #latex_use_parts = False 186 | 187 | # Additional stuff for the LaTeX preamble. 188 | #latex_preamble = '' 189 | 190 | # Documents to append as an appendix to all manuals. 191 | #latex_appendices = [] 192 | 193 | # If false, no module index is generated. 194 | #latex_use_modindex = True 195 | -------------------------------------------------------------------------------- /prctl.py: -------------------------------------------------------------------------------- 1 | # python-pctrl -- python interface to the prctl function 2 | # (c)2010-2020 Dennis Kaarsemaker 3 | # See COPYING for licensing details 4 | 5 | import _prctl # The C interface 6 | import sys 7 | 8 | # Python 3.x compatibility 9 | if sys.version_info[0] >= 3: 10 | basestring = str 11 | 12 | # Code generation functions 13 | def prctl_wrapper(option): 14 | def call_prctl(arg=None, arg2=None): 15 | if arg == None: 16 | return _prctl.prctl(option) 17 | if arg2 == None: 18 | return _prctl.prctl(option, arg) 19 | return _prctl.prctl(option, arg, arg2) 20 | return call_prctl 21 | 22 | def capb_wrapper(cap): 23 | def getter(self): 24 | return _prctl.prctl(_prctl.PR_CAPBSET_READ, cap) 25 | def setter(self, value): 26 | if value: 27 | raise ValueError("Can only drop capabilities from the bounding set, not add new ones") 28 | _prctl.prctl(_prctl.PR_CAPBSET_DROP, cap) 29 | return property(getter, setter) 30 | 31 | def cap_wrapper(cap): 32 | def getter(self): 33 | return get_caps((cap, self.flag))[self.flag][cap] 34 | def setter(self, val): 35 | set_caps((cap, self.flag, val)) 36 | return property(getter, setter) 37 | 38 | def sec_wrapper(bit): 39 | def getter(self): 40 | return bool(_prctl.prctl(_prctl.PR_GET_SECUREBITS) & bit) 41 | def setter(self, value): 42 | bits = _prctl.prctl(_prctl.PR_GET_SECUREBITS) 43 | if value: 44 | bits |= bit 45 | else: 46 | bits &= ~(bit) 47 | _prctl.prctl(_prctl.PR_SET_SECUREBITS, bits) 48 | return property(getter, setter) 49 | 50 | # Wrap the capabilities, capability bounding set and securebits in an object 51 | _ALL_FLAG_NAMES = ('CAP_EFFECTIVE', 'CAP_INHERITABLE', 'CAP_PERMITTED') 52 | _ALL_CAP_NAMES = tuple(x for x in dir(_prctl) if x.startswith('CAP_') and x not in _ALL_FLAG_NAMES) 53 | ALL_FLAG_NAMES = tuple(x[4:].lower() for x in _ALL_FLAG_NAMES) 54 | ALL_CAP_NAMES = tuple(x[4:].lower() for x in _ALL_CAP_NAMES) 55 | ALL_FLAGS = tuple(getattr(_prctl,x) for x in _ALL_FLAG_NAMES) 56 | ALL_CAPS = tuple(getattr(_prctl,x) for x in _ALL_CAP_NAMES) 57 | 58 | for i in range(_prctl.CAP_LAST_CAP+1): 59 | if i not in ALL_CAPS: 60 | _ALL_CAP_NAMES += ("CAP_UNKNOWN_%d" % i,) 61 | del i 62 | 63 | if len(_ALL_CAP_NAMES) != len(ALL_CAPS): 64 | warnings.warn("not all known capabilities are named, this is a bug in python-prctl", RuntimeWarning) 65 | ALL_CAP_NAMES = tuple(x[4:].lower() for x in _ALL_CAP_NAMES) 66 | ALL_CAPS = tuple(getattr(_prctl,x) for x in _ALL_CAP_NAMES) 67 | 68 | class Capbset(object): 69 | __slots__ = ALL_CAP_NAMES 70 | def __init__(self): 71 | for name in _ALL_CAP_NAMES: 72 | friendly_name = name[4:].lower() 73 | setattr(self.__class__, friendly_name, capb_wrapper(getattr(_prctl, name))) 74 | 75 | def drop(self, *caps): 76 | for cap in _parse_caps_simple(caps): 77 | _prctl.prctl(_prctl.PR_CAPBSET_DROP, cap) 78 | 79 | def limit(self, *caps): 80 | for cap in [x for x in ALL_CAPS if x not in _parse_caps_simple(caps)]: 81 | _prctl.prctl(_prctl.PR_CAPBSET_DROP, cap) 82 | 83 | capbset = Capbset() 84 | 85 | class Capset(object): 86 | __slots__ = ALL_CAP_NAMES + ('flag',) 87 | def __init__(self, flag): 88 | self.flag = flag 89 | for name in _ALL_CAP_NAMES: 90 | friendly_name = name[4:].lower() 91 | setattr(self.__class__, friendly_name, cap_wrapper(getattr(_prctl, name))) 92 | 93 | def drop(self, *caps): 94 | set_caps((_parse_caps_simple(caps), self.flag, False)) 95 | 96 | def limit(self, *caps): 97 | set_caps(([x for x in ALL_CAPS if x not in _parse_caps_simple(caps)], self.flag, False)) 98 | 99 | cap_effective = Capset(_prctl.CAP_EFFECTIVE) 100 | cap_inheritable = Capset(_prctl.CAP_INHERITABLE) 101 | cap_permitted = Capset(_prctl.CAP_PERMITTED) 102 | 103 | class Securebits(object): 104 | __slots__ = [name[7:].lower() for name in dir(_prctl) if name.startswith('SECBIT_')] 105 | def __init__(self): 106 | for name in dir(_prctl): 107 | if name.startswith('SECBIT_'): 108 | friendly_name = name[7:].lower() 109 | setattr(self.__class__, friendly_name, sec_wrapper(getattr(_prctl, name))) 110 | 111 | securebits = Securebits() 112 | 113 | # Copy constants from _prctl and generate the functions 114 | self = sys.modules['prctl'] 115 | 116 | for name in dir(_prctl): 117 | if name.startswith(('PR_GET', 'PR_SET', 'PR_CAPBSET', 'PR_PAC_RESET', 'PR_MPX', 'PR_TASK')) and not \ 118 | name.startswith(('PR_SET_MM_', 'PR_SET_PTRACER_')): 119 | 120 | # Generate a function for this option 121 | val = getattr(_prctl, name) 122 | friendly_name = name.lower()[3:] 123 | setattr(self, friendly_name, prctl_wrapper(val)) 124 | 125 | elif name.startswith('PR_'): 126 | # Add the argument constants without PR_ prefix 127 | setattr(self, name[3:], getattr(_prctl, name)) 128 | 129 | elif name.startswith('CAP_') or name.startswith('SECBIT_') or name.startswith('SECURE_'): 130 | # Add CAP_*/SECBIT_*/SECURE_* constants verbatim. You shouldn't use them anyway, 131 | # use the capbset/securebits object 132 | setattr(self, name, getattr(_prctl, name)) 133 | 134 | def _parse_caps_simple(caps): 135 | ret = [] 136 | for cap in caps: 137 | if isinstance(cap, basestring): 138 | if 'CAP_' + cap.upper() in _ALL_CAP_NAMES: 139 | cap = 'CAP_' + cap.upper() 140 | elif cap not in _ALL_CAP_NAMES: 141 | raise ValueError("Unknown capability: %s" % cap) 142 | cap = getattr(_prctl, cap) 143 | elif cap not in ALL_CAPS: 144 | raise ValueError("Unknown capability: %s" % str(cap)) 145 | ret.append(cap) 146 | return ret 147 | 148 | def _parse_caps(has_value, *args): 149 | if has_value: 150 | new_args = {(_prctl.CAP_PERMITTED,True): [], 151 | (_prctl.CAP_INHERITABLE,True): [], 152 | (_prctl.CAP_EFFECTIVE,True): [], 153 | (_prctl.CAP_PERMITTED,False): [], 154 | (_prctl.CAP_INHERITABLE,False): [], 155 | (_prctl.CAP_EFFECTIVE,False): []} 156 | else: 157 | new_args = {_prctl.CAP_PERMITTED: [], 158 | _prctl.CAP_INHERITABLE: [], 159 | _prctl.CAP_EFFECTIVE: []} 160 | for arg in args: 161 | if has_value: 162 | caps, flags, value = arg 163 | else: 164 | caps, flags = arg 165 | # Accepted format: (cap|[cap,...], flag|[flag,...]) 166 | if not (hasattr(caps, '__iter__') or hasattr(caps, '__getitem__')): 167 | caps = [caps] 168 | caps = _parse_caps_simple(caps) 169 | if not (hasattr(flags, '__iter__') or hasattr(flags, '__getitem__')): 170 | flags = [flags] 171 | for cap in caps: 172 | for flag in flags: 173 | if has_value: 174 | new_args[(flag,value)].append(cap) 175 | else: 176 | new_args[flag].append(cap) 177 | if has_value: 178 | et = list(set(new_args[(_prctl.CAP_EFFECTIVE,True)])) 179 | pt = list(set(new_args[(_prctl.CAP_PERMITTED,True)])) 180 | it = list(set(new_args[(_prctl.CAP_INHERITABLE,True)])) 181 | ef = list(set(new_args[(_prctl.CAP_EFFECTIVE,False)])) 182 | pf = list(set(new_args[(_prctl.CAP_PERMITTED,False)])) 183 | if_ = list(set(new_args[(_prctl.CAP_INHERITABLE,False)])) 184 | return (et, pt, it, ef, pf, if_) 185 | else: 186 | e = list(set(new_args[_prctl.CAP_EFFECTIVE])) 187 | p = list(set(new_args[_prctl.CAP_PERMITTED])) 188 | i = list(set(new_args[_prctl.CAP_INHERITABLE])) 189 | return (e, p, i) 190 | 191 | def get_caps(*args): 192 | return _prctl.get_caps(*_parse_caps(False, *args)) 193 | 194 | def set_caps(*args): 195 | return _prctl.set_caps(*_parse_caps(True, *args)) 196 | 197 | # Functions copied directly, not part of the prctl interface 198 | set_proctitle = _prctl.set_proctitle 199 | 200 | # Delete the init-only things 201 | del self, friendly_name, name, prctl_wrapper, cap_wrapper, capb_wrapper, sec_wrapper 202 | del sys, val 203 | -------------------------------------------------------------------------------- /test_prctl.py: -------------------------------------------------------------------------------- 1 | # python-pctrl -- python interface to the prctl function 2 | # (c)2010-2020 Dennis Kaarsemaker 3 | # See COPYING for licensing details 4 | 5 | import distutils.util 6 | import glob 7 | import os 8 | import re 9 | import signal 10 | import stat 11 | import sys 12 | import subprocess 13 | import unittest 14 | 15 | so = '.so' 16 | try: 17 | import sysconfig 18 | so = sysconfig.get_config_var('EXT_SUFFIX') or sysconfig.get_config_var('SO') 19 | except ImportError: 20 | pass 21 | 22 | curdir = os.path.dirname(__file__) 23 | builddir = os.path.join(curdir, 'build', 'lib.%s-%s' % (distutils.util.get_platform(), sys.version[0:3])) 24 | 25 | # Always run from the builddir 26 | if not os.path.exists(builddir) or \ 27 | not os.path.exists(os.path.join(builddir, 'prctl.py')) or \ 28 | not os.path.exists(os.path.join(builddir, '_prctl' + so)) or \ 29 | int(os.path.getmtime(os.path.join(curdir, 'prctl.py'))) > int(os.path.getmtime(os.path.join(builddir, 'prctl.py'))) or \ 30 | os.path.getmtime(os.path.join(curdir, '_prctlmodule.c')) > os.path.getmtime(os.path.join(builddir, '_prctl' + so)): 31 | sys.stderr.write("Please build the extension first, using ./setup.py build\n") 32 | sys.exit(1) 33 | sys.path.insert(0, builddir) 34 | 35 | import prctl 36 | import _prctl 37 | 38 | def require(attr): 39 | def decorator(func): 40 | if not hasattr(prctl, attr) and not hasattr(_prctl, attr): 41 | return lambda *args, **kwargs: None 42 | return func 43 | return decorator 44 | 45 | class PrctlTest(unittest.TestCase): 46 | # There are architecture specific tests 47 | arch = os.uname()[4] 48 | # prctl behaviour differs when root, so you should test as root and non-root 49 | am_root = os.geteuid() == 0 50 | 51 | def test_constants(self): 52 | """Test whether copying of constants works""" 53 | self.assertEqual(prctl.ENDIAN_LITTLE, _prctl.PR_ENDIAN_LITTLE) 54 | self.assertEqual(prctl.SECBIT_NOROOT, _prctl.SECBIT_NOROOT) 55 | self.assertEqual(prctl.CAP_SYS_ADMIN, _prctl.CAP_SYS_ADMIN) 56 | self.assertRaises(AttributeError, getattr, prctl, 'PR_ENDIAN_LITTLE') 57 | self.assertRaises(AttributeError, getattr, prctl, 'PR_CAPBSET_READ') 58 | self.assertRaises(AttributeError, getattr, prctl, 'CAPBSET_READ') 59 | self.assertEqual(prctl.SET_PTRACER_ANY, _prctl.PR_SET_PTRACER_ANY) 60 | 61 | @require('PR_CAPBSET_READ') 62 | def test_capbset(self): 63 | """Test the get_capbset/set_capbset functions""" 64 | self.assertEqual(prctl.capbset_read(prctl.CAP_FOWNER), True) 65 | if self.am_root: 66 | self.assertEqual(prctl.capbset_drop(prctl.CAP_FOWNER), None) 67 | self.assertEqual(prctl.capbset_read(prctl.CAP_FOWNER), False) 68 | else: 69 | self.assertRaises(OSError, prctl.capbset_drop, prctl.CAP_MKNOD) 70 | self.assertRaises(ValueError, prctl.capbset_read, 999) 71 | 72 | @require('PR_CAPBSET_READ') 73 | def test_capbset_object(self): 74 | """Test manipulation of the capability bounding set via the capbset object""" 75 | self.assertEqual(prctl.capbset.sys_admin, True) 76 | if self.am_root: 77 | prctl.capbset.kill = False 78 | self.assertEqual(prctl.capbset.kill, False) 79 | self.assertEqual(prctl.capbset.sys_admin, True) 80 | prctl.capbset.drop("setgid", prctl.CAP_SETGID) 81 | self.assertEqual(prctl.capbset.setgid, False) 82 | caps = list(prctl.ALL_CAPS) 83 | caps.remove(prctl.CAP_NET_RAW) 84 | prctl.capbset.limit(*caps) 85 | self.assertEqual(prctl.capbset.net_raw, False) 86 | self.assertEqual(prctl.capbset.net_broadcast, True) 87 | 88 | else: 89 | def set_false(): 90 | prctl.capbset.kill = False 91 | self.assertRaises(OSError, set_false) 92 | def set_true(): 93 | prctl.capbset.kill = True 94 | self.assertRaises(ValueError, set_true) 95 | def unknown_attr(): 96 | prctl.capbset.foo = 1 97 | self.assertRaises(AttributeError, unknown_attr) 98 | 99 | @require('get_child_subreaper') 100 | def test_child_subreaper(self): 101 | self.assertEqual(prctl.get_child_subreaper(), 0) 102 | prctl.set_child_subreaper(1) 103 | self.assertEqual(prctl.get_child_subreaper(), 1) 104 | prctl.set_child_subreaper(0) 105 | 106 | def test_dumpable(self): 107 | """Test manipulation of the dumpable flag""" 108 | prctl.set_dumpable(True) 109 | self.assertEqual(prctl.get_dumpable(), True) 110 | prctl.set_dumpable(False) 111 | self.assertEqual(prctl.get_dumpable(), False) 112 | self.assertRaises(TypeError, prctl.get_dumpable, "42") 113 | 114 | def test_endian(self): 115 | """Test manipulation of the endianness setting""" 116 | if self.arch == 'powerpc': 117 | # FIXME untested 118 | prctl.set_endian(prctl.ENDIAN_BIG) 119 | self.assertEqual(prctl.get_endian(), prctl.ENDIAN_BIG) 120 | prctl.set_endian(prctl.ENDIAN_LITTLE) 121 | self.assertEqual(prctl.get_endian(), prctl.ENDIAN_LITTLE) 122 | self.assertRaises(ValueError, prctl.set_endian, 999) 123 | else: 124 | self.assertRaises(OSError, prctl.get_endian) 125 | self.assertRaises(OSError, prctl.set_endian) 126 | 127 | def test_fpemu(self): 128 | """Test manipulation of the fpemu setting""" 129 | if self.arch == 'ia64': 130 | # FIXME - untested 131 | prctl.set_fpemu(prctl.FPEMU_SIGFPE) 132 | self.assertEqual(prctl.get_fpemu(), prctl.FPEMU_SIGFPE) 133 | prctl.set_fpemu(prctl.FPEMU_NOPRINT) 134 | self.assertEqual(prctl.get_fpemu(), prctl.FPEMU_NOPRINT) 135 | self.assertRaises(ValueError, prctl.set_fpexc, 999) 136 | else: 137 | self.assertRaises(OSError, prctl.get_fpemu) 138 | self.assertRaises(OSError, prctl.set_fpemu, prctl.FPEMU_SIGFPE) 139 | 140 | def test_fpexc(self): 141 | """Test manipulation of the fpexc setting""" 142 | if self.arch == 'powerpc': 143 | # FIXME - untested 144 | prctl.set_fpexc(prctl.FP_EXC_SW_ENABLE) 145 | self.assertEqual(prctl.get_fpexc() & prctl.PR_FP_EXC_SW_ENABLE, prctl.PR_FP_EXC_SW_ENABLE) 146 | self.assertRaises(ValueError, prctl.set_fpexc, 999) 147 | else: 148 | self.assertRaises(OSError, prctl.get_fpexc) 149 | self.assertRaises(OSError, prctl.set_fpexc) 150 | 151 | @require('set_fp_mode') 152 | def test_fp_mode(self): 153 | """Test manipulation of the fp_mode setting""" 154 | if self.arch == 'mips': 155 | # FIXME - untested 156 | prctl.set_fp_mode(prctl.FP_MODE_FR) 157 | self.assertEqual(prctl.get_fp_mode(), prctl.FP_MODE_FR) 158 | prctl.set_fp_mode(prctl.FP_MODE_FRE) 159 | self.assertEqual(prctl.get_fp_mode(), prctl.FP_MODE_FRE) 160 | self.assertRaises(ValueError, prctl.set_fp_mode, 999) 161 | else: 162 | self.assertRaises(OSError, prctl.get_fpexc) 163 | self.assertRaises(OSError, prctl.set_fpexc) 164 | 165 | @require('set_io_flusher') 166 | def test_io_flusher(self): 167 | if self.am_root: 168 | self.assertEqual(prctl.get_io_flusher(), 0) 169 | self.assertEqual(prctl.set_io_flusher(True), None) 170 | self.assertEqual(prctl.get_io_flusher(), 1) 171 | self.assertEqual(prctl.set_io_flusher(False), None) 172 | self.assertEqual(prctl.get_io_flusher(), 0) 173 | else: 174 | self.assertRaises(OSError, prctl.get_io_flusher) 175 | self.assertRaises(OSError, prctl.set_io_flusher) 176 | 177 | def test_keepcaps(self): 178 | """Test manipulation of the keepcaps setting""" 179 | prctl.set_keepcaps(True) 180 | self.assertEqual(prctl.get_keepcaps(), True) 181 | prctl.set_keepcaps(False) 182 | self.assertEqual(prctl.get_keepcaps(), False) 183 | 184 | @require('set_mce_kill') 185 | def test_mce_kill(self): 186 | """Test the MCE_KILL setting""" 187 | if not os.path.exists('/proc/sys/vm/memory_failure_early_kill'): 188 | return 189 | fd = open('/proc/sys/vm/memory_failure_early_kill') 190 | current = int(fd.read().strip()) 191 | fd.close() 192 | prctl.set_mce_kill(prctl.MCE_KILL_EARLY) 193 | self.assertEqual(prctl.get_mce_kill(), prctl.MCE_KILL_EARLY) 194 | prctl.set_mce_kill(prctl.MCE_KILL_LATE) 195 | self.assertEqual(prctl.get_mce_kill(), prctl.MCE_KILL_LATE) 196 | prctl.set_mce_kill(prctl.MCE_KILL_DEFAULT) 197 | self.assertEqual(prctl.get_mce_kill(), prctl.MCE_KILL_DEFAULT) 198 | 199 | @require('mpx_enable_management') 200 | def test_mpx(self): 201 | """Test MPX enabling/disabling""" 202 | if os.uname().release > "5.4": 203 | self.assertRaises(OSError, prctl.mpx_enable_management) 204 | self.assertRaises(OSError, prctl.mpx_disable_management) 205 | else: 206 | self.assertEqual(prctl.mpx_enable_management(), None) 207 | self.assertEqual(prctl.mpx_disable_management(), None) 208 | 209 | def test_name(self): 210 | """Test setting the process name""" 211 | name = prctl.get_name().swapcase() * 16 212 | prctl.set_name(name) 213 | self.assertEqual(prctl.get_name(), name[:15]) 214 | 215 | @require('get_no_new_privs') 216 | def test_no_new_privs(self): 217 | """Test the no_new_privs function""" 218 | self.assertEqual(prctl.get_no_new_privs(), 0) 219 | if not (os.stat('/bin/ping').st_mode & stat.S_ISUID): 220 | # Test doesn't work unless ping is setuid 221 | return 222 | pid = os.fork() 223 | if pid: 224 | self.assertEqual(os.waitpid(pid, 0)[1], 0) 225 | else: 226 | prctl.set_no_new_privs(1) 227 | self.assertEqual(prctl.get_no_new_privs(), 1) 228 | if os.geteuid() != 0: 229 | sp = subprocess.Popen(['ping', '-c1', 'localhost'], stderr=subprocess.PIPE) 230 | sp.communicate() 231 | self.assertNotEqual(sp.returncode, 0) 232 | os._exit(0) 233 | 234 | @require('pac_reset_keys') 235 | def test_pac_reset_keys(self): 236 | if self.arch == 'arm64': 237 | # FIXME untested 238 | self.assertEqual(prctl.pac_reset_keys(prctl.PAC_APIAKEY), None) 239 | self.assertRaises(ValueError, prctl.pac_reset_keys, 0xff) 240 | else: 241 | self.assertRaises(OSError, prctl.pac_reset_keys, prctl.PAC_APIAKEY) 242 | 243 | def test_proctitle(self): 244 | """Test setting the process title, including too long titles""" 245 | with open('/proc/self/cmdline') as fd: 246 | orig = len(fd.read()) 247 | title = "This is a test!" 248 | prctl.set_proctitle(title) 249 | with open('/proc/self/cmdline') as fd: 250 | cmdline = fd.read().rstrip('\n\0') 251 | self.assertEqual(cmdline, title) 252 | # This should not segfault 253 | title2 = "And this is a test too! Don't segfault." * 3 254 | prctl.set_proctitle(title2) 255 | with open('/proc/self/cmdline') as fd: 256 | cmdline = fd.read().rstrip('\n\0') 257 | self.assertEqual(cmdline, title2[:orig-1]) 258 | 259 | def test_pdeathsig(self): 260 | """Test manipulation of the pdeathsig setting""" 261 | self.assertRaises(ValueError, prctl.set_pdeathsig, 999) 262 | self.assertEqual(prctl.get_pdeathsig(), 0) 263 | prctl.set_pdeathsig(signal.SIGINT) 264 | self.assertEqual(prctl.get_pdeathsig(), signal.SIGINT) 265 | 266 | @require('set_ptracer') 267 | def test_ptracer(self): 268 | """Test manipulation of the ptracer setting""" 269 | if not os.path.exists('/proc/sys/kernel/yama'): 270 | return 271 | self.assertEqual(prctl.get_ptracer(), os.getppid()) 272 | prctl.set_ptracer(1) 273 | self.assertEqual(prctl.get_ptracer(), 1) 274 | new_pid = os.fork() 275 | if new_pid: 276 | os.waitpid(new_pid, 0) 277 | else: 278 | os._exit(0) 279 | self.assertRaises(OSError, prctl.set_ptracer, new_pid) 280 | 281 | @require('get_seccomp') 282 | def test_seccomp(self): 283 | """Test manipulation of the seccomp setting""" 284 | self.assertEqual(prctl.get_seccomp(), False) 285 | result = os.fork() 286 | if result == 0: 287 | # In child 288 | prctl.set_seccomp(True) 289 | # This should kill ourselves 290 | open('/etc/resolv.conf') 291 | # If not, kill ourselves anyway 292 | sys.exit(0) 293 | else: 294 | pid, result = os.waitpid(result, 0) 295 | self.assertTrue(os.WIFSIGNALED(result)) 296 | self.assertEqual(os.WTERMSIG(result), signal.SIGKILL) 297 | 298 | @require('PR_GET_SECUREBITS') 299 | def test_securebits(self): 300 | """Test manipulation of the securebits flag""" 301 | self.assertEqual(prctl.get_securebits(), 0) 302 | if os.geteuid() == 0: 303 | prctl.set_securebits(prctl.SECBIT_KEEP_CAPS) 304 | self.assertEqual(prctl.get_securebits(), prctl.SECBIT_KEEP_CAPS) 305 | else: 306 | self.assertRaises(OSError, prctl.set_securebits, prctl.SECBIT_KEEP_CAPS) 307 | 308 | @require('PR_GET_SECUREBITS') 309 | def test_securebits_obj(self): 310 | """Test manipulation of the securebits via the securebits object""" 311 | self.assertEqual(prctl.securebits.noroot, False) 312 | if os.geteuid() == 0: 313 | prctl.securebits.noroot = True 314 | self.assertEqual(prctl.securebits.noroot, True) 315 | self.assertEqual(prctl.securebits.no_setuid_fixup, False) 316 | prctl.securebits.noroot_locked = True 317 | def set_false(): 318 | prctl.securebits.noroot = False 319 | self.assertRaises(OSError, set_false) 320 | else: 321 | def set_true(): 322 | prctl.securebits.noroot = True 323 | self.assertRaises(OSError, set_true) 324 | 325 | @require('set_speculation_ctrl') 326 | def test_speculation_ctrl(self): 327 | self.assertTrue(prctl.get_speculation_ctrl(prctl.SPEC_STORE_BYPASS) > 0) 328 | self.assertTrue(prctl.get_speculation_ctrl(prctl.SPEC_INDIRECT_BRANCH) > 0) 329 | self.assertRaises(ValueError, prctl.get_speculation_ctrl, 99) 330 | self.assertRaises(ValueError, prctl.set_speculation_ctrl, 99) 331 | prctl.set_speculation_ctrl(prctl.SPEC_STORE_BYPASS, prctl.SPEC_ENABLE) 332 | self.assertEqual(prctl.get_speculation_ctrl(prctl.SPEC_STORE_BYPASS) & ~prctl.SPEC_PRCTL, prctl.SPEC_ENABLE) 333 | prctl.set_speculation_ctrl(prctl.SPEC_STORE_BYPASS, prctl.SPEC_FORCE_DISABLE) 334 | self.assertRaises(PermissionError, prctl.set_speculation_ctrl, prctl.SPEC_STORE_BYPASS, prctl.SPEC_ENABLE) 335 | 336 | @require('task_perf_events_enable') 337 | def test_task_perf_events(self): 338 | prctl.task_perf_events_disable() 339 | prctl.task_perf_events_enable() 340 | 341 | @require('get_thp_disable') 342 | def test_thp_disable(self): 343 | self.assertEqual(prctl.get_thp_disable(), False) 344 | prctl.set_thp_disable(True) 345 | self.assertEqual(prctl.get_thp_disable(), True) 346 | prctl.set_thp_disable(False) 347 | self.assertEqual(prctl.get_thp_disable(), False) 348 | 349 | @require('get_timerslack') 350 | def test_timerslack(self): 351 | """Test manipulation of the timerslack value""" 352 | default = prctl.get_timerslack() 353 | prctl.set_timerslack(1000) 354 | self.assertEqual(prctl.get_timerslack(), 1000) 355 | prctl.set_timerslack(0) 356 | self.assertEqual(prctl.get_timerslack(), default) 357 | 358 | def test_timing(self): 359 | """Test manipulation of the timing setting""" 360 | self.assertRaises(OSError, prctl.set_timing, prctl.TIMING_TIMESTAMP); 361 | self.assertEqual(prctl.get_timing(), prctl.TIMING_STATISTICAL) 362 | prctl.set_timing(prctl.TIMING_STATISTICAL) 363 | self.assertEqual(prctl.get_timing(), prctl.TIMING_STATISTICAL) 364 | 365 | @require('set_tsc') 366 | def test_tsc(self): 367 | """Test manipulation of the timestamp counter flag""" 368 | if re.match('i.86|x86_64', self.arch): 369 | prctl.set_tsc(prctl.TSC_SIGSEGV) 370 | self.assertEqual(prctl.get_tsc(), prctl.TSC_SIGSEGV) 371 | prctl.set_tsc(prctl.TSC_ENABLE) 372 | self.assertEqual(prctl.get_tsc(), prctl.TSC_ENABLE) 373 | else: 374 | # FIXME untested 375 | self.assertRaises(OSError, prctl.get_tsc) 376 | self.assertRaises(OSError, prctl.set_tsc, prctl.TSC_ENABLE) 377 | 378 | def test_unalign(self): 379 | """Test manipulation of the unaligned access setting""" 380 | if self.arch in ('ia64', 'parisc', 'powerpc', 'alpha'): 381 | # FIXME untested 382 | prctl.set_unalign(prctl.UNALIGN_NOPRINT) 383 | self.assertEqual(prctl.get_unalign(), prctl.UNALIGN_NOPRINT) 384 | prctl.set_unalign(prctl.UNALIGN_SIGBUS) 385 | self.assertEqual(prctl.get_unalign(), prctl.UNALIGN_SIGBUS) 386 | else: 387 | self.assertRaises(OSError, prctl.get_unalign) 388 | self.assertRaises(OSError, prctl.set_unalign, prctl.UNALIGN_NOPRINT) 389 | 390 | def test_getcaps(self): 391 | """Test the get_caps function""" 392 | self.assertEqual(prctl.get_caps(), {prctl.CAP_EFFECTIVE: {}, prctl.CAP_INHERITABLE: {}, prctl.CAP_PERMITTED: {}}) 393 | self.assertEqual(prctl.get_caps((prctl.CAP_SYS_ADMIN, prctl.ALL_FLAGS),(prctl.CAP_NET_ADMIN, prctl.CAP_EFFECTIVE)), 394 | {prctl.CAP_EFFECTIVE: {prctl.CAP_SYS_ADMIN: self.am_root, prctl.CAP_NET_ADMIN: self.am_root}, 395 | prctl.CAP_INHERITABLE: {prctl.CAP_SYS_ADMIN: False}, 396 | prctl.CAP_PERMITTED: {prctl.CAP_SYS_ADMIN: self.am_root}}) 397 | self.assertEqual(prctl.get_caps(([prctl.CAP_SYS_ADMIN,prctl.CAP_NET_ADMIN], [prctl.CAP_EFFECTIVE,prctl.CAP_PERMITTED])), 398 | {prctl.CAP_EFFECTIVE: {prctl.CAP_SYS_ADMIN: self.am_root, prctl.CAP_NET_ADMIN: self.am_root}, 399 | prctl.CAP_INHERITABLE: {}, 400 | prctl.CAP_PERMITTED: {prctl.CAP_SYS_ADMIN: self.am_root, prctl.CAP_NET_ADMIN: self.am_root}}) 401 | self.assertRaises(KeyError, prctl.get_caps, (prctl.CAP_SYS_ADMIN,'abc')) 402 | def fail(): 403 | prctl.get_caps((1234,prctl.ALL_FLAGS)) 404 | self.assertRaises(ValueError, fail) 405 | 406 | def test_setcaps(self): 407 | """Test the setcaps function""" 408 | if self.am_root: 409 | prctl.set_caps((prctl.CAP_SETUID, prctl.ALL_FLAGS, True)) 410 | else: 411 | self.assertRaises(OSError, prctl.set_caps, (prctl.CAP_SETUID, prctl.ALL_FLAGS, True)) 412 | self.assertEqual(prctl.get_caps((prctl.CAP_SETUID, prctl.ALL_FLAGS)), 413 | {prctl.CAP_EFFECTIVE: {prctl.CAP_SETUID: self.am_root}, 414 | prctl.CAP_PERMITTED: {prctl.CAP_SETUID: self.am_root}, 415 | prctl.CAP_INHERITABLE: {prctl.CAP_SETUID: self.am_root}}) 416 | prctl.set_caps((prctl.CAP_SETUID, prctl.ALL_FLAGS, False)) 417 | self.assertEqual(prctl.get_caps((prctl.CAP_SETUID, prctl.ALL_FLAGS)), 418 | {prctl.CAP_EFFECTIVE: {prctl.CAP_SETUID: False}, 419 | prctl.CAP_PERMITTED: {prctl.CAP_SETUID: False}, 420 | prctl.CAP_INHERITABLE: {prctl.CAP_SETUID: False}}) 421 | self.assertRaises(OSError, prctl.set_caps, (prctl.CAP_SETUID, prctl.ALL_FLAGS, True)) 422 | 423 | capabilities = [x[4:].lower() for x in dir(_prctl) if x.startswith('CAP_')] 424 | @require('cap_to_name') 425 | def test_capabilities_objects(self): 426 | for cap in self.capabilities: 427 | if cap in ('all','effective','permitted','inheritable','setuid'): 428 | continue 429 | # This one now triggers EINVAL 430 | if cap == 'wake_alarm': 431 | continue 432 | self.assertEqual(getattr(prctl.cap_effective, cap), self.am_root) 433 | self.assertEqual(getattr(prctl.cap_permitted, cap), self.am_root) 434 | self.assertEqual(getattr(prctl.cap_inheritable, cap), False) 435 | for cap in ['dac_override','mac_override','net_raw']: 436 | if self.am_root: 437 | setattr(prctl.cap_effective, cap, False) 438 | setattr(prctl.cap_permitted, cap, False) 439 | setattr(prctl.cap_inheritable, cap, False) 440 | self.assertRaises(OSError, setattr, prctl.cap_effective, cap, True) 441 | self.assertRaises(OSError, setattr, prctl.cap_permitted, cap, True) 442 | if self.am_root: 443 | setattr(prctl.cap_inheritable, cap, True) 444 | else: 445 | self.assertRaises(OSError, setattr, prctl.cap_inheritable, cap, True) 446 | 447 | if self.am_root: 448 | prctl.cap_effective.drop('linux_immutable', 'sys_boot', 'sys_pacct') 449 | self.assertEqual(prctl.cap_effective.linux_immutable, False) 450 | self.assertEqual(prctl.cap_effective.sys_boot, False) 451 | self.assertEqual(prctl.cap_effective.sys_pacct, False) 452 | 453 | caps = list(prctl.ALL_CAPS) 454 | caps.remove(prctl.CAP_SYS_NICE) 455 | prctl.cap_effective.limit(*caps) 456 | self.assertEqual(prctl.cap_effective.sys_nice, False) 457 | 458 | @require('cap_to_name') 459 | def test_captoname(self): 460 | self.assertEqual(_prctl.cap_to_name(prctl.CAP_SYS_ADMIN), 'sys_admin') 461 | 462 | if __name__ == '__main__': 463 | unittest.main() 464 | -------------------------------------------------------------------------------- /_prctlmodule.c: -------------------------------------------------------------------------------- 1 | /* 2 | * python-pctrl -- python interface to the prctl function 3 | * (c)2010-2020 Dennis Kaarsemaker 4 | * See COPYING for licensing details 5 | */ 6 | 7 | #include 8 | #if PY_MAJOR_VERSION >= 3 9 | #define PyInt_FromLong PyLong_FromLong 10 | #define PyInt_Check PyLong_Check 11 | #define PyInt_AsLong PyLong_AsLong 12 | #endif 13 | #include "securebits.h" 14 | #include 15 | #include 16 | #include 17 | 18 | /* New in 2.6.32, but named and implemented inconsistently. The linux 19 | * implementation has two ways of setting the policy to the default, and thus 20 | * needs an extra argument. We ignore the first argument and always call 21 | * PR_MCE_KILL_SET. This makes our implementation simpler and keeps the prctl 22 | * interface more consistent 23 | */ 24 | #ifdef PR_MCE_KILL 25 | #define PR_GET_MCE_KILL PR_MCE_KILL_GET 26 | #define PR_SET_MCE_KILL PR_MCE_KILL 27 | #endif 28 | 29 | /* New in 2.6.XX (Ubuntu 10.10) */ 30 | #define NOT_SET (-1) 31 | #ifdef PR_SET_PTRACER 32 | /* This one has no getter for some reason, but guard against that being fixed */ 33 | #ifndef PR_GET_PTRACER 34 | #define PR_GET_PTRACER NOT_SET 35 | /* Icky global variable to cache ptracer */ 36 | static int __cached_ptracer = NOT_SET; 37 | #endif 38 | #endif 39 | 40 | /* This function is not in Python.h, so define it here */ 41 | #if PY_MAJOR_VERSION < 3 42 | void Py_GetArgcArgv(int*, char***); 43 | #else 44 | void Py_GetArgcArgv(int*, wchar_t***); 45 | #endif 46 | 47 | /* The prctl wrapper */ 48 | static PyObject * 49 | prctl_prctl(PyObject *self, PyObject *args) 50 | { 51 | long option = 0; 52 | long arg = 0; 53 | long arg2 = 0; 54 | char *argstr = NULL; 55 | char name[17] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 56 | int result; 57 | 58 | /* 59 | * Accept single int, two ints and int+string. That covers all current 60 | * prctl possibilities. int+string is required for (and only accepted for) 61 | * PR_SET_NAME 62 | */ 63 | if(!PyArg_ParseTuple(args, "l|ll", &option, &arg, &arg2)) { 64 | if(!PyArg_ParseTuple(args, "ls", &option, &argstr)) { 65 | return NULL; 66 | } 67 | if(option != PR_SET_NAME) { 68 | PyErr_SetString(PyExc_TypeError, "an integer is required"); 69 | return NULL; 70 | } 71 | PyErr_Clear(); 72 | } 73 | else { 74 | if(option == PR_SET_NAME) { 75 | PyErr_SetString(PyExc_TypeError, "a string is required"); 76 | return NULL; 77 | } 78 | } 79 | 80 | /* Validate the optional arguments */ 81 | switch(option) { 82 | #ifdef PR_CAPBSET_READ 83 | case(PR_CAPBSET_READ): 84 | case(PR_CAPBSET_DROP): 85 | if(!cap_valid(arg)) { 86 | PyErr_SetString(PyExc_ValueError, "Unknown capability"); 87 | return NULL; 88 | } 89 | break; 90 | #endif 91 | case(PR_SET_DUMPABLE): 92 | #ifdef PR_SET_IO_FLUSHER 93 | case(PR_SET_IO_FLUSHER): 94 | #endif 95 | case(PR_SET_KEEPCAPS): 96 | /* Only 0 and 1 are allowed */ 97 | arg = arg ? 1 : 0; 98 | break; 99 | case(PR_SET_ENDIAN): 100 | if(arg != PR_ENDIAN_LITTLE && arg != PR_ENDIAN_BIG && arg != PR_ENDIAN_PPC_LITTLE) { 101 | PyErr_SetString(PyExc_ValueError, "Unknown endianness"); 102 | return NULL; 103 | } 104 | break; 105 | case(PR_SET_FPEMU): 106 | if(arg != PR_FPEMU_NOPRINT && arg != PR_FPEMU_SIGFPE) { 107 | PyErr_SetString(PyExc_ValueError, "Unknown floating-point emulation setting"); 108 | return NULL; 109 | } 110 | break; 111 | case(PR_SET_FPEXC): 112 | if(arg & ~(PR_FP_EXC_SW_ENABLE | PR_FP_EXC_DIV | PR_FP_EXC_OVF | 113 | PR_FP_EXC_UND | PR_FP_EXC_RES | PR_FP_EXC_INV | 114 | PR_FP_EXC_DISABLED | PR_FP_EXC_NONRECOV | 115 | PR_FP_EXC_ASYNC | PR_FP_EXC_PRECISE)) { 116 | PyErr_SetString(PyExc_ValueError, "Unknown floating-point exception mode"); 117 | return NULL; 118 | } 119 | break; 120 | #ifdef PR_SET_FP_MODE 121 | case(PR_SET_FP_MODE): 122 | if(arg != PR_FP_MODE_FR && arg != PR_FP_MODE_FRE) { 123 | PyErr_SetString(PyExc_ValueError, "Unknown floating-point mode"); 124 | return NULL; 125 | } 126 | break; 127 | #endif 128 | #ifdef PR_MCE_KILL 129 | case(PR_SET_MCE_KILL): 130 | if(arg != PR_MCE_KILL_DEFAULT && arg != PR_MCE_KILL_EARLY && arg != PR_MCE_KILL_LATE) { 131 | PyErr_SetString(PyExc_ValueError, "Unknown memory corruption kill policy"); 132 | return NULL; 133 | } 134 | break; 135 | #endif 136 | case(PR_SET_NAME): 137 | if(strlen(argstr) > 16) { 138 | /* FIXME: warn */ 139 | } 140 | strncpy(name, argstr, 16); 141 | break; 142 | #ifdef PR_PAC_RESET_KEYS 143 | case(PR_PAC_RESET_KEYS): 144 | if(arg & ~(PR_PAC_APIAKEY | PR_PAC_APIBKEY | PR_PAC_APDAKEY | PR_PAC_APDBKEY | PR_PAC_APGAKEY)) { 145 | PyErr_SetString(PyExc_ValueError, "Unknown pointer authentication key"); 146 | return NULL; 147 | } 148 | break; 149 | #endif 150 | case(PR_SET_PDEATHSIG): 151 | if(arg < 0 || arg > SIGRTMAX) { 152 | PyErr_SetString(PyExc_ValueError, "Unknown signal"); 153 | return NULL; 154 | } 155 | break; 156 | #ifdef PR_SET_SECCOMP 157 | case(PR_SET_SECCOMP): 158 | #ifdef PR_SET_NO_NEW_PRIVS 159 | case(PR_SET_NO_NEW_PRIVS): 160 | #endif 161 | if(!arg) { 162 | PyErr_SetString(PyExc_ValueError, "Argument must be 1"); 163 | return NULL; 164 | } 165 | arg = 1; 166 | break; 167 | #endif 168 | #ifdef PR_SET_SECUREBITS 169 | case(PR_SET_SECUREBITS): 170 | if(arg & ~ ((1 << SECURE_NOROOT) | (1 << SECURE_NOROOT_LOCKED) | 171 | (1 << SECURE_NO_SETUID_FIXUP) | (1 << SECURE_NO_SETUID_FIXUP_LOCKED) | 172 | (1 << SECURE_KEEP_CAPS) | (1 << SECURE_KEEP_CAPS_LOCKED))) { 173 | PyErr_SetString(PyExc_ValueError, "Invalid securebits set"); 174 | return NULL; 175 | } 176 | break; 177 | #endif 178 | #ifdef PR_GET_SPECULATION_CTRL 179 | case(PR_SET_SPECULATION_CTRL): 180 | #ifdef PR_SPEC_INDIRECT_BRANCH 181 | if(arg != PR_SPEC_STORE_BYPASS && arg != PR_SPEC_INDIRECT_BRANCH) { 182 | #else 183 | if(arg != PR_SPEC_STORE_BYPASS) { 184 | #endif 185 | PyErr_SetString(PyExc_ValueError, "Invalid speculation control setting"); 186 | return NULL; 187 | } 188 | if(arg2 != PR_SPEC_ENABLE 189 | && arg2 != PR_SPEC_DISABLE 190 | && arg2 != PR_SPEC_FORCE_DISABLE 191 | #ifdef PR_SPEC_DISABLE_NOEXEC 192 | && arg2 != PR_SPEC_DISABLE_NOEXEC 193 | #endif 194 | ) { 195 | PyErr_SetString(PyExc_ValueError, "Invalid speculation control value"); 196 | return NULL; 197 | } 198 | /* Intentionally not breaking */ 199 | case(PR_GET_SPECULATION_CTRL): 200 | #ifdef PR_SPEC_INDIRECT_BRANCH 201 | if(arg != PR_SPEC_STORE_BYPASS && arg != PR_SPEC_INDIRECT_BRANCH) { 202 | #else 203 | if(arg != PR_SPEC_STORE_BYPASS) { 204 | #endif 205 | PyErr_SetString(PyExc_ValueError, "Invalid speculation control setting"); 206 | return NULL; 207 | } 208 | break; 209 | #endif 210 | case(PR_SET_TIMING): 211 | if(arg != PR_TIMING_STATISTICAL && arg != PR_TIMING_TIMESTAMP) { 212 | PyErr_SetString(PyExc_ValueError, "Invalid timing constant"); 213 | return NULL; 214 | } 215 | break; 216 | #ifdef PR_SET_TSC 217 | case(PR_SET_TSC): 218 | if(arg != PR_TSC_ENABLE && arg != PR_TSC_SIGSEGV) { 219 | PyErr_SetString(PyExc_ValueError, "Invalid TSC setting"); 220 | return NULL; 221 | } 222 | break; 223 | #endif 224 | case(PR_SET_UNALIGN): 225 | if(arg != PR_UNALIGN_NOPRINT && arg != PR_UNALIGN_SIGBUS) { 226 | PyErr_SetString(PyExc_ValueError, "Invalid TSC setting"); 227 | return NULL; 228 | } 229 | break; 230 | } 231 | /* 232 | * Calling prctl 233 | * There are 3 basic call modes: 234 | * - Setters and getters for which the return value is the result 235 | * - Getters for which the result is placed in arg2 236 | * - Getters and setters that deal with strings. 237 | * 238 | * This function takes care of all that and always returns Py_None for 239 | * settings or the result of a getter call as a PyInt or PyString. 240 | */ 241 | switch(option) { 242 | #ifdef PR_CAPBSET_READ 243 | case(PR_CAPBSET_READ): 244 | case(PR_CAPBSET_DROP): 245 | #endif 246 | #ifdef PR_SET_CHILD_SUBREAPER 247 | case(PR_SET_CHILD_SUBREAPER): 248 | #endif 249 | case(PR_SET_DUMPABLE): 250 | case(PR_GET_DUMPABLE): 251 | case(PR_SET_ENDIAN): 252 | case(PR_SET_FPEMU): 253 | case(PR_SET_FPEXC): 254 | #ifdef PR_SET_FP_MODE 255 | case(PR_GET_FP_MODE): 256 | case(PR_SET_FP_MODE): 257 | #endif 258 | #ifdef PR_SET_IO_FLUSHER 259 | case(PR_GET_IO_FLUSHER): 260 | case(PR_SET_IO_FLUSHER): 261 | #endif 262 | case(PR_SET_KEEPCAPS): 263 | case(PR_GET_KEEPCAPS): 264 | #ifdef PR_MCE_KILL 265 | case(PR_GET_MCE_KILL): 266 | #endif 267 | #ifdef PR_MPX_ENABLE_MANAGEMENT 268 | case(PR_MPX_ENABLE_MANAGEMENT): 269 | case(PR_MPX_DISABLE_MANAGEMENT): 270 | #endif 271 | #ifdef PR_GET_NO_NEW_PRIVS 272 | case(PR_GET_NO_NEW_PRIVS): 273 | case(PR_SET_NO_NEW_PRIVS): 274 | #endif 275 | #ifdef PR_PAC_RESET_KEYS 276 | case(PR_PAC_RESET_KEYS): 277 | #endif 278 | case(PR_SET_PDEATHSIG): 279 | #if defined(PR_GET_PTRACER) && (PR_GET_PTRACER != NOT_SET) 280 | case(PR_GET_PTRACER): 281 | #endif 282 | #ifdef PR_SET_PTRACER 283 | case(PR_SET_PTRACER): 284 | #endif 285 | #ifdef PR_SET_SECCOMP 286 | case(PR_SET_SECCOMP): 287 | case(PR_GET_SECCOMP): 288 | #endif 289 | #ifdef PR_SET_SECUREBITS 290 | case(PR_SET_SECUREBITS): 291 | case(PR_GET_SECUREBITS): 292 | #endif 293 | #ifdef PR_GET_SPECULATION_CTRL 294 | case(PR_GET_SPECULATION_CTRL): 295 | case(PR_SET_SPECULATION_CTRL): 296 | #endif 297 | #ifdef PR_TASK_PERF_EVENTS_DISABLE 298 | case(PR_TASK_PERF_EVENTS_DISABLE): 299 | case(PR_TASK_PERF_EVENTS_ENABLE): 300 | #endif 301 | #ifdef PR_SET_THP_DISABLE 302 | case(PR_GET_THP_DISABLE): 303 | case(PR_SET_THP_DISABLE): 304 | #endif 305 | #ifdef PR_GET_TIMERSLACK 306 | case(PR_GET_TIMERSLACK): 307 | case(PR_SET_TIMERSLACK): 308 | #endif 309 | case(PR_SET_TIMING): 310 | case(PR_GET_TIMING): 311 | #ifdef PR_SET_TSC 312 | case(PR_SET_TSC): 313 | #endif 314 | case(PR_SET_UNALIGN): 315 | result = prctl(option, arg, arg2, 0, 0); 316 | if(result < 0) { 317 | PyErr_SetFromErrno(PyExc_OSError); 318 | return NULL; 319 | } 320 | switch(option) { 321 | #ifdef PR_CAPBSET_READ 322 | case(PR_CAPBSET_READ): 323 | #endif 324 | case(PR_GET_DUMPABLE): 325 | #ifdef PR_SET_IO_FLUSHER 326 | case(PR_GET_IO_FLUSHER): 327 | #endif 328 | case(PR_GET_KEEPCAPS): 329 | #ifdef PR_GET_SECCOMP 330 | case(PR_GET_SECCOMP): 331 | #endif 332 | #ifdef PR_GET_THP_DISABLE 333 | case(PR_GET_THP_DISABLE): 334 | #endif 335 | case(PR_GET_TIMING): 336 | return PyBool_FromLong(result); 337 | 338 | #ifdef PR_SET_FP_MODE 339 | case(PR_GET_FP_MODE): 340 | #endif 341 | #ifdef PR_MCE_KILL 342 | case(PR_GET_MCE_KILL): 343 | #endif 344 | #ifdef PR_GET_NO_NEW_PRIVS 345 | case(PR_GET_NO_NEW_PRIVS): 346 | #endif 347 | #if defined(PR_GET_PTRACER) && (PR_GET_PTRACER != NOT_SET) 348 | case(PR_GET_PTRACER): 349 | #endif 350 | #ifdef PR_GET_SECUREBITS 351 | case(PR_GET_SECUREBITS): 352 | #endif 353 | #ifdef PR_GET_SPECULATION_CTRL 354 | case(PR_GET_SPECULATION_CTRL): 355 | #endif 356 | #ifdef PR_GET_TIMERSLACK 357 | case(PR_GET_TIMERSLACK): 358 | #endif 359 | return PyInt_FromLong(result); 360 | #if defined(PR_GET_PTRACER) && (PR_GET_PTRACER == NOT_SET) 361 | case(PR_SET_PTRACER): 362 | __cached_ptracer = arg; 363 | break; 364 | #endif 365 | } 366 | break; 367 | #ifdef PR_GET_CHILD_SUBREAPER 368 | case(PR_GET_CHILD_SUBREAPER): 369 | #endif 370 | case(PR_GET_ENDIAN): 371 | case(PR_GET_FPEMU): 372 | case(PR_GET_FPEXC): 373 | case(PR_GET_PDEATHSIG): 374 | #ifdef PR_GET_TSC 375 | case(PR_GET_TSC): 376 | #endif 377 | case(PR_GET_UNALIGN): 378 | #ifdef PR_GET_TID_ADDRESS 379 | case(PR_GET_TID_ADDRESS): 380 | #endif 381 | result = prctl(option, &arg, 0, 0, 0); 382 | if(result < 0) { 383 | PyErr_SetFromErrno(PyExc_OSError); 384 | return NULL; 385 | } 386 | return PyInt_FromLong(arg); 387 | case(PR_SET_NAME): 388 | case(PR_GET_NAME): 389 | result = prctl(option, name, 0, 0, 0); 390 | if(result < 0) { 391 | PyErr_SetFromErrno(PyExc_OSError); 392 | return NULL; 393 | } 394 | if(option == PR_GET_NAME) { 395 | return Py_BuildValue("s", name); 396 | } 397 | break; 398 | #if defined(PR_GET_PTRACER) && (PR_GET_PTRACER == NOT_SET) 399 | case(PR_GET_PTRACER): 400 | if(__cached_ptracer == NOT_SET) 401 | return PyInt_FromLong(getppid()); 402 | return PyInt_FromLong(__cached_ptracer); 403 | #endif 404 | #ifdef PR_MCE_KILL 405 | case(PR_SET_MCE_KILL): 406 | result = prctl(option, PR_MCE_KILL_SET, arg, 0, 0); 407 | if(result < 0) { 408 | PyErr_SetFromErrno(PyExc_OSError); 409 | return NULL; 410 | } 411 | break; 412 | #endif 413 | default: 414 | PyErr_SetString(PyExc_ValueError, "Unknown prctl option"); 415 | return NULL; 416 | } 417 | 418 | /* None is returned by default */ 419 | Py_RETURN_NONE; 420 | } 421 | 422 | #if PY_MAJOR_VERSION < 3 423 | #define _Py_GetArgcArgv Py_GetArgcArgv 424 | #else 425 | /* In python 3, Py_GetArgcArgv doesn't actually return the real argv, but an 426 | * encoded copy of it. We try to find the real one by going back from the start 427 | * of environ. 428 | */ 429 | static char * encode(wchar_t *wstr) { 430 | PyObject *unicodestr = NULL, *bytesstr = NULL; 431 | char *str = NULL; 432 | 433 | unicodestr = PyUnicode_FromWideChar(wstr, -1); 434 | if(!unicodestr) { 435 | PyErr_Clear(); 436 | return NULL; 437 | } 438 | 439 | bytesstr = PyUnicode_AsEncodedString(unicodestr, PyUnicode_GetDefaultEncoding(), "strict"); 440 | if(!bytesstr) { 441 | PyErr_Clear(); 442 | Py_XDECREF(unicodestr); 443 | return NULL; 444 | } 445 | 446 | str = PyBytes_AsString(bytesstr); 447 | Py_XDECREF(unicodestr); 448 | Py_XDECREF(bytesstr); 449 | return str; 450 | } 451 | 452 | static int _Py_GetArgcArgv(int* argc, char ***argv) { 453 | int i = 0; 454 | wchar_t **argv_w; 455 | char **buf = NULL , *arg0 = NULL, *ptr = 0, *limit = NULL; 456 | 457 | Py_GetArgcArgv(argc, &argv_w); 458 | if (*argc < 1 || argv_w == NULL) { 459 | return 0; 460 | } 461 | 462 | buf = (char **)malloc((*argc + 1) * sizeof(char *)); 463 | buf[*argc] = NULL; 464 | 465 | /* Walk back from environ until you find argc-1 null-terminated strings. */ 466 | ptr = environ[0] - 1; 467 | limit = ptr - 8192; 468 | for(i=*argc-1; i >= 1; --i) { 469 | ptr--; 470 | while (*ptr && ptr-- > limit); 471 | if (ptr <= limit) { 472 | free(buf); 473 | return 0; 474 | } 475 | buf[i] = (ptr + 1); 476 | } 477 | 478 | /* Now try to find argv[0] */ 479 | arg0 = encode(argv_w[0]); 480 | if(!arg0) { 481 | free(buf); 482 | return 0; 483 | } 484 | ptr -= strlen(arg0); 485 | if(strcmp(ptr, arg0)) { 486 | free(buf); 487 | return 0; 488 | } 489 | 490 | buf[0] = ptr; 491 | *argv = buf; 492 | return 1; 493 | } 494 | #endif 495 | 496 | static PyObject * 497 | prctl_set_proctitle(PyObject *self, PyObject *args) 498 | { 499 | int argc = 0; 500 | char *title; 501 | static size_t len = 0; 502 | static char **argv; 503 | 504 | if(!PyArg_ParseTuple(args, "s", &title)) { 505 | return NULL; 506 | } 507 | if(argv == NULL) { 508 | _Py_GetArgcArgv(&argc, &argv); 509 | if(argc > 0) { 510 | len = (size_t)(argv[argc-1]) + strlen(argv[argc-1]) - (size_t)(argv[0]); 511 | } 512 | } 513 | 514 | if(len <= 0) { 515 | PyErr_SetString(PyExc_RuntimeError, "Failed to locate argc/argv"); 516 | return NULL; 517 | } 518 | strncpy(argv[0], title, len); 519 | if(strlen(title) < len) 520 | memset(argv[0] + strlen(title), 0, len - strlen(title)); 521 | Py_RETURN_NONE; 522 | } 523 | 524 | /* TODO: Add a getter? */ 525 | 526 | static PyObject * prctl_get_caps_flag(PyObject *list, cap_t caps, int flag) { 527 | int i; 528 | PyObject *ret, *item, *val; 529 | cap_flag_value_t value; 530 | 531 | if(list && !PySequence_Check(list)) { 532 | PyErr_SetString(PyExc_TypeError, "A sequence of integers is required"); 533 | return NULL; 534 | } 535 | ret = PyDict_New(); 536 | if(!list) 537 | return ret; 538 | for(i=0; i < PyList_Size(list); i++) { 539 | item = PyList_GetItem(list, i); 540 | if(!PyInt_Check(item)) { 541 | PyErr_SetString(PyExc_TypeError, "A sequence of integers is required"); 542 | return ret; /* Return the list so it can be freed */ 543 | } 544 | if(cap_get_flag(caps, PyInt_AsLong(item), flag, &value) == -1) { 545 | PyErr_SetFromErrno(PyExc_OSError); 546 | return ret; 547 | } 548 | val = PyBool_FromLong(value); 549 | PyDict_SetItem(ret, item, val); 550 | Py_XDECREF(val); 551 | } 552 | return ret; 553 | } 554 | 555 | static int prctl_set_caps_flag(PyObject *list, cap_t caps, int flag, cap_flag_value_t value) { 556 | int i; 557 | cap_value_t cap; 558 | PyObject *item; 559 | 560 | if(list && !PySequence_Check(list)) { 561 | PyErr_SetString(PyExc_TypeError, "A sequence of integers is required"); 562 | return 0; 563 | } 564 | if(!list) 565 | return 1; 566 | 567 | for(i=0; i < PyList_Size(list); i++) { 568 | item = PyList_GetItem(list, i); 569 | if(!PyInt_Check(item)) { 570 | PyErr_SetString(PyExc_TypeError, "A sequence of integers is required"); 571 | return 0; 572 | } 573 | cap = PyInt_AsLong(item); 574 | if(cap_set_flag(caps, flag, 1, &cap, value) == -1) { 575 | PyErr_SetFromErrno(PyExc_OSError); 576 | return 0; 577 | } 578 | } 579 | return 1; 580 | } 581 | 582 | static PyObject * prctl_get_caps(PyObject *self, PyObject *args) 583 | { 584 | PyObject *effective = NULL; 585 | PyObject *permitted = NULL; 586 | PyObject *inheritable = NULL; 587 | PyObject *effective_ = NULL; 588 | PyObject *permitted_ = NULL; 589 | PyObject *inheritable_ = NULL; 590 | PyObject *ret = NULL; 591 | PyObject *key = NULL; 592 | cap_t caps = NULL; 593 | 594 | if(!PyArg_ParseTuple(args, "O|OO", &effective, &permitted, &inheritable)) { 595 | return NULL; 596 | } 597 | caps = cap_get_proc(); 598 | if(!caps) { 599 | PyErr_SetFromErrno(PyExc_OSError); 600 | return NULL; 601 | } 602 | effective_ = prctl_get_caps_flag(effective, caps, CAP_EFFECTIVE); 603 | if(PyErr_Occurred()) goto error; 604 | permitted_ = prctl_get_caps_flag(permitted, caps, CAP_PERMITTED); 605 | if(PyErr_Occurred()) goto error; 606 | inheritable_ = prctl_get_caps_flag(inheritable, caps, CAP_INHERITABLE); 607 | if(PyErr_Occurred()) goto error; 608 | 609 | /* Now build the dict */ 610 | ret = PyDict_New(); 611 | key = PyInt_FromLong(CAP_EFFECTIVE); 612 | PyDict_SetItem(ret, key, effective_); 613 | Py_XDECREF(key); 614 | key = PyInt_FromLong(CAP_PERMITTED); 615 | PyDict_SetItem(ret, key, permitted_); 616 | Py_XDECREF(key); 617 | key = PyInt_FromLong(CAP_INHERITABLE); 618 | PyDict_SetItem(ret, key, inheritable_); 619 | Py_XDECREF(key); 620 | 621 | error: 622 | cap_free(caps); 623 | Py_XDECREF(effective_); 624 | Py_XDECREF(permitted_); 625 | Py_XDECREF(inheritable_); 626 | 627 | return ret; 628 | } 629 | 630 | static PyObject * prctl_set_caps(PyObject *self, PyObject *args) 631 | { 632 | PyObject *effective_set = NULL; 633 | PyObject *permitted_set = NULL; 634 | PyObject *inheritable_set = NULL; 635 | PyObject *effective_clear = NULL; 636 | PyObject *permitted_clear = NULL; 637 | PyObject *inheritable_clear = NULL; 638 | cap_t caps = NULL; 639 | 640 | if(!PyArg_ParseTuple(args, "O|OOOOO", &effective_set, &permitted_set, &inheritable_set, 641 | &effective_clear, &permitted_clear, &inheritable_clear)) { 642 | return NULL; 643 | } 644 | caps = cap_get_proc(); 645 | if(!caps) { 646 | PyErr_SetFromErrno(PyExc_OSError); 647 | return NULL; 648 | } 649 | if(!prctl_set_caps_flag(effective_set, caps, CAP_EFFECTIVE, CAP_SET)) 650 | return NULL; 651 | if(!prctl_set_caps_flag(permitted_set, caps, CAP_PERMITTED, CAP_SET)) 652 | return NULL; 653 | if(!prctl_set_caps_flag(inheritable_set, caps, CAP_INHERITABLE, CAP_SET)) 654 | return NULL; 655 | if(!prctl_set_caps_flag(effective_clear, caps, CAP_EFFECTIVE, CAP_CLEAR)) 656 | return NULL; 657 | if(!prctl_set_caps_flag(permitted_clear, caps, CAP_PERMITTED, CAP_CLEAR)) 658 | return NULL; 659 | if(!prctl_set_caps_flag(inheritable_clear, caps, CAP_INHERITABLE, CAP_CLEAR)) 660 | return NULL; 661 | 662 | if(cap_set_proc(caps) == -1) { 663 | PyErr_SetFromErrno(PyExc_OSError); 664 | return NULL; 665 | } 666 | 667 | Py_RETURN_NONE; 668 | } 669 | 670 | #ifdef cap_to_name 671 | static PyObject * prctl_cap_to_name(PyObject *self, PyObject *args) { 672 | cap_value_t cap; 673 | char *name; 674 | PyObject *ret; 675 | 676 | if(!PyArg_ParseTuple(args, "i", &cap)){ 677 | return NULL; 678 | } 679 | name = cap_to_name(cap); 680 | if(!name) { 681 | PyErr_SetFromErrno(PyExc_OSError); 682 | return NULL; 683 | } 684 | ret = Py_BuildValue("s", name+4); /* Exclude the cap_ prefix */ 685 | cap_free(name); 686 | return ret; 687 | } 688 | #endif 689 | 690 | static PyMethodDef PrctlMethods[] = { 691 | {"get_caps", prctl_get_caps, METH_VARARGS, "Get process capabilities"}, 692 | {"set_caps", prctl_set_caps, METH_VARARGS, "Set process capabilities"}, 693 | #ifdef cap_to_name 694 | {"cap_to_name", prctl_cap_to_name, METH_VARARGS, "Convert capability number to name"}, 695 | #endif 696 | {"prctl", prctl_prctl, METH_VARARGS, "Call prctl"}, 697 | {"set_proctitle", prctl_set_proctitle, METH_VARARGS, "Set the process title"}, 698 | {NULL, NULL, 0, NULL} /* Sentinel */ 699 | }; 700 | 701 | #if PY_MAJOR_VERSION >= 3 702 | static struct PyModuleDef prctlmodule = { 703 | PyModuleDef_HEAD_INIT, 704 | "_prctl", 705 | NULL, 706 | -1, 707 | PrctlMethods 708 | }; 709 | #endif 710 | 711 | /* These macros avoid tediously repeating a name 2 or 4 times */ 712 | #define namedconstant(x) PyModule_AddIntConstant(_prctl, #x, x) 713 | #define namedattribute(x) do{ \ 714 | PyModule_AddIntConstant(_prctl, "PR_GET_" #x, PR_GET_ ## x); \ 715 | PyModule_AddIntConstant(_prctl, "PR_SET_" #x, PR_SET_ ## x); \ 716 | } while(0) 717 | 718 | PyMODINIT_FUNC 719 | #if PY_MAJOR_VERSION < 3 720 | init_prctl(void) 721 | #else 722 | PyInit__prctl(void) 723 | #endif 724 | { 725 | #if PY_MAJOR_VERSION < 3 726 | PyObject *_prctl = Py_InitModule("_prctl", PrctlMethods); 727 | #else 728 | PyObject *_prctl = PyModule_Create(&prctlmodule); 729 | #endif 730 | /* Add the PR_* constants */ 731 | #ifdef PR_CAPBSET_READ 732 | namedconstant(PR_CAPBSET_READ); 733 | namedconstant(PR_CAPBSET_DROP); 734 | #endif 735 | #ifdef PR_SET_CHILD_SUBREAPER 736 | namedattribute(CHILD_SUBREAPER); 737 | #endif 738 | namedattribute(DUMPABLE); 739 | namedattribute(ENDIAN); 740 | namedconstant(PR_ENDIAN_BIG); 741 | namedconstant(PR_ENDIAN_LITTLE); 742 | namedconstant(PR_ENDIAN_PPC_LITTLE); 743 | namedattribute(FPEMU); 744 | namedconstant(PR_FPEMU_NOPRINT); 745 | namedconstant(PR_FPEMU_SIGFPE); 746 | namedattribute(FPEXC); 747 | namedconstant(PR_FP_EXC_SW_ENABLE); 748 | namedconstant(PR_FP_EXC_DIV); 749 | namedconstant(PR_FP_EXC_OVF); 750 | namedconstant(PR_FP_EXC_UND); 751 | namedconstant(PR_FP_EXC_RES); 752 | namedconstant(PR_FP_EXC_INV); 753 | namedconstant(PR_FP_EXC_DISABLED); 754 | namedconstant(PR_FP_EXC_NONRECOV); 755 | namedconstant(PR_FP_EXC_ASYNC); 756 | namedconstant(PR_FP_EXC_PRECISE); 757 | #ifdef PR_SET_FP_MODE 758 | namedattribute(FP_MODE); 759 | namedconstant(PR_FP_MODE_FR); 760 | namedconstant(PR_FP_MODE_FRE); 761 | #endif 762 | #ifdef PR_SET_IO_FLUSHER 763 | namedattribute(IO_FLUSHER); 764 | #endif 765 | namedattribute(KEEPCAPS); 766 | #ifdef PR_MCE_KILL 767 | namedattribute(MCE_KILL); 768 | namedconstant(PR_MCE_KILL_DEFAULT); 769 | namedconstant(PR_MCE_KILL_EARLY); 770 | namedconstant(PR_MCE_KILL_LATE); 771 | #endif 772 | #ifdef PR_MPX_ENABLE_MANAGEMENT 773 | namedconstant(PR_MPX_ENABLE_MANAGEMENT); 774 | namedconstant(PR_MPX_DISABLE_MANAGEMENT); 775 | #endif 776 | namedattribute(NAME); 777 | #ifdef PR_SET_NO_NEW_PRIVS 778 | namedattribute(NO_NEW_PRIVS); 779 | #endif 780 | #ifdef PR_PAC_RESET_KEYS 781 | namedconstant(PR_PAC_RESET_KEYS); 782 | namedconstant(PR_PAC_APIAKEY); 783 | namedconstant(PR_PAC_APIBKEY); 784 | namedconstant(PR_PAC_APDAKEY); 785 | namedconstant(PR_PAC_APDBKEY); 786 | namedconstant(PR_PAC_APGAKEY); 787 | #endif 788 | namedattribute(PDEATHSIG); 789 | #ifdef PR_SET_PTRACER 790 | namedattribute(PTRACER); 791 | #endif 792 | #ifdef PR_SET_PTRACER_ANY 793 | namedconstant(PR_SET_PTRACER_ANY); 794 | #endif 795 | #ifdef PR_GET_SECCOMP 796 | namedattribute(SECCOMP); 797 | #endif 798 | #ifdef PR_GET_SECUREBITS 799 | namedattribute(SECUREBITS); 800 | #endif 801 | #ifdef PR_SET_SPECULATION_CTRL 802 | namedattribute(SPECULATION_CTRL); 803 | namedconstant(PR_SPEC_STORE_BYPASS); 804 | #ifdef PR_SPEC_INDIRECT_BRANCH 805 | namedconstant(PR_SPEC_INDIRECT_BRANCH); 806 | #endif 807 | namedconstant(PR_SPEC_PRCTL); 808 | namedconstant(PR_SPEC_ENABLE); 809 | namedconstant(PR_SPEC_DISABLE); 810 | namedconstant(PR_SPEC_FORCE_DISABLE); 811 | #ifdef PR_SPEC_DISABLE_NOEXEC 812 | namedconstant(PR_SPEC_DISABLE_NOEXEC); 813 | #endif 814 | #endif 815 | #ifdef PR_TASK_PERF_EVENTS_DISABLE 816 | namedconstant(PR_TASK_PERF_EVENTS_DISABLE); 817 | namedconstant(PR_TASK_PERF_EVENTS_ENABLE); 818 | #endif 819 | #ifdef PR_SET_THP_DISABLE 820 | namedattribute(THP_DISABLE); 821 | #endif 822 | #ifdef PR_GET_TID_ADDRESS 823 | namedconstant(PR_GET_TID_ADDRESS); 824 | #endif 825 | #ifdef PR_GET_TIMERSLACK 826 | namedattribute(TIMERSLACK); 827 | #endif 828 | namedattribute(TIMING); 829 | namedconstant(PR_TIMING_STATISTICAL); 830 | namedconstant(PR_TIMING_TIMESTAMP); 831 | #ifdef PR_SET_TSC 832 | namedattribute(TSC); 833 | namedconstant(PR_TSC_ENABLE); 834 | namedconstant(PR_TSC_SIGSEGV); 835 | #endif 836 | namedattribute(UNALIGN); 837 | namedconstant(PR_UNALIGN_NOPRINT); 838 | namedconstant(PR_UNALIGN_SIGBUS); 839 | 840 | /* Add the CAP_* constants too */ 841 | namedconstant(CAP_EFFECTIVE); 842 | namedconstant(CAP_PERMITTED); 843 | namedconstant(CAP_INHERITABLE); 844 | namedconstant(CAP_AUDIT_CONTROL); 845 | #ifdef CAP_AUDIT_READ 846 | namedconstant(CAP_AUDIT_READ); 847 | #endif 848 | namedconstant(CAP_AUDIT_WRITE); 849 | #ifdef CAP_BLOCK_SUSPEND 850 | namedconstant(CAP_BLOCK_SUSPEND); 851 | #endif 852 | #ifdef CAP_BPF 853 | namedconstant(CAP_BPF); 854 | #endif 855 | namedconstant(CAP_CHOWN); 856 | namedconstant(CAP_DAC_OVERRIDE); 857 | namedconstant(CAP_DAC_READ_SEARCH); 858 | namedconstant(CAP_FOWNER); 859 | namedconstant(CAP_FSETID); 860 | namedconstant(CAP_IPC_LOCK); 861 | namedconstant(CAP_IPC_OWNER); 862 | namedconstant(CAP_KILL); 863 | namedconstant(CAP_LEASE); 864 | namedconstant(CAP_LINUX_IMMUTABLE); 865 | #ifdef CAP_MAC_OVERRIDE 866 | namedconstant(CAP_MAC_OVERRIDE); 867 | #endif 868 | #ifdef CAP_MAC_ADMIN 869 | namedconstant(CAP_MAC_ADMIN); 870 | #endif 871 | namedconstant(CAP_MKNOD); 872 | namedconstant(CAP_NET_ADMIN); 873 | namedconstant(CAP_NET_BIND_SERVICE); 874 | namedconstant(CAP_NET_BROADCAST); 875 | namedconstant(CAP_NET_RAW); 876 | #ifdef CAP_PERFMON 877 | namedconstant(CAP_PERFMON); 878 | #endif 879 | #ifdef CAP_SETFCAP 880 | namedconstant(CAP_SETFCAP); 881 | #endif 882 | namedconstant(CAP_SETGID); 883 | namedconstant(CAP_SETPCAP); 884 | namedconstant(CAP_SETUID); 885 | namedconstant(CAP_SYS_ADMIN); 886 | namedconstant(CAP_SYS_BOOT); 887 | namedconstant(CAP_SYS_CHROOT); 888 | namedconstant(CAP_SYS_MODULE); 889 | namedconstant(CAP_SYS_NICE); 890 | namedconstant(CAP_SYS_PACCT); 891 | namedconstant(CAP_SYS_PTRACE); 892 | namedconstant(CAP_SYS_RAWIO); 893 | namedconstant(CAP_SYS_RESOURCE); 894 | namedconstant(CAP_SYS_TIME); 895 | namedconstant(CAP_SYS_TTY_CONFIG); 896 | #ifdef CAP_SYSLOG 897 | namedconstant(CAP_SYSLOG); 898 | #endif 899 | #ifdef CAP_WAKE_ALARM 900 | namedconstant(CAP_WAKE_ALARM); 901 | #endif 902 | namedconstant(CAP_LAST_CAP); 903 | /* And the securebits constants */ 904 | namedconstant(SECURE_KEEP_CAPS); 905 | namedconstant(SECURE_NO_SETUID_FIXUP); 906 | namedconstant(SECURE_NOROOT); 907 | namedconstant(SECURE_KEEP_CAPS_LOCKED); 908 | namedconstant(SECURE_NO_SETUID_FIXUP_LOCKED); 909 | namedconstant(SECURE_NOROOT_LOCKED); 910 | namedconstant(SECBIT_KEEP_CAPS); 911 | namedconstant(SECBIT_NO_SETUID_FIXUP); 912 | namedconstant(SECBIT_NOROOT); 913 | namedconstant(SECBIT_KEEP_CAPS_LOCKED); 914 | namedconstant(SECBIT_NO_SETUID_FIXUP_LOCKED); 915 | namedconstant(SECBIT_NOROOT_LOCKED); 916 | #if PY_MAJOR_VERSION >= 3 917 | return _prctl; 918 | #endif 919 | } 920 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Welcome to python-prctl's documentation! 3 | ======================================== 4 | 5 | The linux prctl function allows you to control specific characteristics of a 6 | process' behaviour. Usage of the function is fairly messy though, due to 7 | limitations in C and linux. This module provides a nice non-messy python(ic) 8 | interface. Most of the text in this documentation is based on text from the 9 | linux manpages :manpage:`prctl(2)` and :manpage:`capabilities(7)` 10 | 11 | Besides prctl, this library also wraps libcap for complete capability handling 12 | and allows you to set the process name as seen in ps and top. 13 | 14 | Downloading and installing 15 | ========================== 16 | 17 | Before you try to install python-prctl, you will need to install the following: 18 | 19 | * gcc 20 | * libc development headers 21 | * libcap development headers 22 | 23 | On Debian and Ubuntu, this is done as follows: 24 | 25 | .. code-block:: sh 26 | 27 | $ sudo apt-get install build-essential libcap-dev 28 | 29 | On Fedora and other RPM-based distributions: 30 | 31 | .. code-block:: sh 32 | 33 | $ sudo yum install gcc glibc-devel libcap-devel 34 | 35 | The latest stable version can be installed with distutils: 36 | 37 | .. code-block:: sh 38 | 39 | $ sudo easy_install python-prctl 40 | 41 | The latest development source for python-prctl can be downloaded from `GitHub 42 | `_. Installing is again done with distutils. 43 | 44 | .. code-block:: sh 45 | 46 | $ git clone http://github.com/seveas/python-prctl 47 | $ cd python-prctl 48 | $ python setup.py build 49 | $ sudo python setup.py install 50 | 51 | The prctl module is now ready to use. 52 | 53 | :mod:`prctl` -- Control process attributes 54 | ========================================== 55 | .. module:: prctl 56 | :platform: Linux (2.6.25 or newer) 57 | :synopsis: Control process attributes 58 | .. moduleauthor:: Dennis Kaarsemaker 59 | 60 | .. function:: set_child_subreaper(flag) 61 | 62 | When processes double-fork, they get implicitly re-parented to PID 1. Using 63 | this function, processes can mark themselves as service manager and will 64 | remain parent of any such processes they launch, becoming a sort of sub-init. 65 | They will then be responsible for handling :const:`~signal.SIGCHLD` and 66 | calling :func:`wait` in them. 67 | 68 | This is only available in linux 3.4 and newer 69 | 70 | .. function:: get_child_subreaper() 71 | 72 | Determine whether we are a sub-init. 73 | 74 | This is only available in linux 3.4 and newer 75 | 76 | .. function:: set_dumpable(flag) 77 | 78 | Set the state of the flag determining whether core dumps are produced for 79 | this process upon delivery of a signal whose default behavior is to produce a 80 | core dump. (Normally this flag is set for a process by default, but it is 81 | cleared when a set-user-ID or set-group-ID program is executed and also by 82 | various system calls that manipulate process UIDs and GIDs). 83 | 84 | .. function:: get_dumpable() 85 | 86 | Return the state of the dumpable flag. 87 | 88 | .. function:: set_endian(endianness) 89 | 90 | Set the endian-ness of the calling process. Valid values are 91 | :const:`~prctl.ENDIAN_BIG`, :const:`~prctl.ENDIAN_LITTLE` and 92 | :const:`~prctl.ENDIAN_PPC_LITTLE` (PowerPC pseudo little endian). 93 | 94 | .. note:: 95 | 96 | This function only works on PowerPC systems. An :exc:`OSError` is raised 97 | when called on other systems. 98 | 99 | .. function:: get_endian() 100 | 101 | Return the endian-ness of the calling process, see :func:`set_endian`. 102 | 103 | .. function:: set_fpemu(flag) 104 | 105 | Set floating-point emulation control flag. Pass :const:`~prctl.FPEMU_NOPRINT` 106 | to silently emulate fp operations accesses, or :const:`~prctl.FPEMU_SIGFPE` 107 | to not emulate fp operations and send :const:`~signal.SIGFPE` instead. 108 | 109 | .. note:: 110 | 111 | This function only works on ia64 systems. An :exc:`OSError` is raised 112 | when called on other systems. 113 | 114 | .. function:: get_fpemu() 115 | 116 | Get floating-point emulation control flag. See :func:`set_fpemu`. 117 | 118 | .. function:: set_fpexc(mode) 119 | 120 | Set floating-point exception mode. Pass :const:`FP_EXC_SW_ENABLE` to use 121 | FPEXC for FP exception, :const:`FP_EXC_DIV` for floating-point divide by 122 | zero, :const:`FP_EXC_OVF` for floating-point overflow, :const:`FP_EXC_UND` 123 | for floating-point underflow, :const:`FP_EXC_RES` for floating-point inexact 124 | result, :const:`FP_EXC_INV` for floating-point invalid operation, 125 | :const:`FP_EXC_DISABLED` for FP exceptions disabled, :const:`FP_EXC_NONRECOV` 126 | for async non-recoverable exception mode, :const:`FP_EXC_ASYNC` for async 127 | recoverable exception mode, :const:`FP_EXC_PRECISE` for precise exception 128 | mode. Modes can be combined with the :const:`|` operator. 129 | 130 | .. note:: 131 | 132 | This function only works on PowerPC systems. An :exc:`OSError` is raised 133 | when called on other systems. 134 | 135 | .. function:: get_fpexc() 136 | 137 | Return the floating-point exception mode as a bitmap of enabled modes. See 138 | :func:`set_fpexc`. 139 | 140 | .. function:: set_io_flusher(is_flusher) 141 | 142 | Put the process in :const:`IO_FLUSHER` state, which, which allows it special 143 | treatment to make progress when allocating memory. This is used by process 144 | involved in the block layer or filesystem i/o path, such as fuse daemons or 145 | scsi device emulation daemons. 146 | 147 | This is only available in linux 5.6 and newer 148 | 149 | .. function:: get_io_flusher() 150 | 151 | Return the :const:`IO_FLUSHER` state of the process. 152 | 153 | This is only available in linux 5.6 and newer 154 | 155 | .. function:: set_keepcaps(flag) 156 | 157 | Set the state of the thread's "keep capabilities" flag, which determines 158 | whether the thread's effective and permitted capability sets are cleared 159 | when a change is made to the thread's user IDs such that the thread's real 160 | UID, effective UID, and saved set-user-ID all become non-zero when at least 161 | one of them previously had the value 0. (By default, these credential sets 162 | are cleared). This value will be reset to :const:`False` on subsequent calls 163 | to :func:`execve`. 164 | 165 | 166 | .. function:: get_keepcaps() 167 | 168 | Return the current state of the calling thread's "keep capabilities" flag. 169 | 170 | .. function:: set_mce_kill(policy) 171 | 172 | Set the machine check memory corruption kill policy for the current thread. 173 | The policy can be early kill (:const:`MCE_KILL_EARLY`), late kill 174 | (:const:`MCE_KILL_LATE`), or the system-wide default 175 | (:const:`MCE_KILL_DEFAULT`). Early kill means that the task receives a 176 | :const:`SIGBUS` signal as soon as hardware memory corruption is detected 177 | inside its address space. In late kill mode, the process is only killed when 178 | it accesses a corrupted page. The policy is inherited by children. use the 179 | system-wide default. The system-wide default is defined by 180 | :file:`/proc/sys/vm/memory_failure_early_kill` 181 | 182 | This is only available in linux 2.6.32 and newer 183 | 184 | .. function:: get_mce_kill() 185 | 186 | Return the current per-process machine check kill policy. 187 | 188 | This is only available in linux 2.6.32 and newer 189 | 190 | .. function:: pr_mpx_enable_management() 191 | 192 | .. function:: pr_mpx_disable_management() 193 | 194 | Enable or disable intel memory protection extensions. See :manpage:`prctl(2)` 195 | for details and limitations. 196 | 197 | This is only available in linux 3.19 and newer, but no longer available in 198 | linux 5.4 and newer. 199 | 200 | .. function:: set_name(name) 201 | 202 | Set the process name for the calling process, the name can be up to 16 bytes 203 | long. This name is displayed in the output of :command:`ps` and 204 | :command:`top`. The initial value is the name of the executable. For python 205 | applications this will likely be :command:`python`. 206 | 207 | .. note:: 208 | Use :func:`set_proctitle` to set the name that's shown with :func:`ps aux` 209 | and :func:`top -c` 210 | 211 | .. function:: get_name() 212 | 213 | Return the (first 16 bytes of) the name for the calling process. 214 | 215 | .. function:: set_no_new_privs() 216 | 217 | Once this is set, no operation that can grant new privileges (such as 218 | execve'ing a setuid binary) will actually grant new privileges. 219 | 220 | This is only available in linux 3.5 and newer 221 | 222 | .. function:: get_no_new_privs() 223 | 224 | Get whether new privileges can be granted to this pid. 225 | 226 | This is only available in linux 3.5 and newer 227 | 228 | .. function:: pac_reset_keys(keys) 229 | 230 | Securely reset the thread's pointer authentication keys to fresh random 231 | values generated by the kernel. The keys must be a logical or of any of the 232 | keys you want to reset, or 0 to reset all keys. The available keys are 233 | :const:`PR_PAC_APIAKEY`, :const:`PR_PAC_APIBKEY`, :const:`PR_PAC_APDAKEY`, 234 | :const:`PR_PAC_APDBKEY` and :const:`PR_PAC_APGAKEY`. 235 | 236 | For more information, see the kernel source file 237 | Documentation/arm64/pointer-authentication.rst 238 | 239 | This is only available in linux 5.0 and newer 240 | 241 | .. note:: 242 | This function only works on arm64 systems. An :exc:`OSError` is raised 243 | when called on other systems. 244 | 245 | .. function:: set_proctitle(title) 246 | 247 | Set the process name for the calling process by overwriting the C-level 248 | :c:data:`**argv` variable. The original value of :c:data:`**argv` is then no 249 | longer visible. in :command:`ps`, :command:`proc`, or 250 | :file:`/proc/self/cmdline`. 251 | 252 | Names longer that what fits in :c:data:`**argv` will be silently truncated. To 253 | set a longer title, make your application accept bogus arguments and call the 254 | application with these arguments. 255 | 256 | .. note:: 257 | 258 | This function is not actually part of the standard :func:`pctrl` syscall, 259 | but was added because it nicely complements :func:`set_name`. 260 | 261 | .. function:: set_pdeathsig(signal) 262 | 263 | Set the parent death signal of the calling process (either a valid signal 264 | value from the :mod:`signal` module, or 0 to clear). This is the signal that 265 | the calling process will get when its parent dies. This value is cleared for 266 | the child of a :func:`fork`. 267 | 268 | .. warning:: 269 | 270 | The "parent" in this case is considered to be the thread that created 271 | this process. In other words, the signal will be sent when that 272 | thread terminates (via, for example, :func:`pthread_exit()`), rather than after all 273 | of the threads in the parent process terminate. 274 | 275 | .. function:: get_pdeathsig() 276 | 277 | Return the current value of the parent process death signal. See 278 | :func:`set_pdeathsig`. 279 | 280 | .. function:: set_ptracer(pid) 281 | 282 | Sets the top of the process tree that is allowed to use :func:`PTRACE` on the 283 | calling process, assuming other requirements are met (matching uid, wasn't 284 | setuid, etc). Use pid 0 to disallow all processes. For more details, see 285 | :file:`/etc/sysctl.d/10-ptrace.conf`. 286 | 287 | This is only available in linux 3.4 and newer 288 | 289 | .. function:: get_ptracer(pid) 290 | 291 | Returns the top of the process tree that is allowed to use :func:`PTRACE` on 292 | the calling process. See :func:`set_ptracer`. 293 | 294 | This is only available in linux 3.4 and newer 295 | 296 | .. function:: set_seccomp(mode) 297 | 298 | Set the secure computing mode for the calling thread. In the current 299 | implementation, mode must be :const:`True`. After the secure computing mode 300 | has been set to :const:`True`, the only system calls that the thread is 301 | permitted to make are :func:`read`, :func:`write`, :func:`_exit`, and 302 | :func:`sigreturn`. Other system calls result in the delivery of a 303 | :const:`~signal.SIGKILL` signal. Secure computing mode is useful for 304 | number-crunching applications that may need to execute untrusted byte code, 305 | perhaps obtained by reading from a pipe or socket. This operation is only 306 | available if the kernel is configured with :const:`CONFIG_SECCOMP` enabled. 307 | 308 | .. function:: get_seccomp() 309 | 310 | Return the secure computing mode of the calling thread. Not very useful for 311 | the current implementation, but may be useful for other possible future 312 | modes: if the caller is not in secure computing mode, this operation returns 313 | False; if the caller is in secure computing mode, then the :func:`prctl` call 314 | will cause a :const:`~signal.SIGKILL` signal to be sent to the process. This 315 | operation is only available if the kernel is configured with 316 | :const:`CONFIG_SECCOMP` enabled. 317 | 318 | .. function:: set_speculation_ctrl(feature, value) 319 | 320 | Sets the state of a speculation misfeature (:const:`SPEC_STORE_BYPASS` or 321 | :const:`SPEC_INDIRECT_BRANCH`). The value is one of :const:`PR_SPEC_ENABLE` 322 | to enable the feature, :const:`PR_SPEC_DISABLE` to disable it, 323 | :const:`PR_SPEC_FORCE_DISABLE` to disable it permanently for the thread and 324 | :const:`PR_SPEC_DISABLE_NOEXEC` to disable it until the next :func:`execve`. 325 | 326 | This is only available in linux 4.17 and newer 327 | 328 | .. function:: get_speculation_ctrl(feature) 329 | 330 | Returns the state of a speculation misfeature (:const:`SPEC_STORE_BYPASS` or 331 | :const:`SPEC_INDIRECT_BRANCH`). The value is one of the values that can be 332 | set by :func:`pr_set_speculation_ctrl`, possibly logically OR'ed with 333 | const:`PR_SPEC_PRCTL` to indicate that the value can be controlled er thread 334 | by that function. If all bits are 0, the CPU is not affected by the 335 | misfeature. 336 | 337 | This is only available in linux 4.17 and newer 338 | 339 | .. function:: task_perf_events_disable() 340 | .. function:: task_perf_events_enable() 341 | 342 | Disable or enable all performance counters attached to the calling process, 343 | regardless of whether the counters were created by this process or another 344 | process. Performance counters created by the calling process for other 345 | processes are unaffected. 346 | 347 | .. function:: set_thp_disable(is_disabled) 348 | 349 | Disable transparent huge ages for the current process. This flag is inhereted 350 | by child process and preserved across execve. 351 | 352 | This is only available in linux 3.15 and newer 353 | 354 | .. function:: get_thp_disable() 355 | 356 | Return whether transparent huge pages are disabled for the current process. 357 | 358 | This is only available in linux 3.15 and newer 359 | 360 | .. function:: get_tid_address() 361 | 362 | Allows the process to obtain its own `clear_tid_address`, used when 363 | checkpointing/restoring processes. 364 | 365 | This is only available in linux 3.5 and newer 366 | 367 | .. function:: set_timerslack() 368 | 369 | Control the default "rounding" in nanoseconds that is used by :func:`select`, 370 | :func:`poll` and friends. 371 | 372 | The default value of the slack is 50 microseconds; this is significantly less 373 | than the kernels average timing error but still allows the kernel to group 374 | timers somewhat to preserve power behavior. 375 | 376 | This is only available in linux 2.6.28 and newer 377 | 378 | .. function:: get_timerslack(value) 379 | 380 | Return the current timing slack, see :func:`get_timing_slack` 381 | 382 | This is only available in linux 2.6.28 and newer 383 | 384 | .. function:: set_timing(flag) 385 | 386 | Set whether to use (normal, traditional) statistical process timing or 387 | accurate timestamp based process timing, by passing 388 | :const:`~prctl.TIMING_STATISTICAL` or :const:`~prctl.PR_TIMING_TIMESTAMP`. 389 | :const:`~prctl.TIMING_TIMESTAMP` is not currently implemented (attempting to 390 | set this mode will cause an :exc:`OSError`). 391 | 392 | .. function:: get_timing() 393 | 394 | Return which process timing method is currently in use. 395 | 396 | .. function:: set_tsc(flag) 397 | 398 | Set the state of the flag determining whether the timestamp counter can be 399 | read by the process. Pass :const:`~prctl.TSC_ENABLE` to allow it to be read, 400 | or :const:`~prctl.TSC_SIGSEGV` to generate a :const:`SIGSEGV` when the 401 | process tries to read the timestamp counter. 402 | 403 | .. note:: 404 | 405 | This function only works on x86 systems. An :exc:`OSError` is raised when 406 | called on other systems. 407 | 408 | .. function:: get_tsc() 409 | 410 | Return the state of the flag determining whether the timestamp counter can be 411 | read, see :func:`set_tsc`. 412 | 413 | .. function:: set_unalign(flag) 414 | 415 | Set unaligned access control flag. Pass :const:`~prctl.UNALIGN_NOPRINT` to 416 | silently fix up unaligned user accesses, or :const:`~prctl.UNALIGN_SIGBUS` to 417 | generate :const:`SIGBUS` on unaligned user access. 418 | 419 | .. note:: 420 | 421 | This function only works on ia64, parisc, PowerPC and Alpha systems. An 422 | :exc:`OSError` is raised when called on other systems. 423 | 424 | .. function:: get_unalign 425 | 426 | Return unaligned access control bits, see :func:`set_unalign`. 427 | 428 | .. function:: set_securebits(bitmap) 429 | 430 | Set the "securebits" flags of the calling thread. 431 | 432 | .. note:: 433 | 434 | It is not recommended to use this function directly, use the 435 | :attr:`~prctl.securebits` object instead. 436 | 437 | .. function:: get_securebits() 438 | 439 | Get the "securebits" flags of the calling thread. 440 | 441 | .. note:: 442 | 443 | As with :func:`set_securebits`, it is not recommended to use this function 444 | directly, use the :attr:`~prctl.securebits` object instead. 445 | 446 | .. function:: capbset_read(capability) 447 | 448 | Return whether the specified capability is in the calling thread's capability 449 | bounding set. The capability bounding set dictates whether the process can 450 | receive the capability through a file's permitted capability set on a 451 | subsequent call to :func:`execve`. An :exc:`OSError` will be raised when an 452 | invalid capability is specified. 453 | 454 | .. note:: 455 | 456 | It is not recommended to use this function directly, use the 457 | :attr:`~prctl.capbset` object instead. 458 | 459 | .. function:: capbset_drop(capability) 460 | 461 | If the calling thread has the :const:`~prctl.CAP_SETPCAP` capability, then 462 | drop the specified capability specified by from the calling thread's 463 | capability bounding set. Any children of the calling thread will inherit the 464 | newly reduced bounding set. 465 | 466 | An :exc:`OSError` will be raised if the calling thread does not have the 467 | :const:`~prctl.CAP_SETPCAP` capability or when the specified capability is 468 | invalid or when capabilities are not enabled in the kernel. 469 | 470 | .. note:: 471 | 472 | As with :func:`capbset_read`, it is not recommended to use this function 473 | directly, use the :attr:`~prctl.capbset` object instead. 474 | 475 | Capabilities and the capability bounding set 476 | ============================================ 477 | 478 | For the purpose of performing permission checks, traditional Unix 479 | implementations distinguish two categories of processes: privileged processes 480 | (whose effective user ID is 0, referred to as superuser or root), and 481 | unprivileged processes (whose effective UID is non-zero). Privileged processes 482 | bypass all kernel permission checks, while unprivileged processes are subject 483 | to full permission checking based on the process's credentials (usually: 484 | effective UID, effective GID, and supplementary group list). 485 | 486 | Starting with kernel 2.2, Linux divides the privileges traditionally associated 487 | with superuser into distinct units, known as capabilities, which can be 488 | independently enabled and disabled. Capabilities are a per-thread attribute. 489 | 490 | Each thread has three capability sets containing zero or more of the 491 | capabilities described below 492 | 493 | Permitted (the :attr:`~prctl.cap_permitted` object): 494 | This is a limiting superset for the effective capabilities that the thread 495 | may assume. It is also a limiting superset for the capabilities that may be 496 | added to the inheritable set by a thread that does not have the 497 | :attr:`setpcap` capability in its effective set. 498 | 499 | If a thread drops a capability from its permitted set, it can never 500 | re-acquire that capability (unless it :func:`execve` s either a 501 | set-user-ID-root program, or a program whose associated file capabilities 502 | grant that capability). 503 | 504 | Inheritable (the :attr:`~prctl.cap_inheritable` object): 505 | This is a set of capabilities preserved across an :func:`execve`. It provides 506 | a mechanism for a process to assign capabilities to the permitted set of the 507 | new program during an :func:`execve`. 508 | 509 | Effective (the :attr:`~prctl.cap_effective` object): 510 | This is the set of capabilities used by the kernel to perform permission 511 | checks for the thread. 512 | 513 | A child created via :func:`fork` inherits copies of its parent's capability 514 | sets. See below for a discussion of the treatment of capabilities during 515 | :func:`execve`. 516 | 517 | The :attr:`~prctl.capbset` object represents the current capability bounding 518 | set of the process. The capability bounding set dictates whether the process 519 | can receive the capability through a file's permitted capability set on a 520 | subsequent call to :func:`execve`. All attributes of :attr:`~prctl.capbset` are 521 | :const:`True` by default, unless a parent process already removed them from the 522 | bounding set. 523 | 524 | These four objects have a number of attributes, all of which are properties. 525 | For the capability bounding set and the effective capabilities, these can only 526 | be set to :const:`False`, this drops them from the corresponding set. 527 | 528 | All details about capabilities and capability bounding sets can be found in the 529 | :manpage:`capabilities(7)` manpage, on which most text below is based. 530 | 531 | These are the attributes (:class:`set` refers to each of the above objects): 532 | 533 | .. attribute:: set.audit_control 534 | 535 | Enable and disable kernel auditing; change auditing filter rules; retrieve 536 | auditing status and filtering rules. 537 | 538 | .. attribute:: set.audit_read 539 | 540 | Allow reading the audit log via a multicast netlink socket. 541 | 542 | .. attribute:: set.audit_write 543 | 544 | Write records to kernel auditing log. 545 | 546 | .. attribute:: set.block_suspend 547 | 548 | Employ features that can block system suspend (:manpage:`epoll(7)` 549 | :const:`EPOLLWAKEUP`, :file:`/proc/sys/wake_lock`). 550 | 551 | .. attribute:: set.bpf 552 | 553 | Employ privileged BPF operations; see :manpage:`bpf(2)` and 554 | :manpage:`bpf-helpers(7)`. 555 | 556 | .. attribute:: set.chown 557 | 558 | Make arbitrary changes to file UIDs and GIDs (see :manpage:`chown(2)`). 559 | 560 | .. attribute:: set.dac_override 561 | 562 | Bypass file read, write, and execute permission checks. (DAC is an 563 | abbreviation of "discretionary access control".) 564 | 565 | .. attribute:: set.dac_read_search 566 | 567 | Bypass file read permission checks and directory read and execute permission 568 | checks. 569 | 570 | .. attribute:: set.fowner 571 | 572 | * Bypass permission checks on operations that normally require the file 573 | system UID of the process to match the UID of the file (e.g., 574 | :func:`chmod`, :func:`utime`), excluding those operations covered by 575 | :attr:`dac_override` and :attr:`dac_read_search`. 576 | * Set extended file attributes (see :manpage:`chattr(1)`) on arbitrary files. 577 | * Set Access Control Lists (ACLs) on arbitrary files. 578 | * Ignore directory sticky bit on file deletion. 579 | * Specify :const:`O_NOATIME` for arbitrary files in :func:`open` and 580 | :func:`fcntl`. 581 | 582 | .. attribute:: set.fsetid 583 | 584 | Don't clear set-user-ID and set-group-ID permission bits when a file is 585 | modified; set the set-group-ID bit for a file whose GID does not match the 586 | file system or any of the supplementary GIDs of the calling process. 587 | 588 | .. attribute:: set.ipc_lock 589 | 590 | Lock memory (:func:`mlock`, :func:`mlockall`, :func:`mmap`, :func:`shmctl`). 591 | 592 | .. attribute:: set.ipc_owner 593 | 594 | Bypass permission checks for operations on System V IPC objects. 595 | 596 | .. attribute:: set.kill 597 | 598 | Bypass permission checks for sending signals (see :manpage:`kill(2)`). This 599 | includes use of the :func:`ioctl` :const:`KDSIGACCEPT` operation. 600 | 601 | .. attribute:: set.lease 602 | 603 | Establish leases on arbitrary files (see :manpage:`fcntl(2)`). 604 | 605 | .. attribute:: set.linux_immutable 606 | 607 | Set the :const:`FS_APPEND_FL` and :const:`FS_IMMUTABLE_FL` i-node flags (see 608 | :manpage:`chattr(1)`). 609 | 610 | .. attribute:: set.mac_admin 611 | 612 | Allow MAC configuration or state changes. Implemented for the Smack LSM. 613 | 614 | .. attribute:: set.mac_override 615 | 616 | Override Mandatory Access Control (MAC). Implemented for the Smack Linux 617 | Security Module (LSM). 618 | 619 | .. attribute:: set.mknod 620 | 621 | Create special files using :func:`mknod`. 622 | 623 | .. attribute:: set.net_admin 624 | 625 | Perform various network-related operations (e.g., setting privileged socket 626 | options, enabling multicasting, interface configuration, modifying routing 627 | tables). 628 | 629 | .. attribute:: set.net_bind_service 630 | 631 | Bind a socket to Internet domain privileged ports (port numbers less than 632 | 1024). 633 | 634 | .. attribute:: set.net_broadcast 635 | 636 | (Unused) Make socket broadcasts, and listen to multicasts. 637 | 638 | .. attribute:: set.net_raw 639 | 640 | Use :const:`RAW` and :const:`PACKET` sockets. 641 | 642 | .. attribute:: set.perfmon 643 | 644 | Employ various performance-monitoring mechanisms, including 645 | :func:`perf_event_open` and various BPF operations that have performance 646 | implications.. 647 | 648 | .. attribute:: set.setgid 649 | 650 | Make arbitrary manipulations of process GIDs and supplementary GID list; 651 | forge GID when passing socket credentials via Unix domain sockets. 652 | 653 | .. attribute:: set.setfcap 654 | 655 | Set file capabilities. 656 | 657 | .. attribute:: set.setpcap 658 | 659 | If file capabilities are not supported: grant or remove any capability in the 660 | caller's permitted capability set to or from any other process. (This 661 | property of :attr:`setpcap` is not available when the kernel is configured to 662 | support file capabilities, since :attr:`setpcap` has entirely different 663 | semantics for such kernels.) 664 | 665 | If file capabilities are supported: add any capability from the calling 666 | thread's bounding set to its inheritable set; drop capabilities from the 667 | bounding set (via :func:`~prctl.capbset_drop`); make changes to the 668 | securebits flags. 669 | 670 | .. attribute:: set.setuid 671 | 672 | Make arbitrary manipulations of process UIDs (:func:`setuid`, 673 | :func:`setreuid`, :func:`setresuid`, :func:`setfsuid`); make forged UID when 674 | passing socket credentials via Unix domain sockets. 675 | 676 | .. attribute:: set.syslog 677 | 678 | Allow configuring the kernel's syslog (printk behaviour). Before linux 2.6.38 679 | the :attr:`sys_admin` capability was needed for this. 680 | 681 | This is only available in linux 2.6.38 and newer 682 | 683 | .. attribute:: set.sys_admin 684 | 685 | Perform a range of system administration operations, which change per kernel 686 | version. See :manpage:`capabilities(7)` for details. 687 | 688 | .. attribute:: set.sys_boot 689 | 690 | Use :func:`reboot` and :func:`kexec_load`. 691 | 692 | .. attribute:: set.sys_chroot 693 | 694 | Use :func:`chroot`. 695 | 696 | .. attribute:: set.sys_module 697 | 698 | Load and unload kernel modules (see :manpage:`init_module(2)` and 699 | :manpage:`delete_module(2)`). 700 | 701 | .. attribute:: set.sys_nice 702 | 703 | * Raise process nice value (:func:`nice`, :func:`setpriority`) and change the 704 | nice value for arbitrary processes. 705 | * Set real-time scheduling policies for calling process, and set scheduling 706 | policies and priorities for arbitrary processes 707 | (:func:`sched_setscheduler`, :func:`sched_setparam`). 708 | * Set CPU affinity for arbitrary processes (:func:`sched_setaffinity`) 709 | * Set I/O scheduling class and priority for arbitrary processes 710 | (:func:`ioprio_set`). 711 | * Apply :func:`migrate_pages` to arbitrary processes and allow processes to 712 | be migrated to arbitrary nodes. 713 | * Apply :func:`move_pages` to arbitrary processes. 714 | * Use the :const:`MPOL_MF_MOVE_ALL` flag with :func:`mbind` and 715 | :func:`move_pages`. 716 | 717 | .. attribute:: set.sys_pacct 718 | 719 | Use :func:`acct`. 720 | 721 | .. attribute:: set.sys_ptrace 722 | 723 | Trace arbitrary processes using :func:`ptrace`. 724 | 725 | .. attribute:: set.sys_rawio 726 | 727 | Perform a range of privileged i/o operations, which change per kernel 728 | version. See :manpage:`capabilities(7)` for details. 729 | 730 | .. attribute:: set.sys_resource 731 | 732 | Use a set of privileged resources, which change per kernel version. See 733 | :manpage:`capabilities(7)` for details. 734 | 735 | .. attribute:: set.sys_time 736 | 737 | Set system clock (:func:`settimeofday`, :func:`stime`, :func:`adjtimex`); set 738 | real-time (hardware) clock. 739 | 740 | .. attribute:: set.sys_tty_config 741 | 742 | Use :func:`vhangup`. 743 | 744 | .. attribute:: set.wake_alarm 745 | 746 | Allow triggering something that will wake the system. 747 | 748 | This is only available in linux 3.0 and newer 749 | 750 | The four capabilities objects also have two additional methods, to make 751 | dropping many capabilities at the same time easier: 752 | 753 | .. function:: set.drop(cap [, ...]) 754 | 755 | Drop all capabilities given as arguments from the set. 756 | 757 | .. function:: set.limit(cap [, ...]) 758 | 759 | Drop all but the given capabilities from the set. 760 | 761 | These function accept both names of capabilities as given above and the 762 | :data:`CAP_` constants as defined in :file:`capabilities.h`. These constants 763 | are available as :attr:`prctl.CAP_SYS_ADMIN` et cetera. 764 | 765 | Capabilities and :func:`execve` 766 | =============================== 767 | During an :func:`execve`, the kernel calculates the new capabilities of the process 768 | using the following algorithm: 769 | 770 | * P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset) 771 | * P'(effective) = F(effective) ? P'(permitted) : 0 772 | * P'(inheritable) = P(inheritable) [i.e., unchanged] 773 | 774 | Where: 775 | 776 | * P denotes the value of a thread capability set before the :func:`execve` 777 | * P' denotes the value of a capability set after the :func:`execve` 778 | * F denotes a file capability set 779 | * cap_bset is the value of the capability bounding set 780 | 781 | The downside of this is that you need to set file capabilities if you want to 782 | make applications capabilities-friendly via wrappers. For instance, to allow an 783 | http daemon to listen on port 80 without it needing root privileges, you could 784 | do the following: 785 | 786 | .. code-block:: python 787 | 788 | prctl.cap_inheritable.net_bind_service = True 789 | os.setuid(pwd.getpwnam('www-data').pw_uid) 790 | os.execve("/usr/sbin/httpd", ["/usr/sbin/httpd"], os.environ) 791 | 792 | This only works if :file:`/usr/sbin/httpd` has :attr:`CAP_NET_BIND_SOCK` in its 793 | inheritable and effective sets. You can do this with the :command:`setcap` tool 794 | shipped with libcap. 795 | 796 | .. code-block:: sh 797 | 798 | $ sudo setcap cap_net_bind_service=ie /usr/sbin/httpd 799 | $ getcap /usr/sbin/httpd 800 | /usr/sbin/httpd = cap_net_bind_service+ei 801 | 802 | Note that it only sets the capability in the inheritable set, so this 803 | capability is only granted if the program calling execve has it in its 804 | inheritable set too. The effective set of file capabilities does not exist in 805 | linux, it is a single bit that specifies whether capabilities in the permitted 806 | set are automatically raised in the effective set upon :func:`execve`. 807 | 808 | Establishing a capabilities-only environment with securebits 809 | ============================================================ 810 | With a kernel in which file capabilities are enabled, Linux implements a set of 811 | per-thread securebits flags that can be used to disable special handling of 812 | capabilities for UID 0 (root). The securebits flags are inherited by child 813 | processes. During an :func:`execve`, all of the flags are preserved, except 814 | :attr:`keep_caps` which is always cleared. 815 | 816 | These capabilities are available via :func:`get_securebits`, but are easier 817 | accessed via the :attr:`~prctl.securebits` object. This object has attributes 818 | tell you whether specific securebits are set, or unset. 819 | 820 | The following attributes are available: 821 | 822 | .. attribute:: securebits.keep_caps 823 | 824 | Setting this flag allows a thread that has one or more 0 UIDs to retain its 825 | capabilities when it switches all of its UIDs to a non-zero value. If this 826 | flag is not set, then such a UID switch causes the thread to lose all 827 | capabilities. This flag is always cleared on an :func:`execve`. 828 | 829 | .. attribute:: securebits.no_setuid_fixup 830 | 831 | Setting this flag stops the kernel from adjusting capability sets when the 832 | thread's effective and file system UIDs are switched between zero and 833 | non-zero values. (See the subsection Effect of User ID Changes on 834 | Capabilities in :manpage:`capabilities(7)`) 835 | 836 | .. attribute:: securebits.noroot 837 | 838 | If this bit is set, then the kernel does not grant capabilities when a 839 | set-user-ID-root program is executed, or when a process with an effective or 840 | real UID of 0 calls :func:`execve`. (See the subsection Capabilities and 841 | execution of programs by root in :manpage:`capabilities(7)`) 842 | 843 | .. attribute:: securebits.keep_caps_locked 844 | 845 | Like :attr:`keep_caps`, but irreversible 846 | 847 | .. attribute:: securebits.no_setuid_fixup_locked 848 | 849 | Like :attr:`no_setuid_fixup`, but irreversible 850 | 851 | .. attribute:: securebits.noroot_locked 852 | 853 | Like :attr:`noroot`, but irreversible 854 | 855 | :mod:`_prctl` -- Basic C wrapper around prctl 856 | ============================================= 857 | .. module:: _prctl 858 | :platform: Linux (2.6.25 or newer) 859 | :synopsis: Basic wrapper around prctl 860 | .. moduleauthor:: Dennis Kaarsemaker 861 | 862 | This is the lower level C module that wraps the :c:func:`prctl` syscall in a way 863 | that it is easy to call from a python module. It should not be used directly, 864 | applications and other libraries should use the functionality provided by the 865 | :mod:`prctl` module. 866 | 867 | This section of the documentation is meant for people who want to contribute to 868 | python-prctl. 869 | 870 | .. c:function:: static PyObject\* prctl_prctl(PyObject \*self, PyObject \*args) 871 | 872 | This is the :c:func:`prctl` wrapper. It accepts as argument either one or two 873 | :obj:`int` variables or an :obj:`int` and a :obj:`str`. 874 | 875 | The mandatory first int must be one of the :const:`PR_SET_*`, 876 | :const:`PR_GET_*`, or :const:`PR_CAPBSET_*` constants defined in 877 | :file:`sys/prctl.h`. The accepted values of the second argument depend on the 878 | first argument, see :manpage:`prctl(2)`. 879 | 880 | The function validates arguments, calls :c:func:`prctl` in the 881 | argument-specific way and returns the proper value, whether :func:`prctl` 882 | returns it as return value or stores it in one of the parameters. 883 | 884 | .. c:function:: static PyObject\* prctl_set_proctitle(PyObject \*self, PyObject \*args) 885 | 886 | Set the process title by mangling :data:`**argv`. Mandatory argument is a 887 | :obj:`str`. 888 | 889 | .. c:function:: PyMODINIT_FUNC init_prctl(void) 890 | 891 | Create the module instance and add all the relevant constants to the module. 892 | That means all :const:`PR_*`, :const:`CAP_*` and :const:`SECBIT_*` constants 893 | mentioned in :manpage:`prctl(2)` and :manpage:`capabilities(7)`. To avoid 894 | repeating yourself all the time, use the :c:macro:`namedconstant` and 895 | :c:macro:`namedattribute` macros when adding new values. 896 | 897 | .. toctree:: 898 | :maxdepth: 2 899 | --------------------------------------------------------------------------------