/usr/lib/python3/dist-packages/subuserlib/permissions.py is in subuser 0.6.1-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | # -*- coding: utf-8 -*-
"""
Module used for the loading and saving of permissions.json files. Contains the default permissions list.
"""
#external imports
import json
import collections
import sys
import os
#internal imports
# import ...
allImagesMustHavePermissions = "All subuser images must have a permissions.json file as defined by the permissions.json standard: <http://subuser.org/subuser-standard/permissions-dot-json-file-format.html>"
# Defaults from http://subuser.org/subuser-standard/permissions-dot-json-file-format.html
# This is a comprehensive list of all permissions
defaults = {
# Conservative permissions
"description": ""
,"maintainer": ""
,"executable": None
,"basic-common-permissions": False
,"stateful-home": False
,"inherit-locale": False
,"inherit-timezone": False
,"entrypoints":{}
# Moderate permissions
,"gui": None
,"user-dirs": []
,"inherit-envvars": []
,"sound-card": False
,"webcam": False
,"access-working-directory": False
,"allow-network-access": False
# Liberal permissions
,"x11": False
,"system-dirs": {}
,"graphics-card": False
,"serial-devices":False
,"system-dbus": False
,"as-root": False
,"sudo": False
# Anarchistic permissions
,"privileged": False
,"run-commands-on-host": False
}
guiDefaults = {
"clipboard": False,
"system-tray": False,
"cursors": False,
"border-color": "red"
}
basicCommonPermissions = ["stateful-home","inherit-locale","inherit-timezone"]
levels = [
{"name" : "prelude",
"description" : "",
"permissions" : ["description", "maintainer", "executable","entrypoints"]},
{"name" : "conservative",
"description" : "Conservative permissions(These are safe):",
"permissions" : ["stateful-home", "inherit-locale", "inherit-timezone"]},
{"name" : "moderate",
"description" : "Moderate permissions(These are probably safe):",
"permissions" : ["gui", "user-dirs", "sound-card", "webcam", "access-working-directory", "allow-network-access"]},
{"name" : "liberal",
"description" : "Liberal permissions(These may pose a security risk):",
"permissions" : ["x11", "system-dirs", "graphics-card", "serial-devices", "system-dbus", "as-root"]},
{"name" : "anarchistic",
"description" : "WARNING: These permissions give the subuser full access to your system when run.",
"permissions" : ["privileged","run-commands-on-host"]}]
descriptions = {
# Prelude
"description":lambda description : ["Description: "+description]
,"maintainer":lambda maintainer : ["Maintainer: "+maintainer]
,"executable":lambda executable : ["Executable: "+executable] if executable else ["Is a library."]
,"entrypoints": lambda entrypoints: ["Entry points: '"+ "' '".join(entrypoints.keys())+"'"] if entrypoints else []
# Conservative
,"stateful-home": lambda p : ["To have its own home directory where it can save files and settings."] if p else []
,"inherit-locale": lambda p : ["To find out which language you speak and what region you live in."] if p else []
,"inherit-timezone": lambda p : ["To find out your current timezone."] if p else []
# Moderate
,"gui": lambda guiOptions : (["To be able to display windows."] + sum([guiDescriptions[permission](value) for permission,value in guiOptions.items()],[])) if guiOptions else []
,"user-dirs": lambda userDirs : ["To access to the following user directories: '~/"+"' '~/".join(userDirs)+"'"] if userDirs else []
,"inherit-envvars": lambda envVars : ["To access to the following environment variables: "+" ".join(envVars)] if envVars else []
,"sound-card": lambda p : ["To access to your soundcard, can play sounds/record sound."] if p else []
,"webcam": lambda p : ["To access your computer's webcam/can see you."] if p else []
,"access-working-directory": lambda p: ["To access the directory from which it was launched."] if p else []
,"allow-network-access": lambda p : ["To access the network/internet."] if p else []
# Liberal
,"x11": lambda p : ["To display X11 windows and interact with your X11 server directly(log keypresses, read over your shoulder, steal your passwords, control your computer ect.)"] if p else []
,"system-dirs": lambda systemDirs : ["To read and write to the host's `"+source+"` directory, mounted in the container as:`"+dest+"`" for source,dest in systemDirs.items()]
,"graphics-card": lambda p : ["To access your graphics-card directly for OpenGL tasks."] if p else []
,"serial-devices": lambda p : ["To access serial devices such as programmable micro-controllers and modems."] if p else []
,"system-dbus": lambda p: ["To talk to the system dbus daemon."] if p else []
,"sudo": lambda p: ["To be allowed to use the sudo command to run subproccesses as root in the container."] if p else []
,"as-root": lambda p: ["To be allowed to run as root within the container."] if p else []
# Anarchistic
,"privileged": lambda p: ["To have full access to your system. To even do things as root outside of its container."] if p else []
,"run-commands-on-host": lambda p: ["To run commands as a normal user on the host system."] if p else []
}
guiDescriptions = {
"clipboard": lambda p : ["Is able to access the host's clipboard."] if p else []
,"system-tray": lambda p : ["Is able to create system tray icons."] if p else []
,"cursors": lambda p : ["Is able to change the mouse's cursor icon."] if p else []
,"border-color": lambda p : ["Window borders will be "+p] if p else ["Window borders will be red."]}
def load(permissionsFilePath=None,permissionsString=None):
"""
Return a dictionary of permissions from the given permissions.json file. Permissions that are not specified are set to their default values.
Loading permissions doesn't crash horribly and does something at least slightly sensible.
>>> getNonDefaultPermissions(load(permissionsString="{}"))
OrderedDict()
Valid paths are loaded when the user-dirs permission is set.
>>> print(json.dumps(getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"Downloads\"]}'))))
{"user-dirs": ["Downloads"]}
However, funny business is strictly frowned upon.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"..\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain relative paths. The user dir: ".." is forbidden.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"../\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain relative paths. The user dir: "../" is forbidden.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"./../../\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain relative paths. The user dir: "./../../" is forbidden.
Even if it is well hiden behind valid values.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"Downloads\",\"../\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain relative paths. The user dir: "../" is forbidden.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"Downloads\",\"./Foo\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain relative paths. The user dir: "./Foo" is forbidden.
>>> getNonDefaultPermissions(load(permissionsString='{\"user-dirs\":[\"Downloads\",\"/Foo\"]}'))
Traceback (most recent call last):
...
SyntaxError: Error, user dir permissions may not contain system wide absolute paths. The user dir: "/Foo" is forbidden.
>>> getNonDefaultPermissions(load(permissionsString='{\"x11\":true,\"gui\":{}}'))
Traceback (most recent call last):
...
SyntaxError: X11 and GUI permissions are mutually exclusive. Cannot provide direct and indirect access to X11 at the same time.
"""
if not permissionsString is None:
try:
permissions=json.loads(permissionsString, object_pairs_hook=collections.OrderedDict)
except ValueError:
sys.exit(permissionsString+" is not valid json. "+allImagesMustHavePermissions)
else:
# read permissions.json file
with open(permissionsFilePath, 'r') as file_f:
try:
permissions=json.load(file_f, object_pairs_hook=collections.OrderedDict)
except ValueError:
raise SyntaxError(permissionsFilePath+" is not valid json. "+allImagesMustHavePermissions)
# Validate that the permissions are supported by this version of subuser.
for permission in permissions.keys():
if not permission in defaults:
raise SyntaxError("Error: the permission \""+permission+"\" is not supported by your version of subuser. Try updating first.")
else: # Validate if the permissions don't have unsafe contents.
if permission == "user-dirs":
userDirs = permissions["user-dirs"]
if userDirs and type(userDirs) is list:
for userDir in permissions["user-dirs"]:
if ".." in userDir or userDir.startswith("./"):
raise SyntaxError("Error, user dir permissions may not contain relative paths. The user dir: \""+userDir+"\" is forbidden.")
if userDir.startswith("/"):
raise SyntaxError("Error, user dir permissions may not contain system wide absolute paths. The user dir: \""+userDir+"\" is forbidden.")
elif userDirs is []:
SyntaxError("The user-dirs permission must be a list of directory names.")
if "gui" in permissions and not permissions["gui"] is None:
for guiPermission in permissions["gui"].keys():
if not guiPermission in guiDefaults:
raise SyntaxError("Error: the gui permission \""+guiPermission+"\" is not supported by your version of subuser. Try updating first.")
# Set permission defaults for permissions that are not explicitly specified in the permissions.json file
if "basic-common-permissions" in permissions and permissions["basic-common-permissions"]:
for basicCommonPermission in basicCommonPermissions:
if not basicCommonPermission in permissions:
permissions[basicCommonPermission] = True
for permission,defaultValue in defaults.items():
if not permission in permissions:
permissions[permission] = defaultValue
# gui permissions
if not permissions["gui"] is None:
for permission,defaultValue in guiDefaults.items():
if not permission in permissions["gui"]:
permissions["gui"][permission] = defaultValue
if permissions["x11"]:
raise SyntaxError("X11 and GUI permissions are mutually exclusive. Cannot provide direct and indirect access to X11 at the same time.")
return permissions
def getNonDefaultPermissions(permissions):
"""
Returns the dictionary of permissions which are NOT set to their default values.
>>> import subuserlib.permissions
>>> permissions = subuserlib.permissions.load(permissionsString='{"x11":true}')
>>> getNonDefaultPermissions(permissions) == {u'x11': True}
True
"""
nonDefaultPermissions = collections.OrderedDict()
for permission,value in permissions.items():
if not value == defaults[permission]:
nonDefaultPermissions[permission] = value
return nonDefaultPermissions
def getJSONString(permissions):
"""
Returns the given permissions as a JSON formated string.
"""
permissionsToSave = getNonDefaultPermissions(permissions)
return json.dumps(permissionsToSave,indent=1, separators=(',', ': '))
def compare(oldDefaults,newDefaults,userApproved):
"""
Analize permission sets for changes.
First, compare the old defaults to the user approved permissions.
By doing so, we aquire an understanding of which permissions have been set by the user, and which are still at their default values.
We will leave the user-set permissions alone.
Next, we compair the old non-user set permissions to the new defaults.
Finally, we return a list of permissions that have been removed as well as a dictionary of permissions which have been added or changed.
The return value is a tuple of the form: ([removed-permisions],{additions/changes})
"""
return __compare(oldDefaults = getNonDefaultPermissions(oldDefaults), newDefaults = getNonDefaultPermissions(newDefaults), userApproved = getNonDefaultPermissions(userApproved))
def __compare(oldDefaults,newDefaults,userApproved):
"""
Analize permission sets for changes.
First, compare the old defaults to the user approved permissions.
By doing so, we aquire an understanding of which permissions have been set by the user, and which are still at their default values.
We will leave the user-set permissions alone.
Next, we compair the old non-user set permissions to the new defaults.
Finally, we return a list of permissions that have been removed as well as a dictionary of permissions which have been added or changed.
>>> import subuserlib.permissions
>>> subuserlib.permissions.__compare(oldDefaults={"a":1,"b":2,"c":3,"d":4,"e":5},newDefaults={"a":1,"b":3,"c":4,"f":4},userApproved={"a":1,"b":5,"e":5,"z":7}) == (["e"],{"f":4})
True
"""
userSetPermissions = {}
for key,value in userApproved.items():
# Cleaver code is evil, but I have given into temptation in this case ^_^
if (not key in oldDefaults) or (oldDefaults[key] != value):
userSetPermissions[key] = value
for key,value in oldDefaults.items():
if (not key in userApproved):
userSetPermissions[key] = value
addedOrChangedPermissions = {}
for key,value in newDefaults.items():
# added or changed
if (not key in userSetPermissions) and ((not key in oldDefaults) or (key in oldDefaults and oldDefaults[key] != value)):
addedOrChangedPermissions[key] = value
droppedPermissions = []
for key in oldDefaults.keys():
if (key not in userSetPermissions) and (key not in newDefaults):
droppedPermissions.append(key)
return (droppedPermissions,addedOrChangedPermissions)
|