// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sql/sandboxed_vfs.h" #include #include #include #include #include #include "base/check_op.h" #include "base/files/file.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/threading/platform_thread.h" #include "build/build_config.h" #include "sql/initialization.h" #include "sql/sandboxed_vfs_file.h" #include "third_party/sqlite/sqlite3.h" namespace sql { namespace { // Extracts the SandboxedVfs* stashed in a SQLite VFS structure. SandboxedVfs& SandboxedVfsFromSqliteVfs(sqlite3_vfs& vfs) { return *reinterpret_cast(vfs.pAppData); } int SandboxedVfsOpen(sqlite3_vfs* vfs, const char* full_path, sqlite3_file* result_file, int requested_flags, int* granted_flags) { return SandboxedVfsFromSqliteVfs(*vfs).Open(full_path, *result_file, requested_flags, granted_flags); } int SandboxedVfsDelete(sqlite3_vfs* vfs, const char* full_path, int sync_dir) { return SandboxedVfsFromSqliteVfs(*vfs).Delete(full_path, sync_dir); } int SandboxedVfsAccess(sqlite3_vfs* vfs, const char* full_path, int flags, int* result) { return SandboxedVfsFromSqliteVfs(*vfs).Access(full_path, flags, *result); } int SandboxedVfsFullPathname(sqlite3_vfs* vfs, const char* file_path, int result_size, char* result) { return SandboxedVfsFromSqliteVfs(*vfs).FullPathname(file_path, result_size, result); } int SandboxedVfsRandomness(sqlite3_vfs* vfs, int result_size, char* result) { return SandboxedVfsFromSqliteVfs(*vfs).Randomness(result_size, result); } int SandboxedVfsSleep(sqlite3_vfs* vfs, int microseconds) { return SandboxedVfsFromSqliteVfs(*vfs).Sleep(microseconds); } int SandboxedVfsGetLastError(sqlite3_vfs* vfs, int message_size, char* message) { return SandboxedVfsFromSqliteVfs(*vfs).GetLastError(message_size, message); } int SandboxedVfsCurrentTimeInt64(sqlite3_vfs* vfs, sqlite3_int64* result_ms) { return SandboxedVfsFromSqliteVfs(*vfs).CurrentTimeInt64(result_ms); } sqlite3_vfs SqliteVfsFor(SandboxedVfs* sandboxed_vfs, const char* name) { DCHECK_EQ(sandboxed_vfs, reinterpret_cast( reinterpret_cast(sandboxed_vfs))) << "This implementation round-trips SandboxedVfs* via void*"; // VFS API entry points are listed at https://www.sqlite.org/c3ref/vfs.html static constexpr int kSqliteVfsApiVersion = 3; // Maximum file path size. // TODO(pwnall): Obtain this from //base or some other good place. static constexpr int kSqliteMaxPathSize = 512; return { kSqliteVfsApiVersion, sizeof(SandboxedVfsFileSqliteBridge), kSqliteMaxPathSize, /*pNext=*/nullptr, name, /*pAppData=*/reinterpret_cast(sandboxed_vfs), SandboxedVfsOpen, SandboxedVfsDelete, SandboxedVfsAccess, SandboxedVfsFullPathname, /*xDlOpen=*/nullptr, /*xDlError=*/nullptr, /*xDlSym=*/nullptr, /*xDlClose=*/nullptr, SandboxedVfsRandomness, SandboxedVfsSleep, /*xCurrentTime=*/nullptr, // Deprecated in API versions 2 and above. SandboxedVfsGetLastError, SandboxedVfsCurrentTimeInt64, /*xSetSystemCall=*/nullptr, /*xGetSystemCall=*/nullptr, /*xNextSystemCall=*/nullptr, }; } // SQLite measures time according to the Julian calendar. base::Time SqliteEpoch() { constexpr const double kMicroSecondsPerDay = 24 * 60 * 60 * 1000; // The ".5" is intentional -- days in the Julian calendar start at noon. // The offset is in the SQLite source code (os_unix.c) multiplied by 10. constexpr const double kUnixEpochAsJulianDay = 2440587.5; return base::Time::FromJsTime(-kUnixEpochAsJulianDay * kMicroSecondsPerDay); } #if DCHECK_IS_ON() // `full_path_cstr` must be a filename argument passed to the VFS from SQLite. SandboxedVfsFileType VfsFileTypeFromPath(const char* full_path_cstr) { base::StringPiece full_path(full_path_cstr); const char* database_file_cstr = sqlite3_filename_database(full_path_cstr); base::StringPiece database_file(database_file_cstr); if (full_path == database_file) return SandboxedVfsFileType::kDatabase; const char* journal_file_cstr = sqlite3_filename_journal(full_path_cstr); base::StringPiece journal_file(journal_file_cstr); if (full_path == journal_file) return SandboxedVfsFileType::kJournal; const char* wal_file_cstr = sqlite3_filename_wal(full_path_cstr); base::StringPiece wal_file(wal_file_cstr); if (full_path == wal_file) return SandboxedVfsFileType::kWal; NOTREACHED() << "Argument is not a file name buffer passed from SQLite to a VFS: " << full_path; return SandboxedVfsFileType::kDatabase; } #endif // DCHECK_IS_ON() } // namespace // static void SandboxedVfs::Register(const char* name, std::unique_ptr delegate, bool make_default) { static base::NoDestructor> registered_vfs_instances; sql::EnsureSqliteInitialized(); registered_vfs_instances->push_back( new SandboxedVfs(name, std::move(delegate), make_default)); } int SandboxedVfs::Open(const char* full_path, sqlite3_file& result_file, int requested_flags, int* granted_flags) { base::FilePath file_path = base::FilePath::FromUTF8Unsafe(full_path); base::File file = delegate_->OpenFile(file_path, requested_flags); if (!file.IsValid()) { // TODO(pwnall): Figure out if we can remove the fallback to read-only. if (!(requested_flags & SQLITE_OPEN_READWRITE)) { // The SQLite API requires that pMethods is set to null even if the open // call returns a failure status. result_file.pMethods = nullptr; return SQLITE_CANTOPEN; } int new_flags = (requested_flags & ~SQLITE_OPEN_READWRITE) | SQLITE_OPEN_READONLY; return Open(full_path, result_file, new_flags, granted_flags); } SandboxedVfsFile::Create(std::move(file), std::move(file_path), #if DCHECK_IS_ON() VfsFileTypeFromPath(full_path), #endif // DCHECK_IS_ON() this, result_file); if (granted_flags) *granted_flags = requested_flags; return SQLITE_OK; } int SandboxedVfs::Delete(const char* full_path, int sync_dir) { DCHECK(full_path); return delegate_->DeleteFile(base::FilePath::FromUTF8Unsafe(full_path), sync_dir == 1); } int SandboxedVfs::Access(const char* full_path, int flags, int& result) { DCHECK(full_path); absl::optional access = delegate_->GetPathAccess(base::FilePath::FromUTF8Unsafe(full_path)); if (!access) { result = 0; return SQLITE_OK; } switch (flags) { case SQLITE_ACCESS_EXISTS: result = 1; break; case SQLITE_ACCESS_READ: result = access->can_read ? 1 : 0; break; case SQLITE_ACCESS_READWRITE: result = (access->can_read && access->can_write) ? 1 : 0; break; default: NOTREACHED() << "Unsupported xAccess flags: " << flags; return SQLITE_ERROR; } return SQLITE_OK; } int SandboxedVfs::FullPathname(const char* file_path, int result_size, char* result) { DCHECK(file_path); DCHECK_GT(result_size, 0); DCHECK(result); // Renderer processes cannot access files directly, so it doesn't make sense // to expose full paths here. size_t file_path_size = std::strlen(file_path) + 1; if (static_cast(result_size) < file_path_size) return SQLITE_CANTOPEN; std::memcpy(result, file_path, file_path_size); return SQLITE_OK; } int SandboxedVfs::Randomness(int result_size, char* result) { DCHECK_GT(result_size, 0); DCHECK(result); // TODO(pwnall): Figure out if we need a real implementation. std::memset(result, 0, result_size); return result_size; } int SandboxedVfs::Sleep(int microseconds) { DCHECK_GE(microseconds, 0); base::PlatformThread::Sleep(base::Microseconds(microseconds)); return SQLITE_OK; } int SandboxedVfs::GetLastError(int message_size, char* message) const { DCHECK_GE(message_size, 0); DCHECK(message_size == 0 || message); std::string error_string = base::File::ErrorToString(last_error_); size_t error_string_size = error_string.length() + 1; size_t copy_length = std::min(static_cast(message_size), error_string_size); std::memcpy(message, error_string.c_str(), copy_length); // The return value is zero if the message fits in the buffer, and non-zero if // it does not fit. return copy_length != error_string_size; } int SandboxedVfs::CurrentTimeInt64(sqlite3_int64* result_ms) { DCHECK(result_ms); base::TimeDelta delta = base::Time::Now() - sqlite_epoch_; *result_ms = delta.InMilliseconds(); return SQLITE_OK; } SandboxedVfs::SandboxedVfs(const char* name, std::unique_ptr delegate, bool make_default) : sandboxed_vfs_(SqliteVfsFor(this, name)), sqlite_epoch_(SqliteEpoch()), delegate_(std::move(delegate)), last_error_(base::File::FILE_OK) { // The register function returns a SQLite status as an int. The status is // ignored here. If registration fails, we'd want to report the error while // attempting to open a database. This is exactly what will happen, because // SQLite won't find the VFS we're asking for. sqlite3_vfs_register(&sandboxed_vfs_, make_default ? 1 : 0); } } // namespace sql