cannam@132: // Copyright (c) 2015 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: #ifndef CAPNP_MEMBRANE_H_ cannam@132: #define CAPNP_MEMBRANE_H_ cannam@132: // In capability theory, a "membrane" is a wrapper around a capability which (usually) forwards cannam@132: // calls but recursively wraps capabilities in those calls in the same membrane. The purpose of a cannam@132: // membrane is to enforce a barrier between two capabilities that cannot be bypassed by merely cannam@132: // introducing new objects. cannam@132: // cannam@132: // The most common use case for a membrane is revocation: Say Alice wants to give Bob a capability cannam@132: // to access Carol, but wants to be able to revoke this capability later. Alice can accomplish this cannam@132: // by wrapping Carol in a revokable wrapper which passes through calls until such a time as Alice cannam@132: // indicates it should be revoked, after which all calls through the wrapper will throw exceptions. cannam@132: // However, a naive wrapper approach has a problem: if Bob makes a call to Carol and sends a new cannam@132: // capability in that call, or if Carol returns a capability to Bob in the response to a call, then cannam@132: // the two are now able to communicate using this new capability, which Alice cannot revoke. In cannam@132: // order to avoid this problem, Alice must use not just a wrapper but a "membrane", which cannam@132: // recursively wraps all objects that pass through it in either direction. Thus, all connections cannam@132: // formed between Bob and Carol (originating from Alice's original introduction) can be revoked cannam@132: // together by revoking the membrane. cannam@132: // cannam@132: // Note that when a capability is passed into a membrane and then passed back out, the result is cannam@132: // the original capability, not a double-membraned capability. This means that in our revocation cannam@132: // example, if Bob uses his capability to Carol to obtain another capability from her, then send cannam@132: // it back to her, the capability Carol receives back will NOT be revoked when Bob's access to cannam@132: // Carol is revoked. Thus Bob can create long-term irrevocable connections. In most practical use cannam@132: // cases, this is what you want. APIs commonly rely on the fact that a capability obtained and then cannam@132: // passed back can be recognized as the original capability. cannam@132: // cannam@132: // Mark Miller on membranes: http://www.eros-os.org/pipermail/e-lang/2003-January/008434.html cannam@132: cannam@132: #include "capability.h" cannam@132: cannam@132: namespace capnp { cannam@132: cannam@132: class MembranePolicy { cannam@132: // Applications may implement this interface to define a membrane policy, which allows some cannam@132: // calls crossing the membrane to be blocked or redirected. cannam@132: cannam@132: public: cannam@132: virtual kj::Maybe inboundCall( cannam@132: uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0; cannam@132: // Given an inbound call (a call originating "outside" the membrane destined for an object cannam@132: // "inside" the membrane), decides what to do with it. The policy may: cannam@132: // cannam@132: // - Return null to indicate that the call should proceed to the destination. All capabilities cannam@132: // in the parameters or result will be properly wrapped in the same membrane. cannam@132: // - Return a capability to have the call redirected to that capability. Note that the redirect cannam@132: // capability will be treated as outside the membrane, so the params and results will not be cannam@132: // auto-wrapped; however, the callee can easily wrap the returned capability in the membrane cannam@132: // itself before returning to achieve this effect. cannam@132: // - Throw an exception to cause the call to fail with that exception. cannam@132: // cannam@132: // `target` is the underlying capability (*inside* the membrane) for which the call is destined. cannam@132: // Generally, the only way you should use `target` is to wrap it in some capability which you cannam@132: // return as a redirect. The redirect capability may modify the call in some way and send it to cannam@132: // `target`. Be careful to use `copyIntoMembrane()` and `copyOutOfMembrane()` as appropriate when cannam@132: // copying parameters or results across the membrane. cannam@132: // cannam@132: // Note that since `target` is inside the capability, if you were to directly return it (rather cannam@132: // than return null), the effect would be that the membrane would be broken: the call would cannam@132: // proceed directly and any new capabilities introduced through it would not be membraned. You cannam@132: // generally should not do that. cannam@132: cannam@132: virtual kj::Maybe outboundCall( cannam@132: uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0; cannam@132: // Like `inboundCall()`, but applies to calls originating *inside* the membrane and terminating cannam@132: // outside. cannam@132: // cannam@132: // Note: It is strongly recommended that `outboundCall()` returns null in exactly the same cases cannam@132: // that `inboundCall()` return null. Conversely, for any case where `inboundCall()` would cannam@132: // redirect or throw, `outboundCall()` should also redirect or throw. Otherwise, you can run cannam@132: // into inconsistent behavion when a promise is returned across a membrane, and that promise cannam@132: // later resolves to a capability on the other side of the membrane: calls on the promise cannam@132: // will enter and then exit the membrane, but calls on the eventual resolution will not cross cannam@132: // the membrane at all, so it is important that these two cases behave the same. cannam@132: cannam@132: virtual kj::Own addRef() = 0; cannam@132: // Return a new owned pointer to the same policy. cannam@132: // cannam@132: // Typically an implementation of MembranePolicy should also inherit kj::Refcounted and implement cannam@132: // `addRef()` as `return kj::addRef(*this);`. cannam@132: // cannam@132: // Note that the membraning system considers two membranes created with the same MembranePolicy cannam@132: // object actually to be the *same* membrane. This is relevant when an object passes into the cannam@132: // membrane and then back out (or out and then back in): instead of double-wrapping the object, cannam@132: // the wrapping will be removed. cannam@132: }; cannam@132: cannam@132: Capability::Client membrane(Capability::Client inner, kj::Own policy); cannam@132: // Wrap `inner` in a membrane specified by `policy`. `inner` is considered "inside" the membrane, cannam@132: // while the returned capability should only be called from outside the membrane. cannam@132: cannam@132: Capability::Client reverseMembrane(Capability::Client outer, kj::Own policy); cannam@132: // Like `membrane` but treat the input capability as "outside" the membrane, and return a cannam@132: // capability appropriate for use inside. cannam@132: // cannam@132: // Applications typically won't use this directly; the membraning code automatically sets up cannam@132: // reverse membranes where needed. cannam@132: cannam@132: template cannam@132: ClientType membrane(ClientType inner, kj::Own policy); cannam@132: template cannam@132: ClientType reverseMembrane(ClientType inner, kj::Own policy); cannam@132: // Convenience templates which return the same interface type as the input. cannam@132: cannam@132: template cannam@132: typename ServerType::Serves::Client membrane( cannam@132: kj::Own inner, kj::Own policy); cannam@132: template cannam@132: typename ServerType::Serves::Client reverseMembrane( cannam@132: kj::Own inner, kj::Own policy); cannam@132: // Convenience templates which input a capability server type and return the appropriate client cannam@132: // type. cannam@132: cannam@132: template cannam@132: Orphan::Reads> copyIntoMembrane( cannam@132: Reader&& from, Orphanage to, kj::Own policy); cannam@132: // Copy a Cap'n Proto object (e.g. struct or list), adding the given membrane to any capabilities cannam@132: // found within it. `from` is interpreted as "outside" the membrane while `to` is "inside". cannam@132: cannam@132: template cannam@132: Orphan::Reads> copyOutOfMembrane( cannam@132: Reader&& from, Orphanage to, kj::Own policy); cannam@132: // Like copyIntoMembrane() except that `from` is "inside" the membrane and `to` is "outside". cannam@132: cannam@132: // ======================================================================================= cannam@132: // inline implementation details cannam@132: cannam@132: template cannam@132: ClientType membrane(ClientType inner, kj::Own policy) { cannam@132: return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy)) cannam@132: .castAs(); cannam@132: } cannam@132: template cannam@132: ClientType reverseMembrane(ClientType inner, kj::Own policy) { cannam@132: return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy)) cannam@132: .castAs(); cannam@132: } cannam@132: cannam@132: template cannam@132: typename ServerType::Serves::Client membrane( cannam@132: kj::Own inner, kj::Own policy) { cannam@132: return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy)) cannam@132: .castAs(); cannam@132: } cannam@132: template cannam@132: typename ServerType::Serves::Client reverseMembrane( cannam@132: kj::Own inner, kj::Own policy) { cannam@132: return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy)) cannam@132: .castAs(); cannam@132: } cannam@132: cannam@132: namespace _ { // private cannam@132: cannam@132: OrphanBuilder copyOutOfMembrane(PointerReader from, Orphanage to, cannam@132: kj::Own policy, bool reverse); cannam@132: OrphanBuilder copyOutOfMembrane(StructReader from, Orphanage to, cannam@132: kj::Own policy, bool reverse); cannam@132: OrphanBuilder copyOutOfMembrane(ListReader from, Orphanage to, cannam@132: kj::Own policy, bool reverse); cannam@132: cannam@132: } // namespace _ (private) cannam@132: cannam@132: template cannam@132: Orphan::Reads> copyIntoMembrane( cannam@132: Reader&& from, Orphanage to, kj::Own policy) { cannam@132: return _::copyOutOfMembrane( cannam@132: _::PointerHelpers::Reads>::getInternalReader(from), cannam@132: to, kj::mv(policy), true); cannam@132: } cannam@132: cannam@132: template cannam@132: Orphan::Reads> copyOutOfMembrane( cannam@132: Reader&& from, Orphanage to, kj::Own policy) { cannam@132: return _::copyOutOfMembrane( cannam@132: _::PointerHelpers::Reads>::getInternalReader(from), cannam@132: to, kj::mv(policy), false); cannam@132: } cannam@132: cannam@132: } // namespace capnp cannam@132: cannam@132: #endif // CAPNP_MEMBRANE_H_