cannam@147
|
1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
|
cannam@147
|
2 // Licensed under the MIT License:
|
cannam@147
|
3 //
|
cannam@147
|
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
|
cannam@147
|
5 // of this software and associated documentation files (the "Software"), to deal
|
cannam@147
|
6 // in the Software without restriction, including without limitation the rights
|
cannam@147
|
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
cannam@147
|
8 // copies of the Software, and to permit persons to whom the Software is
|
cannam@147
|
9 // furnished to do so, subject to the following conditions:
|
cannam@147
|
10 //
|
cannam@147
|
11 // The above copyright notice and this permission notice shall be included in
|
cannam@147
|
12 // all copies or substantial portions of the Software.
|
cannam@147
|
13 //
|
cannam@147
|
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
cannam@147
|
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
cannam@147
|
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
cannam@147
|
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
cannam@147
|
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
cannam@147
|
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
cannam@147
|
20 // THE SOFTWARE.
|
cannam@147
|
21
|
cannam@147
|
22 #ifndef CAPNP_MEMBRANE_H_
|
cannam@147
|
23 #define CAPNP_MEMBRANE_H_
|
cannam@147
|
24 // In capability theory, a "membrane" is a wrapper around a capability which (usually) forwards
|
cannam@147
|
25 // calls but recursively wraps capabilities in those calls in the same membrane. The purpose of a
|
cannam@147
|
26 // membrane is to enforce a barrier between two capabilities that cannot be bypassed by merely
|
cannam@147
|
27 // introducing new objects.
|
cannam@147
|
28 //
|
cannam@147
|
29 // The most common use case for a membrane is revocation: Say Alice wants to give Bob a capability
|
cannam@147
|
30 // to access Carol, but wants to be able to revoke this capability later. Alice can accomplish this
|
cannam@147
|
31 // by wrapping Carol in a revokable wrapper which passes through calls until such a time as Alice
|
cannam@147
|
32 // indicates it should be revoked, after which all calls through the wrapper will throw exceptions.
|
cannam@147
|
33 // However, a naive wrapper approach has a problem: if Bob makes a call to Carol and sends a new
|
cannam@147
|
34 // capability in that call, or if Carol returns a capability to Bob in the response to a call, then
|
cannam@147
|
35 // the two are now able to communicate using this new capability, which Alice cannot revoke. In
|
cannam@147
|
36 // order to avoid this problem, Alice must use not just a wrapper but a "membrane", which
|
cannam@147
|
37 // recursively wraps all objects that pass through it in either direction. Thus, all connections
|
cannam@147
|
38 // formed between Bob and Carol (originating from Alice's original introduction) can be revoked
|
cannam@147
|
39 // together by revoking the membrane.
|
cannam@147
|
40 //
|
cannam@147
|
41 // Note that when a capability is passed into a membrane and then passed back out, the result is
|
cannam@147
|
42 // the original capability, not a double-membraned capability. This means that in our revocation
|
cannam@147
|
43 // example, if Bob uses his capability to Carol to obtain another capability from her, then send
|
cannam@147
|
44 // it back to her, the capability Carol receives back will NOT be revoked when Bob's access to
|
cannam@147
|
45 // Carol is revoked. Thus Bob can create long-term irrevocable connections. In most practical use
|
cannam@147
|
46 // cases, this is what you want. APIs commonly rely on the fact that a capability obtained and then
|
cannam@147
|
47 // passed back can be recognized as the original capability.
|
cannam@147
|
48 //
|
cannam@147
|
49 // Mark Miller on membranes: http://www.eros-os.org/pipermail/e-lang/2003-January/008434.html
|
cannam@147
|
50
|
cannam@147
|
51 #include "capability.h"
|
cannam@147
|
52
|
cannam@147
|
53 namespace capnp {
|
cannam@147
|
54
|
cannam@147
|
55 class MembranePolicy {
|
cannam@147
|
56 // Applications may implement this interface to define a membrane policy, which allows some
|
cannam@147
|
57 // calls crossing the membrane to be blocked or redirected.
|
cannam@147
|
58
|
cannam@147
|
59 public:
|
cannam@147
|
60 virtual kj::Maybe<Capability::Client> inboundCall(
|
cannam@147
|
61 uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0;
|
cannam@147
|
62 // Given an inbound call (a call originating "outside" the membrane destined for an object
|
cannam@147
|
63 // "inside" the membrane), decides what to do with it. The policy may:
|
cannam@147
|
64 //
|
cannam@147
|
65 // - Return null to indicate that the call should proceed to the destination. All capabilities
|
cannam@147
|
66 // in the parameters or result will be properly wrapped in the same membrane.
|
cannam@147
|
67 // - Return a capability to have the call redirected to that capability. Note that the redirect
|
cannam@147
|
68 // capability will be treated as outside the membrane, so the params and results will not be
|
cannam@147
|
69 // auto-wrapped; however, the callee can easily wrap the returned capability in the membrane
|
cannam@147
|
70 // itself before returning to achieve this effect.
|
cannam@147
|
71 // - Throw an exception to cause the call to fail with that exception.
|
cannam@147
|
72 //
|
cannam@147
|
73 // `target` is the underlying capability (*inside* the membrane) for which the call is destined.
|
cannam@147
|
74 // Generally, the only way you should use `target` is to wrap it in some capability which you
|
cannam@147
|
75 // return as a redirect. The redirect capability may modify the call in some way and send it to
|
cannam@147
|
76 // `target`. Be careful to use `copyIntoMembrane()` and `copyOutOfMembrane()` as appropriate when
|
cannam@147
|
77 // copying parameters or results across the membrane.
|
cannam@147
|
78 //
|
cannam@147
|
79 // Note that since `target` is inside the capability, if you were to directly return it (rather
|
cannam@147
|
80 // than return null), the effect would be that the membrane would be broken: the call would
|
cannam@147
|
81 // proceed directly and any new capabilities introduced through it would not be membraned. You
|
cannam@147
|
82 // generally should not do that.
|
cannam@147
|
83
|
cannam@147
|
84 virtual kj::Maybe<Capability::Client> outboundCall(
|
cannam@147
|
85 uint64_t interfaceId, uint16_t methodId, Capability::Client target) = 0;
|
cannam@147
|
86 // Like `inboundCall()`, but applies to calls originating *inside* the membrane and terminating
|
cannam@147
|
87 // outside.
|
cannam@147
|
88 //
|
cannam@147
|
89 // Note: It is strongly recommended that `outboundCall()` returns null in exactly the same cases
|
cannam@147
|
90 // that `inboundCall()` return null. Conversely, for any case where `inboundCall()` would
|
cannam@147
|
91 // redirect or throw, `outboundCall()` should also redirect or throw. Otherwise, you can run
|
cannam@147
|
92 // into inconsistent behavion when a promise is returned across a membrane, and that promise
|
cannam@147
|
93 // later resolves to a capability on the other side of the membrane: calls on the promise
|
cannam@147
|
94 // will enter and then exit the membrane, but calls on the eventual resolution will not cross
|
cannam@147
|
95 // the membrane at all, so it is important that these two cases behave the same.
|
cannam@147
|
96
|
cannam@147
|
97 virtual kj::Own<MembranePolicy> addRef() = 0;
|
cannam@147
|
98 // Return a new owned pointer to the same policy.
|
cannam@147
|
99 //
|
cannam@147
|
100 // Typically an implementation of MembranePolicy should also inherit kj::Refcounted and implement
|
cannam@147
|
101 // `addRef()` as `return kj::addRef(*this);`.
|
cannam@147
|
102 //
|
cannam@147
|
103 // Note that the membraning system considers two membranes created with the same MembranePolicy
|
cannam@147
|
104 // object actually to be the *same* membrane. This is relevant when an object passes into the
|
cannam@147
|
105 // membrane and then back out (or out and then back in): instead of double-wrapping the object,
|
cannam@147
|
106 // the wrapping will be removed.
|
cannam@147
|
107 };
|
cannam@147
|
108
|
cannam@147
|
109 Capability::Client membrane(Capability::Client inner, kj::Own<MembranePolicy> policy);
|
cannam@147
|
110 // Wrap `inner` in a membrane specified by `policy`. `inner` is considered "inside" the membrane,
|
cannam@147
|
111 // while the returned capability should only be called from outside the membrane.
|
cannam@147
|
112
|
cannam@147
|
113 Capability::Client reverseMembrane(Capability::Client outer, kj::Own<MembranePolicy> policy);
|
cannam@147
|
114 // Like `membrane` but treat the input capability as "outside" the membrane, and return a
|
cannam@147
|
115 // capability appropriate for use inside.
|
cannam@147
|
116 //
|
cannam@147
|
117 // Applications typically won't use this directly; the membraning code automatically sets up
|
cannam@147
|
118 // reverse membranes where needed.
|
cannam@147
|
119
|
cannam@147
|
120 template <typename ClientType>
|
cannam@147
|
121 ClientType membrane(ClientType inner, kj::Own<MembranePolicy> policy);
|
cannam@147
|
122 template <typename ClientType>
|
cannam@147
|
123 ClientType reverseMembrane(ClientType inner, kj::Own<MembranePolicy> policy);
|
cannam@147
|
124 // Convenience templates which return the same interface type as the input.
|
cannam@147
|
125
|
cannam@147
|
126 template <typename ServerType>
|
cannam@147
|
127 typename ServerType::Serves::Client membrane(
|
cannam@147
|
128 kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy);
|
cannam@147
|
129 template <typename ServerType>
|
cannam@147
|
130 typename ServerType::Serves::Client reverseMembrane(
|
cannam@147
|
131 kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy);
|
cannam@147
|
132 // Convenience templates which input a capability server type and return the appropriate client
|
cannam@147
|
133 // type.
|
cannam@147
|
134
|
cannam@147
|
135 template <typename Reader>
|
cannam@147
|
136 Orphan<typename kj::Decay<Reader>::Reads> copyIntoMembrane(
|
cannam@147
|
137 Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy);
|
cannam@147
|
138 // Copy a Cap'n Proto object (e.g. struct or list), adding the given membrane to any capabilities
|
cannam@147
|
139 // found within it. `from` is interpreted as "outside" the membrane while `to` is "inside".
|
cannam@147
|
140
|
cannam@147
|
141 template <typename Reader>
|
cannam@147
|
142 Orphan<typename kj::Decay<Reader>::Reads> copyOutOfMembrane(
|
cannam@147
|
143 Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy);
|
cannam@147
|
144 // Like copyIntoMembrane() except that `from` is "inside" the membrane and `to` is "outside".
|
cannam@147
|
145
|
cannam@147
|
146 // =======================================================================================
|
cannam@147
|
147 // inline implementation details
|
cannam@147
|
148
|
cannam@147
|
149 template <typename ClientType>
|
cannam@147
|
150 ClientType membrane(ClientType inner, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
151 return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
|
cannam@147
|
152 .castAs<typename ClientType::Calls>();
|
cannam@147
|
153 }
|
cannam@147
|
154 template <typename ClientType>
|
cannam@147
|
155 ClientType reverseMembrane(ClientType inner, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
156 return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
|
cannam@147
|
157 .castAs<typename ClientType::Calls>();
|
cannam@147
|
158 }
|
cannam@147
|
159
|
cannam@147
|
160 template <typename ServerType>
|
cannam@147
|
161 typename ServerType::Serves::Client membrane(
|
cannam@147
|
162 kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
163 return membrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
|
cannam@147
|
164 .castAs<typename ServerType::Serves>();
|
cannam@147
|
165 }
|
cannam@147
|
166 template <typename ServerType>
|
cannam@147
|
167 typename ServerType::Serves::Client reverseMembrane(
|
cannam@147
|
168 kj::Own<ServerType> inner, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
169 return reverseMembrane(Capability::Client(kj::mv(inner)), kj::mv(policy))
|
cannam@147
|
170 .castAs<typename ServerType::Serves>();
|
cannam@147
|
171 }
|
cannam@147
|
172
|
cannam@147
|
173 namespace _ { // private
|
cannam@147
|
174
|
cannam@147
|
175 OrphanBuilder copyOutOfMembrane(PointerReader from, Orphanage to,
|
cannam@147
|
176 kj::Own<MembranePolicy> policy, bool reverse);
|
cannam@147
|
177 OrphanBuilder copyOutOfMembrane(StructReader from, Orphanage to,
|
cannam@147
|
178 kj::Own<MembranePolicy> policy, bool reverse);
|
cannam@147
|
179 OrphanBuilder copyOutOfMembrane(ListReader from, Orphanage to,
|
cannam@147
|
180 kj::Own<MembranePolicy> policy, bool reverse);
|
cannam@147
|
181
|
cannam@147
|
182 } // namespace _ (private)
|
cannam@147
|
183
|
cannam@147
|
184 template <typename Reader>
|
cannam@147
|
185 Orphan<typename kj::Decay<Reader>::Reads> copyIntoMembrane(
|
cannam@147
|
186 Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
187 return _::copyOutOfMembrane(
|
cannam@147
|
188 _::PointerHelpers<typename kj::Decay<Reader>::Reads>::getInternalReader(from),
|
cannam@147
|
189 to, kj::mv(policy), true);
|
cannam@147
|
190 }
|
cannam@147
|
191
|
cannam@147
|
192 template <typename Reader>
|
cannam@147
|
193 Orphan<typename kj::Decay<Reader>::Reads> copyOutOfMembrane(
|
cannam@147
|
194 Reader&& from, Orphanage to, kj::Own<MembranePolicy> policy) {
|
cannam@147
|
195 return _::copyOutOfMembrane(
|
cannam@147
|
196 _::PointerHelpers<typename kj::Decay<Reader>::Reads>::getInternalReader(from),
|
cannam@147
|
197 to, kj::mv(policy), false);
|
cannam@147
|
198 }
|
cannam@147
|
199
|
cannam@147
|
200 } // namespace capnp
|
cannam@147
|
201
|
cannam@147
|
202 #endif // CAPNP_MEMBRANE_H_
|