This project is under Development, most functionalities are not implemented yet.
LDFL (LD File Liar) is a powerful LD_PRELOAD
library that intercepts and modify libc
file system operations. It allows you to:
- Log Filesystem Interactions: File & Directory manipulation can be logged to syslog or stderr
- Remap File Paths: Redirect file access to different locations
- Control File Access: Restrict or allow access to specific files/directories
- Modify File Permissions: Change ownership and permissions on-the-fly
- Memory-based Files: Serve files directly from memory
- Static Content: Serve predefined static content
- Executable Redirection: Redirect executable paths
This tool can be used on existing binaries or can be included with a static configuration header inside your projects.
You can also view the latest documentation online at GitHub Pages.
The following dependencies are required to build ldfl:
- CMake (version 3.12 or higher)
- PCRE2 library
- Jansson library
- CUnit (optional, for tests)
- Doxygen (optional, for documentation)
On Ubuntu/Debian, you can install these dependencies with:
sudo apt update
sudo apt install -y cmake libpcre2-dev libjansson-dev libcunit1-dev doxygen
- Clone the repository:
git clone https://github.com/kakwa/ldfl.git
cd ldfl
- Create a build directory and configure CMake:
cmake .
- Build the project:
make
Optional build options:
-DBUILD_TESTS=ON
: Enable building tests-DBUILD_DOC=ON
: Enable building documentation-DCOVERAGE=ON
: Enable code coverage (requires-DBUILD_TESTS=ON
)-DDEBUG=ON
: Build with debug symbols-DSTATIC=ON
: Build static library
To run the tests & coverage
cmake . -DBUILD_TESTS=ON -DCOVERAGE=ON
make
make coverage
$BROWSER coverage/index.html
To build the documentation:
# Configure CMake
cmake -DBUILD_DOC=ON .
# Get Doxygen awesome CSS
./misc/setup_doxycss.sh
# Build Doc
make
# open the doc
$BROWSER ./docs/html/index.html
After building, you can install the library system-wide:
sudo make install
This will install:
- The library (
libldfl.so
) to/usr/local/lib/
- The wrapper executable (
ldfl-cli
) to/usr/local/bin/
- Create a configuration file (e.g.,
config.json
) with your rule rules:
ldfl-cli -c config.json -- your-application [args...]
To enable debug output, use the -d
flag:
ldfl-cli -d -c config.json -- your-application [args...]
If you need to specify a custom library path:
ldfl-cli -l /path/to/libldfl.so -c config.json -- your-application [args...]
You can use LDFL directly with LD_PRELOAD
without the wrapper:
# Set the configuration file
export LDFL_CONFIG=/path/to/config.json
# Preload the library
LD_PRELOAD=/path/to/libldfl.so your-application [args...]
The configuration file is a JSON file with two main sections: settings
and rules
.
The settings
section controls the logging behavior:
{
"settings": {
"log_mask": [
"rule_found",
"fn_call",
"init",
"rule_apply",
"rule_search",
"fn_call_err"
],
"log_level": "warning",
"logger": "syslog"
}
}
Available log masks:
rule_found
: Log when a rule rule is foundfn_call
: Log LibC function callsinit
: Log initialization operationsrule_apply
: Log when a rule rule is appliedrule_search
: Log rule search operationsfn_call_err
: Log LibC function call errors
Log levels:
debug
info
warning
error
Loggers:
syslog
: System loggerstderr
: Standard error outputdummy
: No logging
The rules
section defines the file path rerule rules. Each rule has the following properties:
{
"rules": [
{
"name": "<descriptive/name>",
"search_pattern": "<regex pattern>",
"operation": "<operation type>",
"target": "<target path>",
"path_transform": "<absolute|original>",
"extra_options": "<operation specific options>",
"final": false
}
]
}
- File Redirection (
path_redir
):
{
"name": "temp files redirect",
"search_pattern": ".*/temp/([^/]*)$",
"operation": "path_redir",
"target": "/tmp/$1",
"path_transform": "absolute",
"final": false
}
- Executable Redirection (
exec_redir
):
{
"name": "executable redirect",
"search_pattern": ".*/.bin/\\([^/]*\\)$",
"operation": "exec_redir",
"target": "/opt/ldfl/bin/\\1",
"path_transform": "absolute",
"final": false
}
- Memory File (
mem_open
):
{
"name": "memory open",
"search_pattern": ".*/file[0-9].txt",
"operation": "mem_open",
"target": null,
"path_transform": "absolute",
"final": false
}
- Static File (
mem_data
):
{
"name": "static file",
"search_pattern": ".*/static.bin",
"operation": "mem_data",
"target": "default_blob",
"path_transform": "absolute",
"final": false
}
- Permission Change (
perm
):
{
"name": "change data perm",
"search_pattern": ".*/data/.*",
"operation": "perm",
"target": null,
"path_transform": "absolute",
"final": false,
"extra_options": "user:group|dir_mode|file_mode"
}
- Access Control:
- Allow (
noop
):
- Allow (
{
"name": "allow /dev",
"search_pattern": "^/dev/.*",
"operation": "noop",
"target": null,
"path_transform": "absolute",
"final": false
}
- Deny (
deny
):
{
"name": "default & deny",
"search_pattern": ".*",
"operation": "deny",
"target": null,
"path_transform": "absolute",
"final": false
}
- Read-Only (
ro
):
{
"name": "read only files",
"search_pattern": ".*/readonly/.*",
"operation": "ro",
"target": null,
"path_transform": "absolute",
"final": false
}
For embedding LDFL in your project, copy over lib/ldfl.c
in your project.
Create a header file (e.g., ldfl-config.h
) with your configuration:
static const unsigned char ldf_default_blob[] = "hello from ldfl";
ldfl_rule_t ldfl_rule[] = {
/* name search_pattern operation target path_transform, final, extra_options */
{ "temp files redirect", ".*/temp/([^/]*)$", LDFL_OP_PATH_REDIR, "/tmp/$1", LDFL_PATH_ABS, false, NULL },
{ "inc redirect", "(.*)/inc/(.*)", LDFL_OP_PATH_REDIR, "$1/lib/$2", LDFL_PATH_ABS, false, NULL },
{ "executable redirect", ".*/.bin/\\([^/]*\\)$", LDFL_OP_EXEC_REDIR, "/opt/ldfl/bin/\\1", LDFL_PATH_ABS, false, NULL },
{ "memory open", ".*/file[0-9].txt", LDFL_OP_MEM_OPEN, NULL, LDFL_PATH_ABS, false, NULL },
{ "static file", ".*/static.bin", LDFL_OP_MEM_DATA, ldf_default_blob, LDFL_PATH_ABS, false, NULL },
{ "change data perm", ".*/data/.*", LDFL_OP_PERM, NULL, LDFL_PATH_ABS, false, "kakwa:kakwa|0700|0600"},
{ "allow /dev", "^/dev/.*", LDFL_OP_NOOP, NULL, LDFL_PATH_ABS, false, NULL },
{ "default & deny", ".*", LDFL_OP_DENY, NULL, LDFL_PATH_ABS, false, NULL },
{ NULL, NULL, LDFL_OP_END, NULL, LDFL_PATH_ABS, false, NULL } // keep this last value
};
ldfl_setting_t ldfl_setting = {
.log_mask = LDFL_LOG_MAPPING_RULE_FOUND | LDFL_LOG_FN_CALL | LDFL_LOG_INIT | LDFL_LOG_MAPPING_RULE_APPLY | LDFL_LOG_FN_CALL_ERR,
.log_level = LOG_WARNING,
.logger = ldfl_syslog_logger,
};
In your code, add:
#define LDFL_CONFIG "ldfl-config.h"
#include "ldfl.c"
Link against libpcre2 (-lpcre2-8
) if necessary.
And finally, build your project.