mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 14:37:50 +00:00
VFS audit plugin
Plugin for auditing VFS access using the VFS server. Useful for tracking which files ported software expects to be present. Fix #2160
This commit is contained in:
parent
8310a94843
commit
f7ba777fff
5
repos/gems/lib/mk/vfs_audit.mk
Normal file
5
repos/gems/lib/mk/vfs_audit.mk
Normal file
@ -0,0 +1,5 @@
|
||||
SRC_CC = vfs_audit.cc
|
||||
|
||||
vpath %.cc $(REP_DIR)/src/lib/vfs/audit
|
||||
|
||||
SHARED_LIB = yes
|
80
repos/gems/run/libc_vfs_audit.run
Normal file
80
repos/gems/run/libc_vfs_audit.run
Normal file
@ -0,0 +1,80 @@
|
||||
#
|
||||
# \brief Test for auditing the vfs
|
||||
# \author Emery Hemingway
|
||||
# \date 2018-03-22
|
||||
#
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
||||
build { core init server/vfs test/libc_vfs lib/vfs/audit }
|
||||
|
||||
create_boot_directory
|
||||
|
||||
#
|
||||
# Generate config
|
||||
#
|
||||
|
||||
set config {
|
||||
<config>
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="PD"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="LOG"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
<start name="vfs">
|
||||
<resource name="RAM" quantum="12M"/>
|
||||
<provides> <service name="File_system"/> </provides>
|
||||
<config>
|
||||
<vfs>
|
||||
<dir name="ram"> <ram/> </dir>
|
||||
<dir name="audit"> <audit path="ram"/> </dir>
|
||||
</vfs>
|
||||
<default-policy root="/audit" writeable="yes"/>
|
||||
</config>
|
||||
</start>
|
||||
<start name="test-libc_vfs">
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<config>
|
||||
<iterations value="1"/>}
|
||||
append_if [have_include "power_on/qemu"] config {
|
||||
<write-read size="1M" buffer_size="8K"/>}
|
||||
append config {
|
||||
<vfs>
|
||||
<dir name="tmp"> <fs/> </dir>
|
||||
<dir name="dev"> <log/> </dir>
|
||||
</vfs>
|
||||
<libc stdout="/dev/log" cwd="/tmp"/>
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
install_config $config
|
||||
|
||||
#
|
||||
# Boot modules
|
||||
#
|
||||
|
||||
build_boot_image {
|
||||
core init vfs
|
||||
ld.lib.so libc.lib.so
|
||||
test-libc_vfs
|
||||
vfs_audit.lib.so
|
||||
}
|
||||
|
||||
#
|
||||
# Execute test case
|
||||
#
|
||||
|
||||
append qemu_args " -nographic "
|
||||
run_genode_until {.*child "test-libc_vfs" exited with exit value 0.*} 60
|
||||
|
||||
# vi: set ft=tcl :
|
74
repos/gems/run/noux_vfs_audit.run
Normal file
74
repos/gems/run/noux_vfs_audit.run
Normal file
@ -0,0 +1,74 @@
|
||||
build {
|
||||
core init
|
||||
app/sequence
|
||||
drivers/timer
|
||||
lib/libc_noux
|
||||
noux/minimal
|
||||
noux-pkg/coreutils
|
||||
server/vfs
|
||||
lib/vfs/audit
|
||||
}
|
||||
|
||||
create_boot_directory
|
||||
|
||||
install_config {
|
||||
<config verbose="yes">
|
||||
<parent-provides>
|
||||
<service name="ROM"/>
|
||||
<service name="LOG"/>
|
||||
<service name="RM"/>
|
||||
<service name="CPU"/>
|
||||
<service name="PD"/>
|
||||
<service name="IRQ"/>
|
||||
<service name="IO_MEM"/>
|
||||
<service name="IO_PORT"/>
|
||||
</parent-provides>
|
||||
<default-route>
|
||||
<any-service> <any-child/> <parent/> </any-service>
|
||||
</default-route>
|
||||
<default caps="256"/>
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
<start name="vfs">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides><service name="File_system"/></provides>
|
||||
<config>
|
||||
<vfs>
|
||||
<dir name="coreutils">
|
||||
<tar name="coreutils.tar" /> </dir>
|
||||
<dir name="audit">
|
||||
<audit path="coreutils"/> </dir>
|
||||
</vfs>
|
||||
<default-policy root="audit"/>
|
||||
</config>
|
||||
</start>
|
||||
<start name="noux">
|
||||
<resource name="RAM" quantum="16M"/>
|
||||
<config verbose="yes" stdin="/null" stdout="/log" stderr="/log">
|
||||
<fstab> <null/> <log/> <fs/> </fstab>
|
||||
<start name="/bin/uname"> <arg value="-a"/> </start>
|
||||
</config>
|
||||
</start>
|
||||
</config>
|
||||
}
|
||||
|
||||
build_boot_image {
|
||||
core init ld.lib.so
|
||||
coreutils.tar
|
||||
libc_noux.lib.so
|
||||
libm.lib.so
|
||||
noux libc.lib.so
|
||||
posix.lib.so
|
||||
sequence
|
||||
timer
|
||||
vfs_audit.lib.so
|
||||
vfs
|
||||
}
|
||||
|
||||
append qemu_args " -nographic -serial mon:stdio "
|
||||
|
||||
# coreutils.tar is really huge when built for x86_64
|
||||
|
||||
run_genode_until {child "sequence" exited with exit value 0.*\n} 30
|
2
repos/gems/src/lib/vfs/audit/target.mk
Normal file
2
repos/gems/src/lib/vfs/audit/target.mk
Normal file
@ -0,0 +1,2 @@
|
||||
TARGET = dummy-vfs_audit
|
||||
LIBS = vfs_audit
|
311
repos/gems/src/lib/vfs/audit/vfs_audit.cc
Normal file
311
repos/gems/src/lib/vfs/audit/vfs_audit.cc
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* \brief VFS audit plugin
|
||||
* \author Emery Hemingway
|
||||
* \date 2018-03-12
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2018 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <vfs/file_system_factory.h>
|
||||
#include <vfs/types.h>
|
||||
#include <log_session/connection.h>
|
||||
|
||||
namespace Vfs_audit {
|
||||
using namespace Vfs;
|
||||
class File_system;
|
||||
}
|
||||
|
||||
class Vfs_audit::File_system : public Vfs::File_system
|
||||
{
|
||||
private:
|
||||
|
||||
class Log : public Genode::Output
|
||||
{
|
||||
private:
|
||||
|
||||
enum { BUF_SIZE = Genode::Log_session::MAX_STRING_LEN };
|
||||
|
||||
Genode::Log_connection _log;
|
||||
|
||||
char _buf[BUF_SIZE];
|
||||
unsigned _num_chars = 0;
|
||||
|
||||
void _flush()
|
||||
{
|
||||
_buf[_num_chars] = '\0';
|
||||
_log.write(Genode::Log_session::String(_buf, _num_chars+1));
|
||||
_num_chars = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Log(Genode::Env &env, char const *label)
|
||||
: _log(env, label) { }
|
||||
|
||||
void out_char(char c) override
|
||||
{
|
||||
_buf[_num_chars++] = c;
|
||||
if (_num_chars >= sizeof(_buf)-1)
|
||||
_flush();
|
||||
}
|
||||
|
||||
template <typename... ARGS>
|
||||
void log(ARGS &&... args)
|
||||
{
|
||||
Output::out_args(*this, args...);
|
||||
_flush();
|
||||
}
|
||||
|
||||
} _audit_log;
|
||||
|
||||
template <typename... ARGS>
|
||||
inline void _log(ARGS &&... args) { _audit_log.log(args...); }
|
||||
|
||||
Vfs::File_system &_root_dir;
|
||||
|
||||
Absolute_path const _audit_path;
|
||||
|
||||
/**
|
||||
* Expand a path to lay within the audit path
|
||||
*/
|
||||
inline Absolute_path _expand(char const *path) {
|
||||
return Absolute_path(path+1, _audit_path.string()); }
|
||||
|
||||
struct Handle final : Vfs_handle
|
||||
{
|
||||
Handle(Handle const &);
|
||||
Handle &operator = (Handle const &);
|
||||
|
||||
Absolute_path const path;
|
||||
Vfs_handle *audit = nullptr;
|
||||
|
||||
void sync_state()
|
||||
{
|
||||
audit->seek(Vfs_handle::seek());
|
||||
audit->context = context;
|
||||
}
|
||||
|
||||
Handle(Vfs_audit::File_system &fs,
|
||||
Genode::Allocator &alloc,
|
||||
int flags,
|
||||
char const *path)
|
||||
: Vfs_handle(fs, fs, alloc, flags), path(path) { };
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
File_system(Genode::Env &env,
|
||||
Genode::Allocator &alloc,
|
||||
Genode::Xml_node config,
|
||||
Vfs::Io_response_handler &io_handler,
|
||||
Vfs::File_system &root_dir)
|
||||
:
|
||||
_audit_log(env, config.attribute_value("label", Genode::String<64>("audit")).string()),
|
||||
_root_dir(root_dir),
|
||||
_audit_path(config.attribute_value(
|
||||
"path", Genode::String<Absolute_path::capacity()>()).string())
|
||||
{
|
||||
(void)env;
|
||||
(void)alloc;
|
||||
(void)io_handler;
|
||||
}
|
||||
|
||||
const char* type() override { return "audit"; }
|
||||
|
||||
/***********************
|
||||
** Directory service **
|
||||
***********************/
|
||||
|
||||
Genode::Dataspace_capability dataspace(const char *path) override
|
||||
{
|
||||
_log(__func__, " ", path);
|
||||
return _root_dir.dataspace(_expand(path).string());
|
||||
}
|
||||
|
||||
void release(char const *path, Dataspace_capability ds) override
|
||||
{
|
||||
_log(__func__, " ", path);
|
||||
return _root_dir.release(_expand(path).string(), ds);
|
||||
}
|
||||
|
||||
Open_result open(const char *path, unsigned int mode, Vfs::Vfs_handle **out, Genode::Allocator &alloc) override
|
||||
{
|
||||
_log(__func__, " ", path, " ", Genode::Hex(mode, Genode::Hex::OMIT_PREFIX, Genode::Hex::PAD));
|
||||
|
||||
Handle *local_handle;
|
||||
try { local_handle = new (alloc) Handle(*this, alloc, mode, path); }
|
||||
catch (Genode::Out_of_ram) { return OPEN_ERR_OUT_OF_RAM; }
|
||||
catch (Genode::Out_of_caps) { return OPEN_ERR_OUT_OF_CAPS; }
|
||||
|
||||
Open_result r = _root_dir.open(
|
||||
_expand(path).string(), mode, &local_handle->audit, alloc);
|
||||
|
||||
if (r == OPEN_OK)
|
||||
*out = local_handle;
|
||||
else
|
||||
destroy(alloc, local_handle);
|
||||
return r;
|
||||
}
|
||||
|
||||
Opendir_result opendir(char const *path, bool create,
|
||||
Vfs_handle **out, Allocator &alloc) override
|
||||
{
|
||||
_log(__func__, " ", path, create ? " create " : "");
|
||||
|
||||
Handle *local_handle;
|
||||
try { local_handle = new (alloc) Handle(*this, alloc, 0, path); }
|
||||
catch (Genode::Out_of_ram) { return OPENDIR_ERR_OUT_OF_RAM; }
|
||||
catch (Genode::Out_of_caps) { return OPENDIR_ERR_OUT_OF_CAPS; }
|
||||
|
||||
Opendir_result r = _root_dir.opendir(
|
||||
_expand(path).string(), create, &local_handle->audit, alloc);
|
||||
|
||||
if (r == OPENDIR_OK)
|
||||
*out = local_handle;
|
||||
else
|
||||
destroy(alloc, local_handle);
|
||||
return r;
|
||||
}
|
||||
|
||||
void close(Vfs::Vfs_handle *vfs_handle) override
|
||||
{
|
||||
Handle *h = static_cast<Handle*>(vfs_handle);
|
||||
_log(__func__, " ", h->path);
|
||||
if (h) {
|
||||
h->audit->ds().close(h->audit);
|
||||
destroy(h->alloc(), h);
|
||||
}
|
||||
}
|
||||
|
||||
Stat_result stat(const char *path, Vfs::Directory_service::Stat &buf) override
|
||||
{
|
||||
_log(__func__, " ", path);
|
||||
return _root_dir.stat(_expand(path).string(), buf);
|
||||
}
|
||||
|
||||
Unlink_result unlink(const char *path) override
|
||||
{
|
||||
_log(__func__, " ", path);
|
||||
return _root_dir.unlink(_expand(path).string());
|
||||
}
|
||||
|
||||
Rename_result rename(const char *from , const char *to) override
|
||||
{
|
||||
_log(__func__, " ", from, " ", to);
|
||||
return _root_dir.rename(_expand(from).string(), _expand(to).string());
|
||||
}
|
||||
|
||||
file_size num_dirent(const char *path) override
|
||||
{
|
||||
return _root_dir.num_dirent(_expand(path).string());
|
||||
}
|
||||
|
||||
bool directory(char const *path) override
|
||||
{
|
||||
return _root_dir.directory(_expand(path).string());
|
||||
}
|
||||
|
||||
const char* leaf_path(const char *path) override
|
||||
{
|
||||
return _root_dir.leaf_path(_expand(path).string());
|
||||
}
|
||||
|
||||
/**********************
|
||||
** File I/O service **
|
||||
**********************/
|
||||
|
||||
Write_result write(Vfs_handle *vfs_handle,
|
||||
const char *buf, file_size len,
|
||||
file_size &out) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().write(h.audit, buf, len, out);
|
||||
}
|
||||
|
||||
bool queue_read(Vfs_handle *vfs_handle, file_size len) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().queue_read(h.audit, len);
|
||||
}
|
||||
|
||||
Read_result complete_read(Vfs_handle *vfs_handle,
|
||||
char *buf, file_size len,
|
||||
file_size &out) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().complete_read(h.audit, buf, len, out);
|
||||
}
|
||||
|
||||
bool read_ready(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().read_ready(h.audit);
|
||||
}
|
||||
|
||||
bool notify_read_ready(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().notify_read_ready(h.audit);
|
||||
}
|
||||
|
||||
Ftruncate_result ftruncate(Vfs_handle *vfs_handle,
|
||||
file_size len) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
_log(__func__, " ", h.path, " ", len);
|
||||
return h.audit->fs().ftruncate(h.audit, len);
|
||||
}
|
||||
|
||||
bool check_unblock(Vfs_handle *vfs_handle, bool rd, bool wr, bool ex) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().check_unblock(h.audit, rd, wr, ex);
|
||||
}
|
||||
|
||||
void register_read_ready_sigh(Vfs_handle *vfs_handle, Signal_context_capability sigh) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
return h.audit->fs().register_read_ready_sigh(h.audit, sigh);
|
||||
}
|
||||
|
||||
Sync_result complete_sync(Vfs_handle *vfs_handle) override
|
||||
{
|
||||
Handle &h = *static_cast<Handle*>(vfs_handle);
|
||||
h.sync_state();
|
||||
_log("sync ", h.path);
|
||||
return h.audit->fs().complete_sync(h.audit);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
|
||||
{
|
||||
struct Factory : Vfs::File_system_factory
|
||||
{
|
||||
Vfs::File_system *create(Genode::Env &env,
|
||||
Genode::Allocator &alloc,
|
||||
Genode::Xml_node config,
|
||||
Vfs::Io_response_handler &io_handler,
|
||||
Vfs::File_system &root_dir) override
|
||||
{
|
||||
return new (alloc)
|
||||
Vfs_audit::File_system(env, alloc, config, io_handler, root_dir);
|
||||
}
|
||||
};
|
||||
|
||||
static Factory f;
|
||||
return &f;
|
||||
}
|
Loading…
Reference in New Issue
Block a user