Skip to content

Commit 9937669

Browse files
authored
Implement a capability restriction system. (proxy-wasm#89)
Signed-off-by: Ryan Apilado <[email protected]>
1 parent 3750104 commit 9937669

File tree

3 files changed

+137
-110
lines changed

3 files changed

+137
-110
lines changed

include/proxy-wasm/exports.h

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <memory>
1919

2020
#include "include/proxy-wasm/word.h"
21+
#include "include/proxy-wasm/wasm_vm.h"
2122

2223
namespace proxy_wasm {
2324

@@ -57,7 +58,7 @@ template <typename Pairs> void marshalPairs(const Pairs &result, char *buffer) {
5758
}
5859
}
5960

60-
// ABI functions exported from envoy to wasm.
61+
// ABI functions exported from host to wasm.
6162

6263
Word get_configuration(void *raw_context, Word address, Word size);
6364
Word get_status(void *raw_context, Word status_code, Word address, Word size);
@@ -153,5 +154,67 @@ Word pthread_equal(void *, Word left, Word right);
153154
// Any currently executing Wasm call context.
154155
::proxy_wasm::ContextBase *ContextOrEffectiveContext(::proxy_wasm::ContextBase *context);
155156

157+
#define FOR_ALL_HOST_FUNCTIONS(_f) \
158+
_f(log) _f(get_status) _f(set_property) _f(get_property) _f(send_local_response) \
159+
_f(get_shared_data) _f(set_shared_data) _f(register_shared_queue) _f(resolve_shared_queue) \
160+
_f(dequeue_shared_queue) _f(enqueue_shared_queue) _f(get_header_map_value) \
161+
_f(add_header_map_value) _f(replace_header_map_value) _f(remove_header_map_value) \
162+
_f(get_header_map_pairs) _f(set_header_map_pairs) _f(get_header_map_size) \
163+
_f(get_buffer_status) _f(get_buffer_bytes) _f(set_buffer_bytes) \
164+
_f(http_call) _f(grpc_call) _f(grpc_stream) _f(grpc_close) \
165+
_f(grpc_cancel) _f(grpc_send) _f(set_tick_period_milliseconds) \
166+
_f(get_current_time_nanoseconds) _f(define_metric) \
167+
_f(increment_metric) _f(record_metric) _f(get_metric) \
168+
_f(set_effective_context) _f(done) \
169+
_f(call_foreign_function)
170+
171+
#define FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_f) \
172+
_f(get_configuration) _f(continue_request) _f(continue_response) _f(clear_route_cache) \
173+
_f(continue_stream) _f(close_stream) _f(get_log_level)
174+
175+
#define FOR_ALL_WASI_FUNCTIONS(_f) \
176+
_f(fd_write) _f(fd_read) _f(fd_seek) _f(fd_close) _f(fd_fdstat_get) _f(environ_get) \
177+
_f(environ_sizes_get) _f(args_get) _f(args_sizes_get) _f(clock_time_get) _f(random_get) \
178+
_f(proc_exit)
179+
180+
// Helpers to generate a stub to pass to VM, in place of a restricted proxy-wasm capability.
181+
#define _CREATE_PROXY_WASM_STUB(_fn) \
182+
template <typename F> struct _fn##Stub; \
183+
template <typename... Args> struct _fn##Stub<Word(void *, Args...)> { \
184+
static Word stub(void *raw_context, Args...) { \
185+
auto context = exports::ContextOrEffectiveContext( \
186+
static_cast<ContextBase *>((void)raw_context, current_context_)); \
187+
context->wasmVm()->integration()->error( \
188+
"Attempted call to restricted proxy-wasm capability: proxy_" #_fn); \
189+
return WasmResult::InternalFailure; \
190+
} \
191+
};
192+
FOR_ALL_HOST_FUNCTIONS(_CREATE_PROXY_WASM_STUB)
193+
FOR_ALL_HOST_FUNCTIONS_ABI_SPECIFIC(_CREATE_PROXY_WASM_STUB)
194+
#undef _CREATE_PROXY_WASM_STUB
195+
196+
// Helpers to generate a stub to pass to VM, in place of a restricted WASI capability.
197+
#define _CREATE_WASI_STUB(_fn) \
198+
template <typename F> struct _fn##Stub; \
199+
template <typename... Args> struct _fn##Stub<Word(void *, Args...)> { \
200+
static Word stub(void *raw_context, Args...) { \
201+
auto context = exports::ContextOrEffectiveContext( \
202+
static_cast<ContextBase *>((void)raw_context, current_context_)); \
203+
context->wasmVm()->integration()->error( \
204+
"Attempted call to restricted WASI capability: " #_fn); \
205+
return 76; /* __WASI_ENOTCAPABLE */ \
206+
} \
207+
}; \
208+
template <typename... Args> struct _fn##Stub<void(void *, Args...)> { \
209+
static void stub(void *raw_context, Args...) { \
210+
auto context = exports::ContextOrEffectiveContext( \
211+
static_cast<ContextBase *>((void)raw_context, current_context_)); \
212+
context->wasmVm()->integration()->error( \
213+
"Attempted call to restricted WASI capability: " #_fn); \
214+
} \
215+
};
216+
FOR_ALL_WASI_FUNCTIONS(_CREATE_WASI_STUB)
217+
#undef _CREATE_WASI_STUB
218+
156219
} // namespace exports
157220
} // namespace proxy_wasm

include/proxy-wasm/wasm.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,18 @@ using WasmForeignFunction =
4242
using WasmVmFactory = std::function<std::unique_ptr<WasmVm>()>;
4343
using CallOnThreadFunction = std::function<void(std::function<void()>)>;
4444

45+
struct SanitizationConfig {
46+
std::vector<std::string> argument_list;
47+
bool is_allowlist;
48+
};
49+
using AllowedCapabilitiesMap = std::unordered_map<std::string, SanitizationConfig>;
50+
4551
// Wasm execution instance. Manages the host side of the Wasm interface.
4652
class WasmBase : public std::enable_shared_from_this<WasmBase> {
4753
public:
4854
WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id,
49-
std::string_view vm_configuration, std::string_view vm_key);
55+
std::string_view vm_configuration, std::string_view vm_key,
56+
AllowedCapabilitiesMap allowed_capabilities);
5057
WasmBase(const std::shared_ptr<WasmHandleBase> &other, WasmVmFactory factory);
5158
virtual ~WasmBase();
5259

@@ -92,6 +99,12 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
9299
return nullptr;
93100
}
94101

102+
// Capability restriction (restricting/exposing the ABI).
103+
bool capabilityAllowed(std::string capability_name) {
104+
return allowed_capabilities_.empty() ||
105+
allowed_capabilities_.find(capability_name) != allowed_capabilities_.end();
106+
}
107+
95108
virtual ContextBase *createVmContext() { return new ContextBase(this); }
96109
virtual ContextBase *createRootContext(const std::shared_ptr<PluginBase> &plugin) {
97110
return new ContextBase(this, plugin);
@@ -225,6 +238,20 @@ class WasmBase : public std::enable_shared_from_this<WasmBase> {
225238
WasmCallVoid<1> on_log_;
226239
WasmCallVoid<1> on_delete_;
227240

241+
#define FOR_ALL_MODULE_FUNCTIONS(_f) \
242+
_f(validate_configuration) _f(on_vm_start) _f(on_configure) _f(on_tick) _f(on_context_create) \
243+
_f(on_new_connection) _f(on_downstream_data) _f(on_upstream_data) \
244+
_f(on_downstream_connection_close) _f(on_upstream_connection_close) _f(on_request_body) \
245+
_f(on_request_trailers) _f(on_request_metadata) _f(on_response_body) \
246+
_f(on_response_trailers) _f(on_response_metadata) _f(on_http_call_response) \
247+
_f(on_grpc_receive) _f(on_grpc_close) _f(on_grpc_receive_initial_metadata) \
248+
_f(on_grpc_receive_trailing_metadata) _f(on_queue_ready) _f(on_done) \
249+
_f(on_log) _f(on_delete)
250+
251+
// Capabilities which are allowed to be linked to the module. If this is empty, restriction
252+
// is not enforced.
253+
AllowedCapabilitiesMap allowed_capabilities_;
254+
228255
std::shared_ptr<WasmHandleBase> base_wasm_handle_;
229256

230257
// Used by the base_wasm to enable non-clonable thread local Wasm(s) to be constructed.

src/wasm.cc

Lines changed: 45 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -100,83 +100,30 @@ void WasmBase::registerCallbacks() {
100100
_REGISTER(pthread_equal);
101101
#undef _REGISTER
102102

103-
#define _REGISTER_WASI(_fn) \
104-
wasm_vm_->registerCallback( \
105-
"wasi_unstable", #_fn, &exports::wasi_unstable_##_fn, \
106-
&ConvertFunctionWordToUint32<decltype(exports::wasi_unstable_##_fn), \
107-
exports::wasi_unstable_##_fn>::convertFunctionWordToUint32); \
108-
wasm_vm_->registerCallback( \
109-
"wasi_snapshot_preview1", #_fn, &exports::wasi_unstable_##_fn, \
110-
&ConvertFunctionWordToUint32<decltype(exports::wasi_unstable_##_fn), \
111-
exports::wasi_unstable_##_fn>::convertFunctionWordToUint32)
112-
_REGISTER_WASI(fd_write);
113-
_REGISTER_WASI(fd_read);
114-
_REGISTER_WASI(fd_seek);
115-
_REGISTER_WASI(fd_close);
116-
_REGISTER_WASI(fd_fdstat_get);
117-
_REGISTER_WASI(environ_get);
118-
_REGISTER_WASI(environ_sizes_get);
119-
_REGISTER_WASI(args_get);
120-
_REGISTER_WASI(args_sizes_get);
121-
_REGISTER_WASI(clock_time_get);
122-
_REGISTER_WASI(random_get);
123-
_REGISTER_WASI(proc_exit);
124-
#undef _REGISTER_WASI
125-
126-
// Calls with the "proxy_" prefix.
127-
#define _REGISTER_PROXY(_fn) \
128-
wasm_vm_->registerCallback( \
129-
"env", "proxy_" #_fn, &exports::_fn, \
130-
&ConvertFunctionWordToUint32<decltype(exports::_fn), \
131-
exports::_fn>::convertFunctionWordToUint32);
132-
_REGISTER_PROXY(log);
133-
134-
_REGISTER_PROXY(get_status);
135-
136-
_REGISTER_PROXY(set_property);
137-
_REGISTER_PROXY(get_property);
138-
139-
_REGISTER_PROXY(send_local_response);
140-
141-
_REGISTER_PROXY(get_shared_data);
142-
_REGISTER_PROXY(set_shared_data);
143-
144-
_REGISTER_PROXY(register_shared_queue);
145-
_REGISTER_PROXY(resolve_shared_queue);
146-
_REGISTER_PROXY(dequeue_shared_queue);
147-
_REGISTER_PROXY(enqueue_shared_queue);
148-
149-
_REGISTER_PROXY(get_header_map_value);
150-
_REGISTER_PROXY(add_header_map_value);
151-
_REGISTER_PROXY(replace_header_map_value);
152-
_REGISTER_PROXY(remove_header_map_value);
153-
_REGISTER_PROXY(get_header_map_pairs);
154-
_REGISTER_PROXY(set_header_map_pairs);
155-
_REGISTER_PROXY(get_header_map_size);
156-
157-
_REGISTER_PROXY(get_buffer_status);
158-
_REGISTER_PROXY(get_buffer_bytes);
159-
_REGISTER_PROXY(set_buffer_bytes);
160-
161-
_REGISTER_PROXY(http_call);
162-
163-
_REGISTER_PROXY(grpc_call);
164-
_REGISTER_PROXY(grpc_stream);
165-
_REGISTER_PROXY(grpc_close);
166-
_REGISTER_PROXY(grpc_cancel);
167-
_REGISTER_PROXY(grpc_send);
168-
169-
_REGISTER_PROXY(set_tick_period_milliseconds);
170-
_REGISTER_PROXY(get_current_time_nanoseconds);
171-
172-
_REGISTER_PROXY(define_metric);
173-
_REGISTER_PROXY(increment_metric);
174-
_REGISTER_PROXY(record_metric);
175-
_REGISTER_PROXY(get_metric);
176-
177-
_REGISTER_PROXY(set_effective_context);
178-
_REGISTER_PROXY(done);
179-
_REGISTER_PROXY(call_foreign_function);
103+
// Register the capability with the VM if it has been allowed, otherwise register a stub.
104+
#define _REGISTER(module_name, name_prefix, export_prefix, _fn) \
105+
if (capabilityAllowed(name_prefix #_fn)) { \
106+
wasm_vm_->registerCallback( \
107+
module_name, name_prefix #_fn, &exports::export_prefix##_fn, \
108+
&ConvertFunctionWordToUint32<decltype(exports::export_prefix##_fn), \
109+
exports::export_prefix##_fn>::convertFunctionWordToUint32); \
110+
} else { \
111+
typedef decltype(exports::export_prefix##_fn) export_type; \
112+
constexpr export_type *stub = &exports::_fn##Stub<export_type>::stub; \
113+
wasm_vm_->registerCallback( \
114+
module_name, name_prefix #_fn, stub, \
115+
&ConvertFunctionWordToUint32<export_type, stub>::convertFunctionWordToUint32); \
116+
}
117+
118+
#define _REGISTER_WASI_UNSTABLE(_fn) _REGISTER("wasi_unstable", , wasi_unstable_, _fn)
119+
#define _REGISTER_WASI_SNAPSHOT(_fn) _REGISTER("wasi_snapshot_preview1", , wasi_unstable_, _fn)
120+
FOR_ALL_WASI_FUNCTIONS(_REGISTER_WASI_UNSTABLE);
121+
FOR_ALL_WASI_FUNCTIONS(_REGISTER_WASI_SNAPSHOT);
122+
#undef _REGISTER_WASI_UNSTABLE
123+
#undef _REGISTER_WASI_SNAPSHOT
124+
125+
#define _REGISTER_PROXY(_fn) _REGISTER("env", "proxy_", , _fn)
126+
FOR_ALL_HOST_FUNCTIONS(_REGISTER_PROXY);
180127

181128
if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) {
182129
_REGISTER_PROXY(get_configuration);
@@ -192,6 +139,8 @@ void WasmBase::registerCallbacks() {
192139
_REGISTER_PROXY(get_log_level);
193140
}
194141
#undef _REGISTER_PROXY
142+
143+
#undef _REGISTER
195144
}
196145

197146
void WasmBase::getFunctions() {
@@ -211,36 +160,21 @@ void WasmBase::getFunctions() {
211160
#undef _GET_ALIAS
212161
#undef _GET
213162

214-
#define _GET_PROXY(_fn) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_);
215-
#define _GET_PROXY_ABI(_fn, _abi) wasm_vm_->getFunction("proxy_" #_fn, &_fn##_abi##_);
216-
_GET_PROXY(validate_configuration);
217-
_GET_PROXY(on_vm_start);
218-
_GET_PROXY(on_configure);
219-
_GET_PROXY(on_tick);
220-
221-
_GET_PROXY(on_context_create);
222-
223-
_GET_PROXY(on_new_connection);
224-
_GET_PROXY(on_downstream_data);
225-
_GET_PROXY(on_upstream_data);
226-
_GET_PROXY(on_downstream_connection_close);
227-
_GET_PROXY(on_upstream_connection_close);
228-
229-
_GET_PROXY(on_request_body);
230-
_GET_PROXY(on_request_trailers);
231-
_GET_PROXY(on_request_metadata);
232-
_GET_PROXY(on_response_body);
233-
_GET_PROXY(on_response_trailers);
234-
_GET_PROXY(on_response_metadata);
235-
_GET_PROXY(on_http_call_response);
236-
_GET_PROXY(on_grpc_receive);
237-
_GET_PROXY(on_grpc_close);
238-
_GET_PROXY(on_grpc_receive_initial_metadata);
239-
_GET_PROXY(on_grpc_receive_trailing_metadata);
240-
_GET_PROXY(on_queue_ready);
241-
_GET_PROXY(on_done);
242-
_GET_PROXY(on_log);
243-
_GET_PROXY(on_delete);
163+
// Try to point the capability to one of the module exports, if the capability has been allowed.
164+
#define _GET_PROXY(_fn) \
165+
if (capabilityAllowed("proxy_" #_fn)) { \
166+
wasm_vm_->getFunction("proxy_" #_fn, &_fn##_); \
167+
} else { \
168+
_fn##_ = nullptr; \
169+
}
170+
#define _GET_PROXY_ABI(_fn, _abi) \
171+
if (capabilityAllowed("proxy_" #_fn)) { \
172+
wasm_vm_->getFunction("proxy_" #_fn, &_fn##_abi##_); \
173+
} else { \
174+
_fn##_abi##_ = nullptr; \
175+
}
176+
177+
FOR_ALL_MODULE_FUNCTIONS(_GET_PROXY);
244178

245179
if (abiVersion() == AbiVersion::ProxyWasm_0_1_0) {
246180
_GET_PROXY_ABI(on_request_headers, _abi_01);
@@ -259,6 +193,7 @@ WasmBase::WasmBase(const std::shared_ptr<WasmHandleBase> &base_wasm_handle, Wasm
259193
: std::enable_shared_from_this<WasmBase>(*base_wasm_handle->wasm()),
260194
vm_id_(base_wasm_handle->wasm()->vm_id_), vm_key_(base_wasm_handle->wasm()->vm_key_),
261195
started_from_(base_wasm_handle->wasm()->wasm_vm()->cloneable()),
196+
allowed_capabilities_(base_wasm_handle->wasm()->allowed_capabilities_),
262197
base_wasm_handle_(base_wasm_handle) {
263198
if (started_from_ != Cloneable::NotCloneable) {
264199
wasm_vm_ = base_wasm_handle->wasm()->wasm_vm()->clone();
@@ -273,8 +208,10 @@ WasmBase::WasmBase(const std::shared_ptr<WasmHandleBase> &base_wasm_handle, Wasm
273208
}
274209

275210
WasmBase::WasmBase(std::unique_ptr<WasmVm> wasm_vm, std::string_view vm_id,
276-
std::string_view vm_configuration, std::string_view vm_key)
211+
std::string_view vm_configuration, std::string_view vm_key,
212+
AllowedCapabilitiesMap allowed_capabilities)
277213
: vm_id_(std::string(vm_id)), vm_key_(std::string(vm_key)), wasm_vm_(std::move(wasm_vm)),
214+
allowed_capabilities_(std::move(allowed_capabilities)),
278215
vm_configuration_(std::string(vm_configuration)), vm_id_handle_(getVmIdHandle(vm_id)) {
279216
if (!wasm_vm_) {
280217
failed_ = FailState::UnableToCreateVM;

0 commit comments

Comments
 (0)