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