Skip to content

Commit

Permalink
src: use policy per environment
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelGSS committed Jul 31, 2022
1 parent c99d946 commit 4dbc8ac
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 92 deletions.
4 changes: 4 additions & 0 deletions src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,10 @@ inline TickInfo* Environment::tick_info() {
return &tick_info_;
}

inline policy::Policy* Environment::policy() {
return &policy_;
}

inline uint64_t Environment::timer_base() const {
return timer_base_;
}
Expand Down
4 changes: 4 additions & 0 deletions src/env.cc
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,10 @@ Environment::Environment(IsolateData* isolate_data,
"args",
std::move(traced_value));
}

policy()->Apply(
options_->policy_deny_fs,
policy::Permission::kFileSystem);
}

Environment::Environment(IsolateData* isolate_data,
Expand Down
3 changes: 3 additions & 0 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "util.h"
#include "uv.h"
#include "v8.h"
#include "policy/policy.h"

#include <array>
#include <atomic>
Expand Down Expand Up @@ -1160,6 +1161,7 @@ class Environment : public MemoryRetainer {
inline ImmediateInfo* immediate_info();
inline TickInfo* tick_info();
inline uint64_t timer_base() const;
inline policy::Policy* policy();
inline std::shared_ptr<KVStore> env_vars();
inline void set_env_vars(std::shared_ptr<KVStore> env_vars);

Expand Down Expand Up @@ -1526,6 +1528,7 @@ class Environment : public MemoryRetainer {
AsyncHooks async_hooks_;
ImmediateInfo immediate_info_;
TickInfo tick_info_;
policy::Policy policy_;
const uint64_t timer_base_;
std::shared_ptr<KVStore> env_vars_;
bool printed_error_ = false;
Expand Down
8 changes: 0 additions & 8 deletions src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -858,14 +858,6 @@ int ProcessGlobalArgs(std::vector<std::string>* args,

if (v8_args_as_char_ptr.size() > 1) return 9;

if (policy::root_policy.Apply(
per_process::cli_options->policy_deny_fs,
policy::Permission::kFileSystem).IsNothing()) {
errors->emplace_back(
"invalid permissions passed to --policy-deny-fs");
return 12;
}

return 0;
}

Expand Down
2 changes: 0 additions & 2 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1622,8 +1622,6 @@ static void RealPath(const FunctionCallbackInfo<Value>& args) {

BufferValue path(isolate, args[0]);
CHECK_NOT_NULL(*path);
THROW_IF_INSUFFICIENT_PERMISSIONS(
env, policy::Permission::kFileSystemIn, *path);

const enum encoding encoding = ParseEncoding(isolate, args[1], UTF8);

Expand Down
9 changes: 5 additions & 4 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
&EnvironmentOptions::experimental_policy_integrity,
kAllowedInEnvironment);
Implies("--policy-integrity", "[has_policy_integrity_string]");

AddOption("--policy-deny-fs",
"denied permissions to the filesystem",
&EnvironmentOptions::policy_deny_fs,
kAllowedInEnvironment);
AddOption("--experimental-repl-await",
"experimental await keyword support in REPL",
&EnvironmentOptions::experimental_repl_await,
Expand Down Expand Up @@ -859,10 +864,6 @@ PerProcessOptionsParser::PerProcessOptionsParser(
"force FIPS crypto (cannot be disabled)",
&PerProcessOptions::force_fips_crypto,
kAllowedInEnvironment);
AddOption("--policy-deny-fs",
"denied permissions to the filesystem",
&PerProcessOptions::policy_deny_fs,
kAllowedInEnvironment);
AddOption("--secure-heap",
"total size of the OpenSSL secure heap",
&PerProcessOptions::secure_heap,
Expand Down
3 changes: 1 addition & 2 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class EnvironmentOptions : public Options {
std::string experimental_policy;
std::string experimental_policy_integrity;
bool has_policy_integrity_string = false;
std::string policy_deny_fs;
bool experimental_repl_await = true;
bool experimental_vm_modules = false;
bool expose_internals = false;
Expand Down Expand Up @@ -244,8 +245,6 @@ class PerProcessOptions : public Options {
bool print_v8_help = false;
bool print_version = false;

std::string policy_deny_fs;

#ifdef NODE_HAVE_I18N_SUPPORT
std::string icu_data_dir;
#endif
Expand Down
60 changes: 33 additions & 27 deletions src/policy/policy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,25 @@

#include "v8.h"

#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <iostream>

namespace node {

using v8::Array;
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Integer;
using v8::Just;
using v8::Local;
using v8::Maybe;
using v8::Nothing;
using v8::Object;
using v8::String;
using v8::Value;

namespace policy {

// The root policy is establish at process start using
// the --policy-deny-* command line arguments.
Policy root_policy;

namespace {

// policy.deny('fs.in', ['/tmp/'])
Expand All @@ -43,26 +38,30 @@ static void Deny(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
CHECK(args.Length() >= 2 || args[1]->IsArray());

std::string denyScope = *String::Utf8Value(isolate, args[0]);
Permission scope = Policy::StringToPermission(denyScope);
std::string deny_scope = *String::Utf8Value(isolate, args[0]);
Permission scope = Policy::StringToPermission(deny_scope);
if (scope == Permission::kPermissionsRoot) {
return args.GetReturnValue().Set(false);
}

Local<Array> jsParams = Local<Array>::Cast(args[1]);
std::vector<std::string> params;
for (uint32_t i = 0; i < jsParams->Length(); ++i) {
Local<Value> arg(
jsParams
->Get(isolate->GetCurrentContext(), Integer::New(isolate, i))
.ToLocalChecked());
Local<Array> js_params = Local<Array>::Cast(args[1]);
Local<Context> context = isolate->GetCurrentContext();

std::vector<std::string> params;
for (uint32_t i = 0; i < js_params->Length(); ++i) {
Local<Value> arg;
if (!js_params->Get(context, Integer::New(isolate, i)).ToLocal(&arg)) {
return;
}
String::Utf8Value utf8_arg(isolate, arg);
if (*utf8_arg == nullptr) {
return;
}
params.push_back(*utf8_arg);
}

return args.GetReturnValue()
.Set(root_policy.Deny(scope, params));
.Set(env->policy()->Deny(scope, params));
}

// policy.check('fs.in', '/tmp/')
Expand All @@ -73,15 +72,23 @@ static void Check(const FunctionCallbackInfo<Value>& args) {
CHECK(args[0]->IsString());
CHECK(args.Length() >= 2 || args[1]->IsString());

std::string denyScope = *String::Utf8Value(isolate, args[0]);
Permission scope = Policy::StringToPermission(denyScope);
String::Utf8Value utf8_deny_scope(isolate, args[0]);
if (*utf8_deny_scope == nullptr) {
return;
}

const std::string deny_scope = *utf8_deny_scope;
Permission scope = Policy::StringToPermission(deny_scope);
if (scope == Permission::kPermissionsRoot) {
// TODO(rafaelgss): throw?
return args.GetReturnValue().Set(false);
}

return args.GetReturnValue()
.Set(root_policy.is_granted(scope, *String::Utf8Value(isolate, args[1])));
String::Utf8Value utf8_arg(isolate, args[1]);
if (*utf8_arg == nullptr) {
return;
}

return args.GetReturnValue().Set(env->policy()->is_granted(scope, *utf8_arg));
}

} // namespace
Expand All @@ -96,7 +103,7 @@ const char* Policy::PermissionToString(const Permission perm) {

#define V(Name, label, _) \
if (perm == label) return Permission::k##Name;
Permission Policy::StringToPermission(std::string perm) {
Permission Policy::StringToPermission(const std::string& perm) {
PERMISSIONS(V)
return Permission::kPermissionsRoot;
}
Expand All @@ -115,15 +122,14 @@ void Policy::ThrowAccessDenied(Environment* env, Permission perm) {
env->isolate()->ThrowException(err);
}

Maybe<bool> Policy::Apply(const std::string& deny, Permission scope) {
void Policy::Apply(const std::string& deny, Permission scope) {
auto policy = deny_policies.find(scope);
if (policy != deny_policies.end()) {
return policy->second->Apply(deny);
policy->second->Apply(deny);
}
return Just(false);
}

bool Policy::Deny(Permission scope, std::vector<std::string> params) {
bool Policy::Deny(Permission scope, const std::vector<std::string>& params) {
auto policy = deny_policies.find(scope);
if (policy != deny_policies.end()) {
return policy->second->Deny(scope, params);
Expand Down
27 changes: 14 additions & 13 deletions src/policy/policy.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ class Environment;
namespace policy {

#define THROW_IF_INSUFFICIENT_PERMISSIONS(env, perm_, resource_, ...) \
if (!node::policy::root_policy.is_granted(perm_, resource_)) { \
return node::policy::Policy::ThrowAccessDenied((env), perm_); \
}
do { \
if (UNLIKELY(!(env)->policy()->is_granted(perm_, resource_))) { \
node::policy::Policy::ThrowAccessDenied((env), perm_); \
return __VA_ARGS__; \
} \
} while (0)

class Policy {
public:
// TODO(rafaelgss): release pointers
Policy() {
auto denyFs = new PolicyDenyFs();
#define V(Name, _, __) \
deny_policies.insert(std::make_pair(Permission::k##Name, denyFs));
FILESYSTEM_PERMISSIONS(V)
std::shared_ptr<PolicyDeny> deny_fs = std::make_shared<PolicyDenyFs>();
#define V(Name, _, __) \
deny_policies.insert(std::make_pair(Permission::k##Name, deny_fs));
FILESYSTEM_PERMISSIONS(V)
#undef V
}

Expand All @@ -45,20 +47,19 @@ class Policy {
return is_granted(permission, res.c_str());
}

static Permission StringToPermission(std::string perm);
static Permission StringToPermission(const std::string& perm);
static const char* PermissionToString(Permission perm);
static void ThrowAccessDenied(Environment* env, Permission perm);

// CLI Call
v8::Maybe<bool> Apply(const std::string& deny, Permission scope);
void Apply(const std::string& deny, Permission scope);
// Policy.Deny API
bool Deny(Permission scope, std::vector<std::string> params);
bool Deny(Permission scope, const std::vector<std::string>& params);

private:
std::map<Permission, PolicyDeny*> deny_policies;
std::map<Permission, std::shared_ptr<PolicyDeny>> deny_policies;
};

extern policy::Policy root_policy;
} // namespace policy

} // namespace node
Expand Down
5 changes: 3 additions & 2 deletions src/policy/policy_deny.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ enum class Permission {

class PolicyDeny {
public:
virtual v8::Maybe<bool> Apply(const std::string& deny) = 0;
virtual bool Deny(Permission scope, std::vector<std::string> params) = 0;
virtual void Apply(const std::string& deny) = 0;
virtual bool Deny(Permission scope,
const std::vector<std::string>& params) = 0;
virtual bool is_granted(Permission perm, const std::string& param = "") = 0;
};

Expand Down
Loading

0 comments on commit 4dbc8ac

Please sign in to comment.