cannam@132: # Copyright (c) 2014 Sandstorm Development Group, Inc. and contributors cannam@132: # Licensed under the MIT License: cannam@132: # cannam@132: # Permission is hereby granted, free of charge, to any person obtaining a copy cannam@132: # of this software and associated documentation files (the "Software"), to deal cannam@132: # in the Software without restriction, including without limitation the rights cannam@132: # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@132: # copies of the Software, and to permit persons to whom the Software is cannam@132: # furnished to do so, subject to the following conditions: cannam@132: # cannam@132: # The above copyright notice and this permission notice shall be included in cannam@132: # all copies or substantial portions of the Software. cannam@132: # cannam@132: # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@132: # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@132: # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@132: # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@132: # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@132: # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@132: # THE SOFTWARE. cannam@132: cannam@132: @0xb8630836983feed7; cannam@132: cannam@132: $import "/capnp/c++.capnp".namespace("capnp"); cannam@132: cannam@132: interface Persistent@0xc8cb212fcd9f5691(SturdyRef, Owner) { cannam@132: # Interface implemented by capabilities that outlive a single connection. A client may save() cannam@132: # the capability, producing a SturdyRef. The SturdyRef can be stored to disk, then later used to cannam@132: # obtain a new reference to the capability on a future connection. cannam@132: # cannam@132: # The exact format of SturdyRef depends on the "realm" in which the SturdyRef appears. A "realm" cannam@132: # is an abstract space in which all SturdyRefs have the same format and refer to the same set of cannam@132: # resources. Every vat is in exactly one realm. All capability clients within that vat must cannam@132: # produce SturdyRefs of the format appropriate for the realm. cannam@132: # cannam@132: # Similarly, every VatNetwork also resides in a particular realm. Usually, a vat's "realm" cannam@132: # corresponds to the realm of its main VatNetwork. However, a Vat can in fact communicate over cannam@132: # a VatNetwork in a different realm -- in this case, all SturdyRefs need to be transformed when cannam@132: # coming or going through said VatNetwork. The RPC system has hooks for registering cannam@132: # transformation callbacks for this purpose. cannam@132: # cannam@132: # Since the format of SturdyRef is realm-dependent, it is not defined here. An application should cannam@132: # choose an appropriate realm for itself as part of its design. Note that under Sandstorm, every cannam@132: # application exists in its own realm and is therefore free to define its own SturdyRef format; cannam@132: # the Sandstorm platform handles translating between realms. cannam@132: # cannam@132: # Note that whether a capability is persistent is often orthogonal to its type. In these cases, cannam@132: # the capability's interface should NOT inherit `Persistent`; instead, just perform a cast at cannam@132: # runtime. It's not type-safe, but trying to be type-safe in these cases will likely lead to cannam@132: # tears. In cases where a particular interface only makes sense on persistent capabilities, it cannam@132: # still should not explicitly inherit Persistent because the `SturdyRef` and `Owner` types will cannam@132: # vary between realms (they may even be different at the call site than they are on the cannam@132: # implementation). Instead, mark persistent interfaces with the $persistent annotation (defined cannam@132: # below). cannam@132: # cannam@132: # Sealing cannam@132: # ------- cannam@132: # cannam@132: # As an added security measure, SturdyRefs may be "sealed" to a particular owner, such that cannam@132: # if the SturdyRef itself leaks to a third party, that party cannot actually restore it because cannam@132: # they are not the owner. To restore a sealed capability, you must first prove to its host that cannam@132: # you are the rightful owner. The precise mechanism for this authentication is defined by the cannam@132: # realm. cannam@132: # cannam@132: # Sealing is a defense-in-depth mechanism meant to mitigate damage in the case of catastrophic cannam@132: # attacks. For example, say an attacker temporarily gains read access to a database full of cannam@132: # SturdyRefs: it would be unfortunate if it were then necessary to revoke every single reference cannam@132: # in the database to prevent the attacker from using them. cannam@132: # cannam@132: # In general, an "owner" is a course-grained identity. Because capability-based security is still cannam@132: # the primary mechanism of security, it is not necessary nor desirable to have a separate "owner" cannam@132: # identity for every single process or object; that is exactly what capabilities are supposed to cannam@132: # avoid! Instead, it makes sense for an "owner" to literally identify the owner of the machines cannam@132: # where the capability is stored. If untrusted third parties are able to run arbitrary code on cannam@132: # said machines, then the sandbox for that code should be designed using Distributed Confinement cannam@132: # such that the third-party code never sees the bits of the SturdyRefs and cannot directly cannam@132: # exercise the owner's power to restore refs. See: cannam@132: # cannam@132: # http://www.erights.org/elib/capability/dist-confine.html cannam@132: # cannam@132: # Resist the urge to represent an Owner as a simple public key. The whole point of sealing is to cannam@132: # defend against leaked-storage attacks. Such attacks can easily result in the owner's private cannam@132: # key being stolen as well. A better solution is for `Owner` to contain a simple globally unique cannam@132: # identifier for the owner, and for everyone to separately maintain a mapping of owner IDs to cannam@132: # public keys. If an owner's private key is compromised, then humans will need to communicate cannam@132: # and agree on a replacement public key, then update the mapping. cannam@132: # cannam@132: # As a concrete example, an `Owner` could simply contain a domain name, and restoring a SturdyRef cannam@132: # would require signing a request using the domain's private key. Authenticating this key could cannam@132: # be accomplished through certificate authorities or web-of-trust techniques. cannam@132: cannam@132: save @0 SaveParams -> SaveResults; cannam@132: # Save a capability persistently so that it can be restored by a future connection. Not all cannam@132: # capabilities can be saved -- application interfaces should define which capabilities support cannam@132: # this and which do not. cannam@132: cannam@132: struct SaveParams { cannam@132: sealFor @0 :Owner; cannam@132: # Seal the SturdyRef so that it can only be restored by the specified Owner. This is meant cannam@132: # to mitigate damage when a SturdyRef is leaked. See comments above. cannam@132: # cannam@132: # Leaving this value null may or may not be allowed; it is up to the realm to decide. If a cannam@132: # realm does allow a null owner, this should indicate that anyone is allowed to restore the cannam@132: # ref. cannam@132: } cannam@132: struct SaveResults { cannam@132: sturdyRef @0 :SturdyRef; cannam@132: } cannam@132: } cannam@132: cannam@132: interface RealmGateway(InternalRef, ExternalRef, InternalOwner, ExternalOwner) { cannam@132: # Interface invoked when a SturdyRef is about to cross realms. The RPC system supports providing cannam@132: # a RealmGateway as a callback hook when setting up RPC over some VatNetwork. cannam@132: cannam@132: import @0 (cap :Persistent(ExternalRef, ExternalOwner), cannam@132: params :Persistent(InternalRef, InternalOwner).SaveParams) cannam@132: -> Persistent(InternalRef, InternalOwner).SaveResults; cannam@132: # Given an external capability, save it and return an internal reference. Used when someone cannam@132: # inside the realm tries to save a capability from outside the realm. cannam@132: cannam@132: export @1 (cap :Persistent(InternalRef, InternalOwner), cannam@132: params :Persistent(ExternalRef, ExternalOwner).SaveParams) cannam@132: -> Persistent(ExternalRef, ExternalOwner).SaveResults; cannam@132: # Given an internal capability, save it and return an external reference. Used when someone cannam@132: # outside the realm tries to save a capability from inside the realm. cannam@132: } cannam@132: cannam@132: annotation persistent(interface, field) :Void; cannam@132: # Apply this annotation to interfaces for objects that will always be persistent, instead of cannam@132: # extending the Persistent capability, since the correct type parameters to Persistent depend on cannam@132: # the realm, which is orthogonal to the interface type and therefore should not be defined cannam@132: # along-side it. cannam@132: # cannam@132: # You may also apply this annotation to a capability-typed field which will always contain a cannam@132: # persistent capability, but where the capability's interface itself is not already marked cannam@132: # persistent. cannam@132: # cannam@132: # Note that absence of the $persistent annotation doesn't mean a capability of that type isn't cannam@132: # persistent; it just means not *all* such capabilities are persistent.