Skip to content
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
82 changes: 70 additions & 12 deletions src/abi/ace_exports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6718,13 +6718,27 @@ UNSIGNED32 materialise_temp_adt_open(Connection* c,
fs::path stem = adt;
stem.replace_extension();
{
std::fstream f(adt, std::ios::in | std::ios::out | std::ios::binary);
if (!f) return fail(openads::AE_INTERNAL_ERROR,
const bool jailed = openads::platform::File::data_jail_active();
openads::platform::File safe_file;
std::fstream f;
if (jailed) {
auto opened = openads::platform::File::open(adt.string(), openads::platform::OpenMode::ReadWrite);
if (!opened) return fail(opened.error());
safe_file = std::move(opened).value();
} else {
f.open(adt, std::ios::in | std::ios::out | std::ios::binary);
}
if (!jailed && !f) return fail(openads::AE_INTERNAL_ERROR,
"temp materialise: reopen of created ADT failed");
auto rd32 = [&](std::streamoff off) -> std::uint32_t {
std::uint8_t b[4] = {0, 0, 0, 0};
f.seekg(off);
f.read(reinterpret_cast<char*>(b), 4);
if (jailed) {
auto rd = safe_file.read_at(static_cast<std::uint64_t>(off), b, 4);
if (!rd || rd.value() != 4) return 0;
} else {
f.seekg(off);
f.read(reinterpret_cast<char*>(b), 4);
}
return static_cast<std::uint32_t>(b[0]) |
(static_cast<std::uint32_t>(b[1]) << 8) |
(static_cast<std::uint32_t>(b[2]) << 16) |
Expand Down Expand Up @@ -6786,18 +6800,30 @@ UNSIGNED32 materialise_temp_adt_open(Connection* c,
}
obuf.insert(obuf.end(), rec.begin(), rec.end());
}
f.seekp(static_cast<std::streamoff>(hdr_len));
f.write(reinterpret_cast<const char*>(obuf.data()),
static_cast<std::streamsize>(obuf.size()));
if (jailed) {
auto wr = safe_file.write_at(hdr_len, obuf.data(), obuf.size());
if (!wr) return fail(wr.error());
if (wr.value() != obuf.size()) return fail(openads::AE_INTERNAL_ERROR, "temp materialise: short write");
} else {
f.seekp(static_cast<std::streamoff>(hdr_len));
f.write(reinterpret_cast<const char*>(obuf.data()),
static_cast<std::streamsize>(obuf.size()));
}
// Patch the record count (header bytes 24-27, LE u32).
std::uint8_t cnt[4] = {
static_cast<std::uint8_t>( nrows & 0xFFu),
static_cast<std::uint8_t>((nrows >> 8) & 0xFFu),
static_cast<std::uint8_t>((nrows >> 16) & 0xFFu),
static_cast<std::uint8_t>((nrows >> 24) & 0xFFu)};
f.seekp(24);
f.write(reinterpret_cast<const char*>(cnt), 4);
if (!f) return fail(openads::AE_INTERNAL_ERROR,
if (jailed) {
auto wr = safe_file.write_at(24, cnt, 4);
if (!wr) return fail(wr.error());
if (wr.value() != 4) return fail(openads::AE_INTERNAL_ERROR, "temp materialise: short header write");
} else {
f.seekp(24);
f.write(reinterpret_cast<const char*>(cnt), 4);
}
if (!jailed && !f) return fail(openads::AE_INTERNAL_ERROR,
"temp materialise: short write");
}

Expand Down Expand Up @@ -10635,13 +10661,21 @@ UNSIGNED32 ENTRYPOINT AdsRestructureTable(ADSHANDLE hConnect,
}
file_bytes.push_back(0x1A);

if (openads::platform::File::data_jail_active()) {
auto jail_file = openads::platform::File::open(tmp.string(), openads::platform::OpenMode::CreateExclusive);
if (!jail_file) return fail(jail_file.error());
auto written = jail_file.value().write_at(0, file_bytes.data(), file_bytes.size());
if (!written) return fail(written.error());
if (written.value() != file_bytes.size()) return fail(openads::AE_INTERNAL_ERROR, "AdsRestructureTable: short write");
} else {
std::ofstream out(tmp, std::ios::binary);
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsRestructureTable: tmp open failed");
out.write(reinterpret_cast<const char*>(file_bytes.data()),
static_cast<std::streamsize>(file_bytes.size()));
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsRestructureTable: tmp write failed");
}
} // engine handle on `full` closes here

{
Expand Down Expand Up @@ -21902,16 +21936,24 @@ UNSIGNED32 ENTRYPOINT AdsCopyTable(ADSHANDLE hHandle,

{
std::error_code ec;
fs::remove(dst, ec);
if (!openads::platform::File::data_jail_active()) fs::remove(dst, ec);
}
{
if (openads::platform::File::data_jail_active()) {
auto opened = openads::platform::File::open(dst.string(), openads::platform::OpenMode::CreateExclusive);
if (!opened) return fail(opened.error());
auto written = opened.value().write_at(0, file.data(), file.size());
if (!written) return fail(written.error());
if (written.value() != file.size()) return fail(openads::AE_INTERNAL_ERROR, "AdsCopyTable: short write");
} else {
std::ofstream out(dst, std::ios::binary);
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCopyTable: open for write failed");
out.write(reinterpret_cast<const char*>(file.data()),
static_cast<std::streamsize>(file.size()));
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCopyTable: write failed");
}
}
return ok();
}
Expand Down Expand Up @@ -41118,13 +41160,21 @@ UNSIGNED32 ENTRYPOINT AdsCloneTable(ADSHANDLE hTable, ADSHANDLE* phClone) {

fs::path tmp_path = fs::path(owning->data_dir()) / tmp_name;
{
if (openads::platform::File::data_jail_active()) {
auto opened = openads::platform::File::open(tmp_path.string(), openads::platform::OpenMode::CreateExclusive);
if (!opened) return fail(opened.error());
auto written = opened.value().write_at(0, file.data(), file.size());
if (!written) return fail(written.error());
if (written.value() != file.size()) return fail(openads::AE_INTERNAL_ERROR, "AdsCloneTable: short write");
} else {
std::ofstream out(tmp_path, std::ios::binary);
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCloneTable: write failed");
out.write(reinterpret_cast<const char*>(file.data()),
static_cast<std::streamsize>(file.size()));
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCloneTable: write error");
}
}

// Open the clone through the owning connection.
Expand Down Expand Up @@ -41197,16 +41247,24 @@ UNSIGNED32 ENTRYPOINT AdsCopyTableStructure(ADSHANDLE hTable, UNSIGNED8* pucFile

{
std::error_code ec;
fs::remove(dst, ec);
if (!openads::platform::File::data_jail_active()) fs::remove(dst, ec);
}
{
if (openads::platform::File::data_jail_active()) {
auto opened = openads::platform::File::open(dst.string(), openads::platform::OpenMode::CreateExclusive);
if (!opened) return fail(opened.error());
auto written = opened.value().write_at(0, file.data(), file.size());
if (!written) return fail(written.error());
if (written.value() != file.size()) return fail(openads::AE_INTERNAL_ERROR, "AdsCopyTableStructure: short write");
} else {
std::ofstream out(dst, std::ios::binary);
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCopyTableStructure: open failed");
out.write(reinterpret_cast<const char*>(file.data()),
static_cast<std::streamsize>(file.size()));
if (!out) return fail(openads::AE_INTERNAL_ERROR,
"AdsCopyTableStructure: write failed");
}
}
return ok();
}
Expand Down
17 changes: 17 additions & 0 deletions src/platform/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace openads::platform {

Expand Down Expand Up @@ -33,6 +34,22 @@ class File {

static util::Result<File> open(const std::string& path, OpenMode mode);

// --- server data jail (ADSCDX) ------------------------------------
// Only openads_serverd activates this policy. The DLL/local path
// remains the legacy plain open while no jail is installed.
// POSIX: root descriptors plus O_NOFOLLOW on every component;
// parent steps use a verified descriptor stack, never openat("..").
// Windows: directory handles opened with OPEN_REPARSE_POINT and
// without share-delete pin the verified path; leaf handles are
// no-follow and verified before truncation. Roots must exist.
// Symlinks/reparse points below configured roots are refused.
static util::Result<void> set_data_jail(std::vector<std::string> roots);
// Remove the jail (tests). Later opens revert to the legacy plain
// path.
static void clear_data_jail() noexcept;
// True while a jail is installed.
static bool data_jail_active() noexcept;

util::Result<std::size_t> read_at (std::uint64_t offset,
void* buf, std::size_t n);
util::Result<std::size_t> write_at(std::uint64_t offset,
Expand Down
Loading
Loading