Files

594 lines
15 KiB
C++
Raw Permalink Normal View History

2021-05-19 12:08:57 +02:00
#include "Message.h"
#include <string.h>
#include <stdexcept>
#include <cassert>
2021-08-14 17:13:41 +02:00
#include <QDebug>
2021-05-19 12:08:57 +02:00
enum FieldType {
2024-02-21 11:21:37 +01:00
JailId = 1,
2021-05-19 12:08:57 +02:00
ExecutablePath = 10,
2021-08-14 22:00:42 +02:00
Argument,
2024-02-24 11:40:42 +01:00
InitScript,
2024-05-17 11:39:44 +02:00
JailPassword, // The jaildir is encfs encrypted. Decrypt password.
2021-08-14 22:00:42 +02:00
2024-02-20 19:14:25 +01:00
IsTry = 20, // allow next command to fail
RBindMountSource, // mount from an existing directory.
2021-08-14 22:00:42 +02:00
RBindMountDest,
2024-02-20 19:14:25 +01:00
UnMountDir, // unmount (remove) an existing mountpoint.
CreateTmpFs, // with the full path as arg
2024-02-16 16:54:09 +01:00
CopyFrom = 30,
CopyTo,
2024-02-17 01:03:43 +01:00
2024-02-20 19:14:25 +01:00
EnvironUnset = 35, // an env-var we want to filter
EnvironSet, // override env-var value.
DBusProxyFrom = 40, // start the xdg-dbus-proxy between from and to
DBusProxyTo,
DBusProxySystemFrom, // start the dbus proxy with system rules
DBusProxySystemTo,
2026-04-11 14:54:32 +02:00
VpnConfig = 50, // base path to vpn files (inside jail)
VpnHasPassFile = 51, // has a password file in the jail
2021-05-19 12:08:57 +02:00
};
2024-02-16 16:54:09 +01:00
// returns the length of the string, or throw if no closing zero found
static int stringLength(const char *from, const char *end)
{
auto at = from;
while (at <= end) {
if (*at == 0)
return at - from;
++at;
}
throw std::runtime_error("unterminated string found");
}
2021-05-19 12:08:57 +02:00
Message::Message(int size)
: m_reservedSize(size)
{
assert(size > 0);
m_buf = std::shared_ptr<char>(new char[size], std::default_delete<char[]>());
m_writePtr = m_buf.get();
2021-08-14 22:53:51 +02:00
memset(m_writePtr, 0, m_reservedSize);
2021-05-19 12:08:57 +02:00
}
Message::Message(char *buffer, int bufferSize)
: m_reservedSize(bufferSize)
{
m_buf = std::shared_ptr<char>(new char[bufferSize], std::default_delete<char[]>());
memcpy(m_buf.get(), buffer, bufferSize);
2021-05-21 15:28:57 +02:00
buffer = m_buf.get();
2021-05-19 12:08:57 +02:00
const char *end = buffer + bufferSize;
while (buffer < end) {
if (*buffer == ExecutablePath) {
m_path = buffer + 1;
}
2021-05-21 15:28:57 +02:00
else if (*buffer == Argument) {
2021-08-14 22:00:42 +02:00
break;
2021-05-19 12:08:57 +02:00
}
2024-02-17 01:03:43 +01:00
const int len = ::stringLength(buffer + 1, end);
2021-05-19 12:08:57 +02:00
if (buffer + len + 2 > end) {
throw std::runtime_error("buffer too small");
}
buffer += len + 2;
}
}
char *Message::path() const
{
return m_path;
}
void Message::setPath(const std::string &path)
{
m_path = m_writePtr + 1;
addString(ExecutablePath, path);
}
2021-05-21 15:28:57 +02:00
void Message::addArgument(const char *string)
{
addString(Argument, string);
}
2021-08-14 17:13:41 +02:00
void Message::printFields() const
{
#ifndef NDEBUG
2021-08-14 22:00:42 +02:00
qDebug().nospace() << "Message[" << path() << "] " << size() << " bytes";
const char *buffer = m_buf.get();
const char *end = buffer + m_reservedSize;
while (buffer < end) {
std::string prefix;
const char type = *buffer;
2024-02-16 16:54:09 +01:00
if (type == Argument) {
2021-08-14 22:00:42 +02:00
prefix = "argument:";
}
else if (type == RBindMountSource) {
prefix = "rbind-src: ";
}
else if (type == RBindMountDest) {
prefix = "rbind-dst: ";
}
else if (type == UnMountDir) {
prefix = "umountDir: ";
}
2024-02-15 23:39:04 +01:00
else if (type == CreateTmpFs) {
prefix = "tmpFs-create: ";
}
2024-02-16 16:54:09 +01:00
else if (type == CopyFrom) {
prefix = "copyFrom: ";
}
else if (type == CopyTo) {
prefix = "copyTo: ";
}
2024-02-17 01:03:43 +01:00
else if (type == EnvironSet) {
prefix = "New Env: ";
}
else if (type == EnvironUnset) {
prefix = "Filter Env: ";
}
2024-02-20 19:14:25 +01:00
else if (type == DBusProxyFrom) {
prefix = "dbus-from: ";
}
else if (type == DBusProxySystemFrom) {
prefix = "dbus-system-from: ";
}
else if (type == DBusProxyTo) {
prefix = "dbus-to: ";
}
else if (type == DBusProxySystemTo) {
prefix = "dbus-system-to: ";
}
2026-04-11 14:54:32 +02:00
else if (type == VpnConfig) {
prefix = "vpn-config: ";
}
2024-02-21 11:21:37 +01:00
else if (type == JailId) {
2026-04-11 14:47:24 +02:00
// This only works if the source and destination have the same endian-ness
// as the basic design places them in the same exe just forked into two
// processes, this is not a concern.
2024-02-21 11:21:37 +01:00
uint32_t data = buffer[4];
data <<= 8;
data += buffer[3];
data <<= 8;
data += buffer[2];
data <<= 8;
data += buffer[1];
buffer += 5;
qDebug() << "=> JailId:" << data;
continue;
}
2026-04-11 14:54:32 +02:00
else if (type == VpnHasPassFile) { // bool
qDebug() << "=> hasPassFile:" << (buffer[1] != 0);
buffer += 2;
continue;
}
2021-08-14 22:00:42 +02:00
else if (type == 0) {
qDebug() << Qt::endl;
return;
}
2024-02-17 01:03:43 +01:00
const int len = ::stringLength(buffer + 1, end);
2021-08-14 22:00:42 +02:00
if (buffer + len + 2 > end) {
qWarning() << "buffer overrun detected, str len:" << len;
return;
}
2024-02-15 23:39:04 +01:00
if (type == IsTry) {
qWarning() << " try";
2024-02-16 09:00:15 +01:00
buffer += 1;
continue;
2024-02-15 23:39:04 +01:00
}
else if (type != ExecutablePath) {
2021-08-14 22:00:42 +02:00
qWarning() << " " << prefix.c_str() << QString::fromLocal8Bit(buffer + 1, len);
}
buffer += len + 2;
2021-08-14 17:13:41 +02:00
}
#endif
}
2021-05-19 12:08:57 +02:00
char *Message::begin() const
{
return m_buf.get();
}
int Message::size() const
{
if (m_writePtr)
return m_writePtr - m_buf.get();
return m_reservedSize;
}
2021-08-14 22:00:42 +02:00
void Message::addUmountPoint(const std::string &dir)
{
addString(UnMountDir, dir);
}
2021-08-14 23:26:47 +02:00
void Message::addRemount(const std::string &source, const std::string &destination)
2021-08-14 22:00:42 +02:00
{
addString(RBindMountSource, source); // always first
addString(RBindMountDest, destination); // make sure this is last as it 'executes' the whole thing.
}
2024-02-15 23:39:04 +01:00
void Message::setTry(bool isTry)
{
if (isTry)
addTag(IsTry); // always first
}
2024-02-21 11:21:37 +01:00
void Message::setJailId(uint32_t jailId)
{
assert(m_writePtr);
if (m_buf.get() + m_reservedSize < m_writePtr + 5) {
throw std::runtime_error("jailId does not fit buffer");
}
2026-04-11 14:47:24 +02:00
// This only works if the source and destination have the same endian-ness
// as the basic design places them in the same exe just forked into two
// processes, this is not a concern.
2024-02-21 11:21:37 +01:00
m_writePtr[0] = JailId;
2026-04-11 14:47:24 +02:00
m_writePtr[1] = jailId & 0xFF;
m_writePtr[2] = (jailId >> 8) & 0xFF;
m_writePtr[3] = (jailId >> 16) & 0xFF;
m_writePtr[4] = (jailId >> 24) & 0xFF;
2024-02-21 11:21:37 +01:00
m_writePtr += 5;
}
2024-02-15 23:39:04 +01:00
void Message::addMountTmpDir(const std::string &dir)
{
addString(CreateTmpFs, dir);
}
2024-02-16 16:54:09 +01:00
void Message::addCopy(const std::string &from, const std::string &to)
{
addString(CopyFrom, from);
addString(CopyTo, to);
}
2024-02-24 11:40:42 +01:00
void Message::addInitSript(const std::string &text)
{
addString(InitScript, text);
}
2024-02-17 01:03:43 +01:00
void Message::addEnvToUnset(const std::string &propertyName)
{
assert(propertyName.find('=') == std::string::npos);
addString(EnvironUnset, propertyName);
}
void Message::addEnvToSet(const std::string &envVar)
{
assert(envVar.find('=') != std::string::npos);
addString(EnvironSet, envVar);
}
2024-05-17 11:39:44 +02:00
void Message::setJailPassword(const std::string &pwd)
{
addString(JailPassword, pwd);
}
2024-02-20 19:14:25 +01:00
void Message::addDBusProxy(DBusType type, const std::string &from, const std::string &to)
{
addString(type == UserSessionBus ? DBusProxyFrom : DBusProxySystemFrom, from);
addString(type == UserSessionBus ? DBusProxyTo : DBusProxySystemTo, to);
}
2026-04-11 14:54:32 +02:00
void Message::setVpnConfig(const std::string &configPath)
{
addString(VpnConfig, configPath);
}
void Message::setHasVpnPassFile(bool yes)
{
assert(m_writePtr);
if (m_buf.get() + m_reservedSize < m_writePtr + 2)
throw std::runtime_error("Bool does not fit buffer");
m_writePtr[0] = VpnHasPassFile;
m_writePtr[1] = yes ? 1 : 0;
m_writePtr += 2;
}
2024-02-15 23:39:04 +01:00
void Message::addTag(char type)
{
assert(m_writePtr);
if (m_buf.get() + m_reservedSize < m_writePtr + 1) {
throw std::runtime_error("Tag does not fit buffer");
}
2024-02-16 09:00:15 +01:00
*m_writePtr = type;
2024-02-15 23:39:04 +01:00
++m_writePtr;
}
2021-05-19 12:08:57 +02:00
void Message::addString(char type, const std::string &string)
{
assert(m_writePtr);
if (m_buf.get() + m_reservedSize < m_writePtr + string.size() + 2) {
// won't fit.
throw std::runtime_error("String does not fit buffer");
}
m_writePtr[0] = type;
2021-05-21 15:28:57 +02:00
memcpy(++m_writePtr, string.c_str(), string.size());
m_writePtr += string.size();
m_writePtr[0] = 0; // trailing zero
++m_writePtr;
}
// ///////////////////////////////////////////////////////////////
2021-08-14 22:00:42 +02:00
Message::Iterator::Iterator(const Message * const message)
: m_parent(message),
m_cur(message->begin()),
m_recordSize(-1)
2021-05-21 15:28:57 +02:00
{
2024-02-21 11:21:37 +01:00
next();
2021-05-21 15:28:57 +02:00
}
2021-08-14 22:00:42 +02:00
bool Message::Iterator::isArgument() const
2021-05-21 15:28:57 +02:00
{
2021-08-14 22:00:42 +02:00
assert(isValid());
return m_cur[0] == Argument;
}
2024-02-17 01:03:43 +01:00
bool Message::Iterator::isNewEnvVar() const
{
assert(isValid());
return m_cur[0] == EnvironSet;
}
bool Message::Iterator::isEnvVarUnset() const
{
assert(isValid());
return m_cur[0] == EnvironUnset;
}
2021-08-14 22:00:42 +02:00
bool Message::Iterator::isUnmount() const
{
assert(isValid());
return m_cur[0] == UnMountDir;
}
bool Message::Iterator::isRemount() const
{
assert(isValid());
return m_cur[0] == RBindMountSource;
}
2024-02-15 23:39:04 +01:00
bool Message::Iterator::isCreateTmp() const
{
assert(isValid());
return m_cur[0] == CreateTmpFs;
}
2024-02-16 16:54:09 +01:00
bool Message::Iterator::isCopy() const
{
assert(isValid());
return m_cur[0] == CopyFrom;
}
2024-02-21 11:21:37 +01:00
bool Message::Iterator::isJailId() const
{
assert(isValid());
return m_cur[0] == JailId;
}
2024-02-24 11:40:42 +01:00
bool Message::Iterator::isInitSript() const
{
assert(isValid());
return m_cur[0] == InitScript;
}
2024-05-17 11:39:44 +02:00
bool Message::Iterator::isJailPwd() const
{
assert(isValid());
return m_cur[0] == JailPassword;
}
2026-04-11 14:54:32 +02:00
bool Message::Iterator::isVpnConfig() const
{
assert(isValid());
return m_cur[0] == VpnConfig;
}
bool Message::Iterator::isVpnPwdBool() const
{
assert(isValid());
return m_cur[0] == VpnHasPassFile;
}
2021-08-14 22:00:42 +02:00
bool Message::Iterator::isValid() const
{
assert(m_parent);
assert(m_cur);
assert(m_cur >= m_parent->begin());
if (m_cur < m_parent->begin() + m_parent->size()) {
return m_cur[0] != 0;
}
return false;
}
2024-02-20 19:14:25 +01:00
bool Message::Iterator::isDBusMapping() const
{
return m_cur[0] == DBusProxyFrom || m_cur[0] == DBusProxySystemFrom;
}
2026-04-11 14:54:32 +02:00
bool Message::Iterator::boolData() const
{
return m_cur[0] != 0;
}
2024-02-16 16:54:09 +01:00
CopyMessage Message::Iterator::copyData() const
{
assert(m_parent);
CopyMessage rc;
if (m_cur[0] != CopyFrom)
throw std::runtime_error("Not copy data");
const char *end = m_parent->m_buf.get() + m_parent->m_reservedSize;
2024-02-17 01:03:43 +01:00
auto length = ::stringLength(m_cur + 1, end);
2024-02-16 16:54:09 +01:00
rc.from = std::string(m_cur + 1, length);
2024-02-17 01:03:43 +01:00
auto length2 = ::stringLength(m_cur + 1 + length + 2, end);
2024-02-16 16:54:09 +01:00
rc.to = std::string(m_cur + 1 + length + 2, length2);
return rc;
}
2024-02-20 19:14:25 +01:00
DBusMapping Message::Iterator::dbusMapping() const
{
assert(m_parent);
DBusMapping rc;
if (m_cur[0] == DBusProxyFrom)
rc.systemBus = false;
else if (m_cur[0] == DBusProxySystemFrom)
rc.systemBus = true;
else
throw std::runtime_error("Not dbus-mapping data");
const char *end = m_parent->m_buf.get() + m_parent->m_reservedSize;
auto length = ::stringLength(m_cur + 1, end);
rc.from = std::string(m_cur + 1, length);
auto length2 = ::stringLength(m_cur + 1 + length + 2, end);
rc.to = std::string(m_cur + 1 + length + 2, length2);
return rc;
}
2024-02-21 11:21:37 +01:00
uint32_t Message::Iterator::jailId() const
{
2026-04-11 14:54:32 +02:00
assert(m_parent);
const char *end = m_parent->m_buf.get() + m_parent->m_reservedSize;
if (m_cur + 4 > end)
throw std::runtime_error("Not enough space in message");
assert(isJailId()); // check if it actually is a jailid
2024-02-21 11:21:37 +01:00
uint32_t data = m_cur[4];
data <<= 8;
data += m_cur[3];
data <<= 8;
data += m_cur[2];
data <<= 8;
data += m_cur[1];
return data;
}
2024-02-17 01:03:43 +01:00
char *Message::Iterator::stringPtr() const
{
return m_cur + 1;
}
int Message::Iterator::stringLength() const
{
assert(isValid());
// not valid for one of the commands that have more than one string.
assert(!isRemount() && !isCopy());
return m_recordSize - 2;
}
2024-02-16 16:54:09 +01:00
MountMessage Message::Iterator::mountData() const
2021-08-14 22:00:42 +02:00
{
assert(m_parent);
MountMessage rc;
if (m_cur[0] == RBindMountSource)
rc.type = MountMessage::Remount;
else if (m_cur[0] == UnMountDir)
rc.type = MountMessage::Umount;
2024-02-15 23:39:04 +01:00
else if (m_cur[0] == CreateTmpFs)
rc.type = MountMessage::CreateTmpFs;
2021-08-14 22:00:42 +02:00
else
throw std::runtime_error("Not mount data");
2024-02-16 16:54:09 +01:00
const char *end = m_parent->m_buf.get() + m_parent->m_reservedSize;
int index = 0;
2021-08-14 22:00:42 +02:00
while (true) {
2024-02-16 16:54:09 +01:00
char t = m_cur[index];
const char *str = m_cur + index + 1;
2024-02-17 01:03:43 +01:00
const int strSize = ::stringLength(m_cur + index + 1, end);
2024-02-16 16:54:09 +01:00
index += strSize + 2;
checkAvail(index);
2021-08-14 22:00:42 +02:00
if (t == RBindMountSource) {
rc.src = std::string(str, strSize);
}
else if (t == RBindMountDest) {
rc.dst = std::string(str, strSize);
break; // last in the sequence
}
else if (rc.type == MountMessage::Umount) {
rc.src = std::string(str, strSize);
// only the src field is expected
break;
2021-05-21 15:28:57 +02:00
}
2024-02-15 23:39:04 +01:00
else if (rc.type == MountMessage::CreateTmpFs) {
rc.dst = std::string(str, strSize);
// only the dst field is expected
break;
}
2021-05-21 15:28:57 +02:00
}
2021-08-14 22:00:42 +02:00
return rc;
}
2024-02-16 16:54:09 +01:00
const char *Message::Iterator::argument() const
2021-08-14 22:00:42 +02:00
{
assert(m_parent);
assert(isArgument());
2024-02-16 09:00:15 +01:00
assert(m_recordSize != -1); // we called 'next'
2021-08-14 22:00:42 +02:00
return m_cur + 1;
}
2024-02-16 09:00:15 +01:00
bool Message::Iterator::next()
2021-08-14 22:00:42 +02:00
{
assert(m_cur);
2024-02-16 09:00:15 +01:00
if (m_recordSize != -1) {
m_cur = m_cur + m_recordSize;
if (!isValid())
return false;
2021-08-14 22:00:42 +02:00
}
2024-02-16 09:00:15 +01:00
m_isTry = *m_cur == IsTry;
if (m_isTry)
m_cur += 1;
2024-02-16 16:54:09 +01:00
m_recordSize = 0;
checkAvail(2);
2024-02-16 09:00:15 +01:00
// find the string-size(s)
2024-02-16 16:54:09 +01:00
const char *end = m_parent->m_buf.get() + m_parent->m_reservedSize;
2024-02-16 09:00:15 +01:00
switch (*m_cur) {
case ExecutablePath: // fall through
case Argument: // fall through
case UnMountDir: // fall through
2024-02-17 01:03:43 +01:00
case EnvironSet: // fall through
case EnvironUnset: // fall through
2024-02-24 11:40:42 +01:00
case InitScript: // fall through
2024-05-17 11:39:44 +02:00
case JailPassword: // fall through
2026-04-11 14:54:32 +02:00
case VpnConfig: // fall through
2024-02-16 09:00:15 +01:00
case CreateTmpFs:
2024-02-17 01:03:43 +01:00
m_recordSize = ::stringLength(m_cur + 1, end) + 2;
2024-02-16 09:00:15 +01:00
break;
2024-02-20 19:14:25 +01:00
case DBusProxyFrom: // fall through
case DBusProxySystemFrom: // fall through
2024-02-16 16:54:09 +01:00
case RBindMountSource: // fall-through
case CopyFrom:
2024-02-20 19:14:25 +01:00
// these take 2 args
2024-02-17 01:03:43 +01:00
m_recordSize = ::stringLength(m_cur + 1, end) + 2;
m_recordSize += ::stringLength(m_cur + m_recordSize + 1, end) + 2;
2024-02-16 09:00:15 +01:00
break;
2024-02-21 11:21:37 +01:00
case JailId:
m_recordSize += 5;
break;
2026-04-11 14:54:32 +02:00
case VpnHasPassFile: // bool
m_recordSize += 2;
break;
2024-02-20 19:14:25 +01:00
case DBusProxyTo: // fall through
case DBusProxySystemTo: // fall through
2024-02-16 09:00:15 +01:00
case RBindMountDest:
assert(false);
default:
assert(false);
}
return true;
2021-08-14 22:00:42 +02:00
}
void Message::Iterator::checkAvail(int bytes) const
{
if (bytes < 0 || bytes > MAX_SIZE)
throw std::runtime_error("Serialization failure: impossible size");
if (m_parent->begin() + m_parent->size() < m_cur + bytes)
throw std::runtime_error("Message::Iterator: out of bounds");
2021-05-19 12:08:57 +02:00
}