Compare commits

7 Commits

Author SHA1 Message Date
8d685dc581 Makefile: Remove -lseccomp as we don't need it anymore 2021-09-29 18:33:45 +02:00
ed43f5f700 submodules: update cpp-httplib 2021-09-29 18:28:18 +02:00
10f00aeb45 main: Pass absolute path of config file
As sandboxing code chroots and chdirs away,
2021-09-23 17:13:08 +02:00
67eb8b6428 sandbox: adjust to latest qssb.h 2021-09-23 17:13:08 +02:00
f26fd19fb4 submodules: sync with latest upstream 2021-09-23 17:13:08 +02:00
204a72da1f setup: Fix broken FTS DELETE op
Thie previous DELETE statement lead to strange
behaviours. It was pure luck this did not blow up
before all these years. It appears it may leave the index
in an undefined state, and the database recently started
to display strange behaviour in connection with newer sqlite
version.

Now, just remove the previous revision from the FTS index,
as for now, search only cares about the most recent revisions.

Also, remove redundant UPDATE trigger on revision table
We never update revisions, thus such trigger is simply
redundant.

Relevant: https://gitlab.gnome.org/GNOME/tracker/-/merge_requests/353
2021-09-23 17:13:08 +02:00
88816a4015 utils: html_xss(): Add ' and &
They REALLY should have been there from the beginning...
2021-06-15 18:37:52 +02:00
7 changed files with 83 additions and 67 deletions

View File

@ -2,7 +2,7 @@
CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra CXXFLAGS=-std=c++17 -O0 -g -no-pie -pipe -MMD -Wall -Wextra
RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra RELEASE_CXXFLAGS=-std=c++17 -O3 -pipe -MMD -Wall -Wextra
LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs -lseccomp LDFLAGS=-lsqlite3 -lpthread -lcrypto -lstdc++fs
INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h INCLUDEFLAGS=-I submodules/sqlitemoderncpp/hdr -I submodules/cpp-httplib -I submodules/qssb.h
CXX=g++ CXX=g++

View File

@ -77,21 +77,21 @@ int main(int argc, char **argv)
Logger::error() << "Sandbox is not supported, exiting"; Logger::error() << "Sandbox is not supported, exiting";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if(argc < 2)
{
std::cerr << "no path to config file provided" << std::endl;
return 1;
}
std::string configpath = std::filesystem::absolute(argv[1]).string();
if(!sandbox->enableForInit()) if(!sandbox->enableForInit())
{ {
Logger::error() << "Sandboxing for init mode could not be activated."; Logger::error() << "Sandboxing for init mode could not be activated.";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if(argc < 2)
{
std::cerr << "no path to config file provided" << std::endl;
return 1;
}
try try
{ {
ConfigReader configreader(argv[1]); ConfigReader configreader(configpath);
Config config = configreader.readConfig(); Config config = configreader.readConfig();
// TODO: config.connectiontring only works as long as we only support sqlite of course // TODO: config.connectiontring only works as long as we only support sqlite of course

View File

@ -26,16 +26,26 @@
bool SandboxLinux::enableForInit() bool SandboxLinux::enableForInit()
{ {
umask(0027); umask(0027);
struct qssb_policy policy = {0}; struct qssb_policy *policy = qssb_init_policy();
int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1}; if(policy == NULL)
policy.blacklisted_syscalls = blacklisted_syscalls;
policy.no_new_privs = 1;
int result = qssb_enable_policy(&policy);
if(result != 0)
{ {
Logger::error() << "Failed to install sandboxing policy (init): " << result; Logger::error() << "Failed to init sandboxing policy (init)";
return false; return false;
} }
policy->namespace_options = QSSB_UNSHARE_USER;
policy->drop_caps = 0;
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execveat));
qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execve));
qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
int result = qssb_enable_policy(policy);
if(result != 0)
{
Logger::error() << "Failed to enable sandboxing policy (init): " << result;
qssb_free_policy(policy);
return false;
}
qssb_free_policy(policy);
return true; return true;
} }
@ -44,28 +54,34 @@ bool SandboxLinux::enablePreWorker(std::vector<std::string> fsPaths)
std::sort(fsPaths.begin(), fsPaths.end(), std::sort(fsPaths.begin(), fsPaths.end(),
[](const std::string &a, const std::string &b) { return a.length() < b.length(); }); [](const std::string &a, const std::string &b) { return a.length() < b.length(); });
struct qssb_path_policy *policies = new qssb_path_policy[fsPaths.size()]; struct qssb_policy *policy = qssb_init_policy();
if(policy == NULL)
{
Logger::error() << "Failed to init sandboxing policy (pre)";
return false;
}
for(unsigned int i = 0; i < fsPaths.size(); i++) for(unsigned int i = 0; i < fsPaths.size(); i++)
{ {
policies[i].next = policies + (i + 1); qssb_append_path_policy(policy, QSSB_FS_ALLOW_READ | QSSB_FS_ALLOW_WRITE, fsPaths[i].c_str());
policies[i].mountpoint = fsPaths[i].c_str();
policies[i].policy = QSSB_MOUNT_ALLOW_READ | QSSB_MOUNT_ALLOW_WRITE;
} }
policies[fsPaths.size() - 1].next = NULL;
struct qssb_policy policy = {0}; policy->namespace_options = QSSB_UNSHARE_MOUNT;
policy.path_policies = policies; policy->drop_caps = 0;
policy.namespace_options |= QSSB_UNSHARE_MOUNT; policy->mount_path_policies_to_chroot = 1;
policy.namespace_options |= QSSB_UNSHARE_USER;
int blacklisted_syscalls[] = {QSSB_SYS(execveat), QSSB_SYS(execve), -1}; qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execveat));
policy.blacklisted_syscalls = blacklisted_syscalls; qssb_append_syscall_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, QSSB_SYS(execve));
int result = qssb_enable_policy(&policy); qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
int result = qssb_enable_policy(policy);
if(result != 0) if(result != 0)
{ {
Logger::error() << "Failed to install sandboxing policy (preworker): %i" << result; Logger::error() << "Failed to install sandboxing policy (preworker): %i" << result;
qssb_free_policy(policy);
return false; return false;
} }
delete[] policies; qssb_free_policy(policy);
return true; return true;
} }
@ -88,30 +104,35 @@ bool SandboxLinux::supported()
} }
bool SandboxLinux::enableForWorker() bool SandboxLinux::enableForWorker()
{ {
struct qssb_policy policy = {0}; struct qssb_policy *policy = qssb_init_policy();
policy.drop_caps = 1; if(policy == NULL)
policy.not_dumpable = 1; {
policy.no_new_privs = 1; Logger::error() << "Failed to init sandboxing policy (worker) ";
return false;
}
policy->drop_caps = 1;
policy->not_dumpable = 1;
policy->no_new_privs = 1;
policy->namespace_options = 0;
/* TODO: as said, a whitelist approach is better. As such, this list is bound to be incomplete in the /* TODO: as said, a whitelist approach is better. As such, this list is bound to be incomplete in the
* sense that more could be listed here and some critical ones are probably missing */ * sense that more could be listed here and some critical ones are probably missing */
int blacklisted_syscalls[] = {QSSB_SYS(setuid),
QSSB_SYS(connect), /* TODO: use qssb groups */
QSSB_SYS(chroot), long blacklisted_syscalls[] = {QSSB_SYS(setuid), QSSB_SYS(connect), QSSB_SYS(chroot), QSSB_SYS(pivot_root),
QSSB_SYS(pivot_root), QSSB_SYS(mount), QSSB_SYS(setns), QSSB_SYS(unshare), QSSB_SYS(ptrace),
QSSB_SYS(mount), QSSB_SYS(personality), QSSB_SYS(prctl)};
QSSB_SYS(setns),
QSSB_SYS(unshare), qssb_append_syscalls_policy(policy, QSSB_SYSCALL_DENY_KILL_PROCESS, blacklisted_syscalls,
QSSB_SYS(ptrace), sizeof(blacklisted_syscalls) / sizeof(blacklisted_syscalls[0]));
QSSB_SYS(personality), qssb_append_syscall_default_policy(policy, QSSB_SYSCALL_ALLOW);
QSSB_SYS(prctl),
-1}; if(qssb_enable_policy(policy) != 0)
policy.blacklisted_syscalls = blacklisted_syscalls;
if(qssb_enable_policy(&policy) != 0)
{ {
Logger::error() << "Sandbox: Activation of seccomp blacklist failed!"; Logger::error() << "Sandbox: Activation of seccomp blacklist failed!";
qssb_free_policy(policy);
return false; return false;
} }
qssb_free_policy(policy);
return true; return true;
} }

View File

@ -25,24 +25,13 @@ count integer
CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255)); CREATE TABLE category(id INTEGER PRIMARY KEY, name varchar(255));
CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id)); CREATE TABLE categorymember(id INTEGER PRIMARY KEY, category REFERENCES category(id), page REFERENCES page (id));
CREATE INDEX revisionid ON revision (revisionid DESC); CREATE INDEX revisionid ON revision (revisionid DESC);
CREATE INDEX pagename ON page (name) CREATE INDEX pagename ON page (name);
; CREATE INDEX token ON session (token);
CREATE INDEX token ON session (token) CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id);
;
CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN
DELETE FROM search WHERE page = new.page;
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
END;
CREATE TRIGGER search_au AFTER UPDATE ON revision BEGIN
DELETE FROM search WHERE page = old.page;
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
END;
CREATE VIRTUAL TABLE search USING fts5(content, page UNINDEXED, content=revision,content_rowid=id)
/* search(content,page) */;
CREATE TABLE IF NOT EXISTS 'search_data'(id INTEGER PRIMARY KEY, block BLOB);
CREATE TABLE IF NOT EXISTS 'search_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;
CREATE TABLE IF NOT EXISTS 'search_docsize'(id INTEGER PRIMARY KEY, sz BLOB);
CREATE TABLE IF NOT EXISTS 'search_config'(k PRIMARY KEY, v) WITHOUT ROWID;
CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN CREATE TRIGGER search_ad AFTER DELETE ON revision BEGIN
INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page); INSERT INTO search(search, rowid, content, page) VALUES('delete', old.id, old.content, old.page);
END; END;
CREATE TRIGGER search_ai AFTER INSERT ON revision BEGIN
INSERT INTO search(search, rowid, content, page) SELECT 'delete', id, content, page FROM revision WHERE page = new.page AND revisionid = new.revisionid - 1;
INSERT INTO search(rowid, content, page) VALUES (new.id, new.content, new.page);
END;

View File

@ -46,6 +46,12 @@ std::string utils::html_xss(std::string_view str)
case '%': case '%':
result += "&#37;"; result += "&#37;";
break; break;
case '\'':
result += "&#x27;";
break;
case '&':
result += "&amp;";
break;
default: default:
result += c; result += c;
} }
@ -93,7 +99,7 @@ std::vector<std::string> utils::split(const std::string &str, char delim)
// TODO: can easily break if we pass a regex here // TODO: can easily break if we pass a regex here
std::vector<std::string> utils::split(const std::string &str, const std::string &delim) std::vector<std::string> utils::split(const std::string &str, const std::string &delim)
{ {
std::regex regex { delim + "+" }; std::regex regex{delim + "+"};
return split(str, regex); return split(str, regex);
} }