Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: try to parse as ProtoJSON #233

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <fcntl.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/util/json_util.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/personality.h>
Expand All @@ -32,6 +33,7 @@
#include <sys/types.h>

#include <fstream>
#include <list>
#include <string>
#include <vector>

Expand Down Expand Up @@ -302,39 +304,69 @@ static bool parseInternal(nsjconf_t* nsjconf, const nsjail::NsJailConfig& njc) {
return true;
}

static std::list<std::string> error_messages;
Copy link
Contributor Author

@tomfitzhenry tomfitzhenry Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% on this. I see https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables advises against this, since list has a non-trivial destructor. Thoughts?


static void logHandler(
google::protobuf::LogLevel level, const char* filename, int line, const std::string& message) {
LOG_W("config.cc: '%s'", message.c_str());
error_messages.push_back(message);
}

static void flushLog() {
for (auto message : error_messages) {
LOG_W("config.cc: '%s'", message.c_str());
}
error_messages.clear();
}

bool parseFile(nsjconf_t* nsjconf, const char* file) {
LOG_D("Parsing configuration from '%s'", file);

int fd = TEMP_FAILURE_RETRY(open(file, O_RDONLY | O_CLOEXEC));
if (fd == -1) {
std::ifstream ifs(file);
if (!ifs.is_open()) {
PLOG_W("Couldn't open config file '%s'", file);
return false;
}

google::protobuf::SetLogHandler(logHandler);
google::protobuf::io::FileInputStream input(fd);
input.SetCloseOnDelete(true);
std::string conf((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));

/* Use static so we can get c_str() pointers, and copy them into the nsjconf struct */
static nsjail::NsJailConfig nsc;
static nsjail::NsJailConfig json_nsc;
static nsjail::NsJailConfig text_nsc;

google::protobuf::SetLogHandler(logHandler);
auto json_status = google::protobuf::util::JsonStringToMessage(conf, &json_nsc);
bool text_parsed = google::protobuf::TextFormat::ParseFromString(conf, &text_nsc);

auto parser = google::protobuf::TextFormat::Parser();
if (!parser.Parse(&input, &nsc)) {
LOG_W("Couldn't parse file '%s' from Text into ProtoBuf", file);
if (json_status.ok() && text_parsed) {
LOG_W("Config file '%s' ambiguously parsed as TextProto and ProtoJSON", file);
return false;
}
if (!parseInternal(nsjconf, nsc)) {
LOG_W("Couldn't parse the ProtoBuf from '%s'", file);

if (!json_status.ok() && !text_parsed) {
LOG_W("Config file '%s' failed to parse as either TextProto or ProtoJSON", file);
flushLog();
LOG_W("config.cc: ProtoJSON parse status: '%s'", json_status.ToString().c_str());
return false;
}

LOG_D("Parsed config from '%s':\n'%s'", file, nsc.DebugString().c_str());
return true;
if (json_status.ok() && !text_parsed) {
if (!parseInternal(nsjconf, json_nsc)) {
LOG_W("Couldn't parse the ProtoJSON from '%s'", file);
return false;
}
LOG_D("Parsed JSON config from '%s':\n'%s'", file, json_nsc.DebugString().c_str());
return true;
}

if (text_parsed && !json_status.ok()) {
if (!parseInternal(nsjconf, text_nsc)) {
LOG_W("Couldn't parse the TextProto from '%s'", file);
return false;
}
LOG_D("Parsed TextProto config from '%s':\n'%s'", file, text_nsc.DebugString().c_str());
return true;
}
return false;
}

} // namespace config
170 changes: 170 additions & 0 deletions configs/bash-with-fake-geteuid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"name": "bash-with-fake-geteuid",
"description": [
"An example/demo policy which allows to execute /bin/bash and other commands in ",
"a fairly restricted jail containing only some directories from the main ",
"system, and with blocked __NR_syslog syscall. Also, __NR_geteuid returns -1337 ",
"value, which /usr/bin/id will show as euid=4294965959, and ptrace is blocked ",
"but returns success, hence strange behavior of the strace command. ",
"This is an example/demo policy, hence it repeats many default values from the ",
"https://github.com/google/nsjail/blob/master/config.proto PB schema "
],
"mode": "ONCE",
"hostname": "JAILED-BASH",
"cwd": "/tmp",
"port": 31337,
"bindhost": "127.0.0.1",
"maxConnsPerIp": 10,
"timeLimit": 100,
"daemon": false,
"maxCpus": 1,
"keepEnv": false,
"envar": [
"ENVAR1=VALUE1",
"ENVAR2=VALUE2",
"TERM=linux",
"HOME=/",
"PS1=[\\H:\\t:\\s-\\V:\\w]\\$ "
],
"keepCaps": true,
"cap": [
"CAP_NET_ADMIN",
"CAP_NET_RAW"
],
"silent": false,
"skipSetsid": true,
"stderrToNull": false,
"passFd": [
100,
3
],
"disableNoNewPrivs": false,
"rlimitAs": "128",
"rlimitCore": "0",
"rlimitCpu": "10",
"rlimitFsize": "0",
"rlimitNofile": "32",
"rlimitNprocType": "SOFT",
"rlimitStackType": "SOFT",
"personaAddrCompatLayout": false,
"personaMmapPageZero": false,
"personaReadImpliesExec": false,
"personaAddrLimit3gb": false,
"personaAddrNoRandomize": false,
"cloneNewnet": true,
"cloneNewuser": true,
"cloneNewns": true,
"cloneNewpid": true,
"cloneNewipc": true,
"cloneNewuts": true,
"cloneNewcgroup": true,
"uidmap": [
{
"insideId": "0",
"outsideId": "",
"count": 1
}
],
"gidmap": [
{
"insideId": "0",
"outsideId": "",
"count": 1
}
],
"mountProc": false,
"mount": [
{
"src": "/lib",
"dst": "/lib",
"isBind": true,
"rw": false
},
{
"src": "/bin",
"dst": "/bin",
"isBind": true,
"rw": false
},
{
"src": "/sbin",
"dst": "/sbin",
"isBind": true,
"rw": false
},
{
"src": "/usr",
"dst": "/usr",
"isBind": true,
"rw": false
},
{
"src": "/lib64",
"dst": "/lib64",
"isBind": true,
"rw": false,
"mandatory": false
},
{
"src": "/lib32",
"dst": "/lib32",
"isBind": true,
"rw": false,
"mandatory": false
},
{
"dst": "/tmp",
"fstype": "tmpfs",
"isBind": false,
"rw": true,
"nosuid": true,
"nodev": true,
"noexec": true
},
{
"src": "/dev/null",
"dst": "/dev/null",
"isBind": true,
"rw": true
},
{
"dst": "/proc",
"fstype": "proc",
"rw": false
},
{
"srcContent": "VGhpcyBmaWxlIHdhcyBjcmVhdGVkIGR5bmFtaWNhbGx5",
"dst": "/DYNAMIC_FILE"
},
{
"src": "/nonexistent_777",
"dst": "/nonexistent_777",
"isBind": true,
"mandatory": false
},
{
"src": "/proc/self/fd",
"dst": "/dev/fd",
"isSymlink": true
},
{
"src": "/some/unimportant/target",
"dst": "/proc/no/symlinks/can/be/created/in/proc",
"mandatory": false,
"isSymlink": true
}
],
"seccompString": [
"ERRNO(1337) { geteuid }\t",
"ERRNO(1) { ptrace, sched_setaffinity }\t\t",
"KILL_PROCESS { syslog }\t\t",
"DEFAULT ALLOW\t\t\t"
],
"execBin": {
"path": "/bin/bash",
"arg": [
"-i"
],
"arg0": "sh"
}
}
Loading