diff options
Diffstat (limited to 'rainbow/permissions')
-rw-r--r-- | rainbow/permissions/__init__.py | 1 | ||||
-rw-r--r-- | rainbow/permissions/manifest.txt | 153 | ||||
-rw-r--r-- | rainbow/permissions/permissions.info | 6 | ||||
-rw-r--r-- | rainbow/permissions/permissions.txt | 67 | ||||
-rw-r--r-- | rainbow/permissions/permlist.py | 94 | ||||
-rw-r--r-- | rainbow/permissions/short.txt | 44 |
6 files changed, 365 insertions, 0 deletions
diff --git a/rainbow/permissions/__init__.py b/rainbow/permissions/__init__.py new file mode 100644 index 0000000..2d52afe --- /dev/null +++ b/rainbow/permissions/__init__.py @@ -0,0 +1 @@ +from rainbow.permissions.permlist import PermissionSet diff --git a/rainbow/permissions/manifest.txt b/rainbow/permissions/manifest.txt new file mode 100644 index 0000000..cf0ff54 --- /dev/null +++ b/rainbow/permissions/manifest.txt @@ -0,0 +1,153 @@ +Verification System Data +======================== + +:Author: Michael Stone <michael@laptop.org>, Noah Kantrowitz <noah@laptop.org> +:Date: August 8, 2007 + +Our system for update verification manipulates four kinds of data: +envelopes, keys, credentials, and manifests. + +Presently, we intend to use JSON as a transfer encoding because we only need to +represent tree-structured data and because we want an encoding that is easily +and safely parseable in pure Python and in other languages. + +If we learn that we need a canonical encoding, a more space-efficient encoding, +or an encoding that can be more efficiently streamed, then we will consider +alternatives. + + +Envelopes +--------- + +Envelopes are tuples of the form + + (version, type, data) :: (Integer, String, Value) + ^ ^ ^ + | | \________ a JSON value in a format governed by (version, type) + | \______ a hint about how to interpret the data field that follows. + \_________ an opaque version identifier; see below. + +The version identifier is intended to specify a minimal dialect that must be +supported by compliant clients. Backwards-compatible extensions of this dialect +may be included in an enveloped value without changing the version identifier. + + Note: Noah suggests make the version identifier transparent and declare it to + be an integer with a a monotonically increasing value. I'm okay with this, + but since we anticipate changing the identifer only when making + backwards-incompatible protocol changes, I think it might as well be opaque. + +Keys +---- + +Keys are envelopes with version 1 and type "key". A key's datum consists of a +tuple + + (algorithm, fingerprint, key) :: (String, String, String) + ^ ^ ^ + | | \______ an opaque string specifiying the actual key + | \______ an opaque string to use to refer to the key + \_________ an opaque algorithm identifier; + +In practice, we might see a key encoded as: + [1, "key", ["rsa", "12508713460986140897", "AE1908579871250981230896109761"]] + + +Credentials +----------- + +A credential is an envelope with version 1 and type "credential". Conceptually, +its datum consists of a relation whose tuples are signatures of the form: + + (signature-algorithm, key, message, signature) + +However, in practice, the message is defined implicitly by context (i.e. as the +hash of the manifest with which this credential is paired) and we wish to use +the credential/manifest system to help detect accidental corruption as early as +possible, including corruption of the manifest. + +Therefore, we actually propose to represent the datum of a manifest credential +as a tuple of + + (hash-alg, sig-alg, expected-hash, key-identifier, signature) + +We include the manifest hash as a convenient way to check that the manifest +was not accidentally damaged in transit (e.g. by a flash corruption bug). + +The key fingerprint identifies the key that should be used to verify the +signature. + +The hash-algorithm and signature-algorithm are used to verify the signature +against the *computed* manifest-hash (not the one cached in the signature +message). + +Finally, the signature itself is opaque. + +In actual JSON, we might imagine that such a message would look like: + + [1, "credential", [ ["sha1", "rsa", "DEADBEEF", "123MYKEY", "0PAQU3S1G"], + ["whirlpool", "ecc", "123095", "9912KEY", "8567A"] ] ] + + + +Manifests +--------- + +Manifests are the most complicated piece of the system because they have +different conceptual and transfer-encoded forms. + +Conceptually, a manifest's datum is a constraint-set. Implementations of this +constraint language are free to support checking as many or as few kinds of +constraint as they wish. Clearly, though, adequate verification of a file-system +against a manifest depends on both the presence of a constraint set that +sufficiently constrains the properties of the file-system being checked and a +correct and complete implementation of a checker for that constraint-set. + +This model controls the implementation cost of both the verification machine +(and the manifest generation tool) while allowing great flexibility in the +underlying properties being verified and in the degree to which we compromise +our implementation goals in the face of time pressure. + +Details +~~~~~~~ + +There are several basic constraints we wish to support. These include +constraints on file content, file type (normal, hardlink, symlink, device, ...), +permissions, ownership, and on the contents of directories. + +Manifests are envelopes with version 1 and type "manifest". A manifest's datum +consists of a tuple of a dictionary of optimization hints and a dictionary of +constraints. Structurally, the datum looks like + + (Hint -> Value, Constraint -> (Query, Result)) + +Here "hints" are opaque strings that may be given some uninterpreted data as an +argument (e.g. "invert-filesystem" : true). These are provided because checking +some of the global constraints may rely on expensive-to-compute structures such +as an inverted index that maps inodes to the paths that point to them. + +The constraint itself dictionary is also made slightly more complicated in the +name of speed and memory usage. While conceptually, we wish to store a ternary +relation with columns + + (constraint-algorithm, query, expected-result) + +in practice, we expect that there will very few (~5) constraint algorithms and +very many (~100000) (query, expected-result) pairs. Hence we suggest +optimizing for this case by grouping the relation's tuples by +constraint-algorithm, as shown above in the structural description. + +A small example of a manifest might be: + + [1, "manifest", [ + {"invert-filesystem": ["/links"]}, + {"file-sha1" : [ ["/etc/passwd", "BLAH18AK"], + ["/bin/bash", "LA81985ED"]], + "permissions" : [ ["/my/secret", 448] ], + "same-inode" : [ [["/links/1", "/links/2", "/links/3"], null] ], + "dir-contains" : [ ["/", ["bin", "sbin"]] ], + "update-excludes": [ ["/security", null] ] + }]] + + + + diff --git a/rainbow/permissions/permissions.info b/rainbow/permissions/permissions.info new file mode 100644 index 0000000..654dc17 --- /dev/null +++ b/rainbow/permissions/permissions.info @@ -0,0 +1,6 @@ +use-camera +use-microphone +lim_nofile 20 +lim_mem 190e6 +lim_nproc 8 +lim_fsize 10e6 diff --git a/rainbow/permissions/permissions.txt b/rainbow/permissions/permissions.txt new file mode 100644 index 0000000..09d3bfe --- /dev/null +++ b/rainbow/permissions/permissions.txt @@ -0,0 +1,67 @@ +# We will make a new section in activity.info called: +[Capabilities] + +# There are several protections which cannot be modified by the installer. +# P_BIOS_CORE -- we sign bios with dev key; firmware checks +# P_BIOS_COPY -- not our problem +# P_SF_CORE -- may be turned off with dev key. +# P_SF_RUN -- What, exactly, does "system files" refer to? + +net=1 # over-all net access; (1, 0) +net.limits.burst=10 # token bucket depth; tokens +net.limits.steady=2 # token bucket refill rate; tokens / sec +net.limits.connections=5 # connections + +# There are several network options that we don't know how or why to implement +# at the moment + +#net.limits.quota=3.5 # total throughput megabytes +#net.firewall=??? # some firewall rules, TBD +#net.access_rules.times= # +#net.ports.53.bind=1 # allow us to bind on port 53 + + +nand.limits.burst=1 # tokens +nand.limits.steady=1 # tokens / sec +nand.limits.quota=0 # mb + +# timed capabilities? (all boolean flags allowing capability request) + +microphone=1 # boolean flags +microphone.analog=0 # +camera=1 # + + +# -- can these be turned off? +cpu.limits.burst=100 # tokens +cpu.limits.steady=50 # tokens/sec + +# P_RTC -- is this a configurable flag? + +dsp.bg=1 # we want to play sounds in the background + +x=0 # synthetic X events + +fs.full=0 # we don't need full disk access +usb=0 # or usb access +sd=0 # or SD access + +# As Noah notes, we're *going* to need an async-notification scheme. +# That can be spammed, so it needs a permission. +# Likewise for a search service. + +#P_IDENT -- any permissions? +#P_SANDBOX -- no permissions ATM; eventual fine-grained library & binary inclusion + +document=0 # boolean flag +document.read_only= # mime-type +document.limits.burst=0 # tokens +document.limits.steady=0 # tokens/sec + + +#P_DOCUMENT_BACKUP -- no permissions +#P_THEFT -- no permissions +#P_SERVER_AUTH -- no permissions... (depends on P_NET?) +#P_PASSWORD -- no permissions + + diff --git a/rainbow/permissions/permlist.py b/rainbow/permissions/permlist.py new file mode 100644 index 0000000..897beca --- /dev/null +++ b/rainbow/permissions/permlist.py @@ -0,0 +1,94 @@ +DOMAINS = {'network': ('via', 'to', 'port', 'rate', 'burst', 'connection-rate', + 'transfer-limit', 'bind-port'), + 'constant-uid': (), + 'strace': (), + 'use-audio': (), + 'use-video': (), + 'use-serial': (), + 'play-background-sound': (), + 'quota': ('limit'), + 'lim_nofile': ('@NUM@'), + 'lim_mem': ('@NUM@'), + 'lim_nproc': ('@NUM@'), + 'lim_fsize': ('@NUM@'), + 'document_read_ro': ('type') + } + +class PermissionSet(object): + def __init__(self, fp=None): + self._permissions = {} + self._network_permissions = [] + + for line in fp: + line = line.lower().strip() + if not line or line.startswith('#'): + continue + + fields = line.split() + if not fields[0] in DOMAINS: + print "Unknown permissions domain: [%s]" % fields[0] + continue + + for field in fields[1:]: + if '@NUM@' in DOMAINS[fields[0]]: + try: + float(fields[1]) + except: + print "Expecting numeric value in domain [%s] (%s)" % (fields[0], fields[1]) + continue + else: + key, value = field.split(':') + if not key in DOMAINS[fields[0]]: + print "Unknown flag [%s] in domain [%s]" % (key, fields[0]) + continue + + if fields[0] == 'network': + self._network_permissions.append(fields[1:]) + else: + self._permissions[fields[0]] = fields[1:] or True + + def has_permission(self, domain, key=None, value=None): + "Fails for network, since it doesn't make sense to query those perms" + if domain not in self._permissions: + return False + elif key and value: + for permission in self._permissions[domain]: + this_key, these_values = permission.split(':') + if not key == this_key: + continue + these_values = these_values.split(',') + if value in these_values: + return True + return self._permissions[domain] == True + + def permission_params(self, domain): + if domain not in self._permissions: + return None + return(self._permissions[domain]) + + +if __name__ == '__main__': + import cStringIO as stringio + from pprint import pprint + + perms = """ + network via:ipv4,ipv6 to:pgp.mit.edu,laptop.org port:80 rate:100Kb/s burst:1Mb + network via:ipv4 port:25 connection-rate:10/min transfer-limit:8Mb/hr + network via:ipv6 bind-port:1400 + + # Comment + use-microphone + use-camera + + # Comment, yay + play-background-sound + + document_read_ro type:text/plain + + quota limit:15Mb + """ + + permfp = stringio.StringIO(perms) + permset = PermissionSet(permfp) + pprint(permset._permissions) + pprint(permset._network_permissions) diff --git a/rainbow/permissions/short.txt b/rainbow/permissions/short.txt new file mode 100644 index 0000000..f012220 --- /dev/null +++ b/rainbow/permissions/short.txt @@ -0,0 +1,44 @@ +# We will make a new section in activity.info called: +[Capabilities] + + +net=1 # over-all net access; (1, 0) +net.limits.burst=10 # token bucket depth; tokens +net.limits.steady=2 # token bucket refill rate; tokens / sec +net.limits.connections=5 # connections + +#net.limits.quota=3.5 # total throughput megabytes +#net.firewall=??? # some firewall rules, TBD +#net.access_rules.times= # +#net.ports.53.bind=1 # allow us to bind on port 53 + +nand.limits.burst=1 # tokens +nand.limits.steady=1 # tokens / sec +nand.limits.quota=0 # mb + + +microphone=1 # boolean flags +microphone.analog=0 # +camera=1 # +dsp.bg=1 # we want to play sounds in the background + + +cpu.limits.burst=100 # tokens +cpu.limits.steady=50 # tokens/sec + + +x=0 # synthetic X events + + +fs.full=0 # we don't need full disk access +usb=0 # or usb access +sd=0 # or SD access + + +document=0 # boolean flag +document.read_only= # mime-type +document.limits.burst=0 # tokens +document.limits.steady=0 # tokens/sec + + + |