????
| Current Path : /var/lib/dkms/file_protector/1.1-1578/source/ |
| Current File : //var/lib/dkms/file_protector/1.1-1578/source/si_writer_common.h |
/**
@file si_writer_common.hpp
@brief Common writer
@details Copyright (c) 2024 Acronis International GmbH
@author Denis Kopyrin (Denis.Kopyrin@acronis.com)
@since $Id: $
*/
#ifdef BPF_PROGRAM
#include "compat_ebpf.h"
#else
#include <linux/audit.h>
#include <linux/cgroup.h>
#include <linux/ktime.h>
#include "file_handle_tools.h"
#include "file_path_tools.h"
#include "task_info_map.h"
#include "transport.h"
#endif
#include "si_writer.h"
static inline uint64_t get_inode_dev(const struct inode *inode) {
uint64_t dev = (uint64_t) KERNEL_READ2(inode, i_sb, s_dev);
#ifdef BPF_PROGRAM
uint64_t fs_dev = get_fs_inode_dev(inode);
if (fs_dev != 0) {
dev = fs_dev;
}
#endif
return dev;
}
#ifdef KERNEL_MOCK
#include "mock/mock.h"
#define HAVE_UUID_IN_SUPER_BLOCK
#endif
#define SI_ESTIMATE_SIZE_PATH_INFO(path_info) ((path_info).str.length)
#define SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(fh_info) ((fh_info).handle_bytes + sizeof((fh_info).handle_type))
#ifndef BPF_PROGRAM
#define PATH_FILTERED_STATUS_T int
#define PATH_IS_NOT_FILTERED 1
#endif
#ifndef BPF_PROGRAM
static inline bool si_property_writer_write_object_path(si_property_writer_t *writer, const struct path *path)
{
(void)writer;
(void)path;
return true;
}
static inline bool si_property_writer_write_target_path(si_property_writer_t *writer, const struct path *path)
{
(void)writer;
(void)path;
return true;
}
static inline void si_property_writer_write_image_file_path(si_property_writer_t *writer, const struct path *path)
{
(void)writer;
(void)path;
}
static inline void si_property_writer_write_image_file_comm(si_property_writer_t *writer, const char *comm)
{
(void)writer;
(void)comm;
}
#endif
static inline void si_property_writer_write_object_key(si_property_writer_t *writer, const file_key_t* key) {
SiObjectId object_id;
object_id.DeviceId = key->dev;
object_id.Id = key->ino;
si_property_writer_write_object_id(writer, object_id);
si_property_writer_write_object_file_generation(writer, key->gen);
// TODO: This pointer should be hidden from userspace!
si_property_writer_write_object_file_ptr(writer, key->ptr);
}
#ifdef BPF_PROGRAM
static inline void si_property_writer_write_common(si_property_writer_t* writer, uint64_t event_uid, pid_t pid, pid_t tgid, uint64_t pid_version)
#else
static inline void si_property_writer_write_common(si_property_writer_t* writer, uint64_t event_uid, pid_t pid, pid_t tgid, task_info_t* task_info)
#endif
{
SiTimeMicroseconds eventUs;
#ifndef BPF_PROGRAM
eventUs.microseconds = ktime_to_us(ktime_get_real());
#else
eventUs.microseconds = bpf_ktime_get_ns () / 1000;
#endif
si_property_writer_write_event_uid(writer, event_uid);
si_property_writer_write_thread_id(writer, pid);
si_property_writer_write_process_id(writer, tgid);
si_property_writer_write_event_timestamp(writer, eventUs);
#ifdef BPF_PROGRAM
si_property_writer_write_process_id_version(writer, pid_version);
#else
si_property_writer_write_process_id_version(writer, task_info ? READ_ONCE(task_info->pid_version) : 0);
#endif
}
#ifdef BPF_PROGRAM
#define CONV_TIMESPEC(from, to) do{ struct timespec64 _from = (from); to.seconds = (uint64_t) (_from.tv_sec); to.nanoseconds = (uint64_t) (_from.tv_nsec); }while(0)
#else
#define CONV_TIMESPEC(from, to) do{ to.seconds = (uint64_t) ((from).tv_sec); to.nanoseconds = (uint64_t) ((from).tv_nsec); }while(0)
#endif
#ifdef STATX_BTIME
// Currently this code is kernel module only
#define FILE_WRITER_KSTAT_FUNCTIONS_FOR(name) \
static inline void si_property_writer_write_##name##_kstat(si_property_writer_t* writer, const struct inode *inode, const struct kstat *sb) { \
SiTimeSpec t; \
if (sb->result_mask & STATX_MODE) { \
si_property_writer_write_##name##_file_mode(writer, sb->mode); \
} else { \
si_property_writer_write_##name##_file_mode(writer, inode->i_mode); \
} \
if (sb->result_mask & STATX_SIZE) { \
si_property_writer_write_##name##_file_size(writer, sb->size); \
} else { \
si_property_writer_write_##name##_file_size(writer, i_size_read(inode)); \
} \
if (sb->result_mask & STATX_UID) \
si_property_writer_write_##name##_file_uid(writer, from_kuid_compat(sb->uid)); \
if (sb->result_mask & STATX_GID) \
si_property_writer_write_##name##_file_gid(writer, from_kgid_compat(sb->gid)); \
if (sb->result_mask & STATX_CTIME) { \
CONV_TIMESPEC(sb->ctime, t); \
si_property_writer_write_##name##_file_change_time(writer, t); \
} \
if (sb->result_mask & STATX_MTIME) { \
CONV_TIMESPEC(sb->mtime, t); \
si_property_writer_write_##name##_file_modification_time(writer, t); \
} \
if (sb->result_mask & STATX_ATIME) { \
CONV_TIMESPEC(sb->atime, t); \
si_property_writer_write_##name##_file_access_time(writer, t); \
} \
if (sb->result_mask & STATX_BTIME) { \
CONV_TIMESPEC(sb->btime, t); \
si_property_writer_write_##name##_file_birth_time(writer, t); \
} \
{ \
dev_t sb_dev = inode->i_sb->s_dev; \
SiObjectId id; \
id.DeviceId = sb->dev; \
id.Id = sb->ino; \
si_property_writer_write_##name##_id(writer, id); \
if (sb_dev != id.DeviceId) si_property_writer_write_##name##_file_superblock_dev(writer, (uint64_t) sb_dev); \
if (inode->i_ino != id.Id) si_property_writer_write_##name##_file_inode_ino(writer, (uint64_t) inode->i_ino); \
si_property_writer_write_##name##_file_ptr(writer, (uint64_t) inode); \
si_property_writer_write_##name##_file_generation(writer, inode->i_generation); \
} \
}
#else
// This function will never be called if btime is not available currently
#define FILE_WRITER_KSTAT_FUNCTIONS_FOR(name) \
static inline void si_property_writer_write_##name##_kstat(si_property_writer_t* writer, const struct inode *inode, const struct kstat *sb) { (void) writer; (void) inode; (void) sb; }
#endif
#define FILE_WRITER_FUNCTIONS_FOR(name) \
static inline void si_property_writer_write_##name##_stat(si_property_writer_t* writer, const struct inode *inode) { \
if (inode) { \
SiTimeSpec t; \
si_property_writer_write_##name##_file_mode(writer, KERNEL_READ(inode, i_mode)); \
si_property_writer_write_##name##_file_size(writer, i_size_read(inode)); \
si_property_writer_write_##name##_file_uid(writer, i_uid_read(inode)); \
si_property_writer_write_##name##_file_gid(writer, i_gid_read(inode)); \
CONV_TIMESPEC(inode_get_ctime(inode), t); \
si_property_writer_write_##name##_file_change_time(writer, t); \
CONV_TIMESPEC(inode_get_mtime(inode), t); \
si_property_writer_write_##name##_file_modification_time(writer, t); \
CONV_TIMESPEC(inode_get_atime(inode), t); \
si_property_writer_write_##name##_file_access_time(writer, t); \
} \
} \
static inline void si_property_writer_write_##name##_mini_ids(si_property_writer_t* writer, const struct inode* inode) { \
if (inode) { \
dev_t sb_dev = (uint64_t) KERNEL_READ2(inode, i_sb, s_dev); \
SiObjectId id; \
id.DeviceId = get_inode_dev(inode); \
id.Id = KERNEL_READ(inode, i_ino); \
si_property_writer_write_##name##_id(writer, id); \
si_property_writer_write_##name##_file_superblock_dev(writer, sb_dev); \
si_property_writer_write_##name##_file_ptr(writer, (uint64_t) inode); \
si_property_writer_write_##name##_file_generation(writer, KERNEL_READ(inode, i_generation)); \
} \
} \
static inline void si_property_writer_write_##name##_ids(si_property_writer_t *writer, const struct path *path, const file_handle_info_t *fh) { \
const struct inode *inode = KERNEL_READ2(path, dentry, d_inode); \
if (inode) { \
struct kstat sb; \
si_property_writer_write_##name##_file_attributes(writer, KERNEL_READ(inode, i_flags)); \
if (0 == vfs_getattr_basic_fast(path, &sb)) { \
si_property_writer_write_##name##_kstat(writer, inode, &sb); \
} else { \
si_property_writer_write_##name##_stat(writer, inode); \
si_property_writer_write_##name##_mini_ids(writer, inode); \
} \
} \
if (fh) { \
si_property_writer_write_##name##_file_mount_id(writer, KERNEL_READ(fh, mnt_id)); \
if (fh->f_handle) \
si_property_writer_write_##name##_file_handle(writer, fh); \
} \
} \
static inline PATH_FILTERED_STATUS_T si_property_writer_write_##name##_file(si_property_writer_t *writer, const path_info_t* path_info, const struct path *path, const file_handle_info_t *fh) { \
if (path_info && path_info->buf) { \
si_property_writer_write_##name##_name(writer, path_info->str); \
} \
si_property_writer_write_##name##_ids(writer, path, fh); \
if (path) { \
return si_property_writer_write_##name##_path(writer, path); \
} else { \
return PATH_IS_NOT_FILTERED; \
} \
};
FILE_WRITER_KSTAT_FUNCTIONS_FOR(object)
FILE_WRITER_KSTAT_FUNCTIONS_FOR(target)
FILE_WRITER_FUNCTIONS_FOR(object)
FILE_WRITER_FUNCTIONS_FOR(target)
#undef FILE_WRITER_FUNCTIONS_FOR
#undef CONV_TIMESPEC
struct volume_id
{
uint64_t low;
uint64_t high;
};
static inline void si_property_writer_write_volume(si_property_writer_t *writer, const struct inode *inode)
{
#ifdef HAVE_UUID_IN_SUPER_BLOCK
struct volume_id id;
#endif
#ifdef HAVE_UUID_IN_SUPER_BLOCK
#ifndef BPF_PROGRAM
memcpy(&id, &inode->i_sb->s_uuid, sizeof(id));
#else
uuid_t s_uuid = KERNEL_READ2(inode, i_sb, s_uuid);
bpf_core_read(&id, sizeof(id), &s_uuid);
#endif
si_property_writer_write_volume_id_low(writer, id.low);
si_property_writer_write_volume_id_high(writer, id.high);
#endif
si_property_writer_write_volume_magic(writer, KERNEL_READ2(inode, i_sb, s_magic));
}
static inline void si_property_writer_write_object_ids_and_volume(si_property_writer_t *writer, const struct path *path, const file_handle_info_t *fh) {
si_property_writer_write_object_ids(writer, path, fh);
si_property_writer_write_volume(writer, KERNEL_READ2(path, dentry, d_inode));
}
static inline PATH_FILTERED_STATUS_T si_property_writer_write_object_file_and_volume(si_property_writer_t *writer, const path_info_t* path_info, const struct path *path, const file_handle_info_t *fh) {
if (path_info && path_info->buf) {
si_property_writer_write_object_name(writer, path_info->str);
}
si_property_writer_write_object_ids_and_volume(writer, path, fh);
if (path) {
return si_property_writer_write_object_path(writer, path);
} else {
return PATH_IS_NOT_FILTERED;
}
}
#undef FILE_WRITER_FUNCTIONS_FOR
static inline void si_property_writer_write_exe_file(si_property_writer_t *writer, const path_info_t *path_info, const struct path *path, const file_handle_info_t *fh)
{
#ifndef BPF_PROGRAM
if (path_info && path_info->buf)
{
si_property_writer_write_image_file_name(writer, path_info->str);
si_property_writer_write_process_file_name_is_not_path(writer, false /*induced from 'inode'*/);
}
else
{
si_property_writer_write_process_file_name_is_not_path(writer, true /*induced from 'comm', not 'inode'*/);
}
#else
if (path) {
si_property_writer_write_image_file_path(writer, path);
si_property_writer_write_process_file_name_is_not_path(writer, false /*induced from 'inode'*/);
}
else
{
si_property_writer_write_process_file_name_is_not_path(writer, true /*induced from 'comm', not 'inode'*/);
}
#endif
si_property_writer_write_object_ids_and_volume(writer, path, fh);
}
static inline void si_property_writer_write_exe_comm(si_property_writer_t *writer, const char* comm)
{
#ifndef BPF_PROGRAM
si_property_writer_write_image_file_name(writer, (SiSizedString){ comm, strlen(comm) });
#else //#ifndef BPF_PROGRAM
si_property_writer_write_image_file_comm(writer, comm);
#endif //#ifndef BPF_PROGRAM
si_property_writer_write_process_file_name_is_not_path(writer, true /*induced from 'comm', not 'inode'*/);
}
static inline void si_property_writer_write_creds(si_property_writer_t *writer, const struct cred *cred)
{
si_property_writer_write_user_id(writer, from_kuid_compat(KERNEL_READ(cred, uid)));
si_property_writer_write_group_id(writer, from_kgid_compat(KERNEL_READ(cred, gid)));
si_property_writer_write_saved_user_id(writer, from_kuid_compat(KERNEL_READ(cred, suid)));
si_property_writer_write_saved_group_id(writer, from_kgid_compat(KERNEL_READ(cred, sgid)));
si_property_writer_write_effective_user_id(writer, from_kuid_compat(KERNEL_READ(cred, euid)));
si_property_writer_write_effective_group_id(writer, from_kgid_compat(KERNEL_READ(cred, egid)));
}
static inline void si_property_writer_write_audit(si_property_writer_t *writer, struct task_struct* tsk)
{
// I am not sure why it is named 'loginuid' but it is outputted as 'auid' everywhere
si_property_writer_write_audit_user_id(writer, from_kuid_compat(audit_get_loginuid(tsk)));
si_property_writer_write_audit_session_id(writer, audit_get_sessionid(tsk));
}
static inline void si_property_writer_write_task_creds(si_property_writer_t *writer, struct task_struct* task)
{
const struct cred *cred;
rcu_read_lock();
cred = __task_cred(task);
if (!cred) {
// I expect this to never happen
rcu_read_unlock();
return;
}
si_property_writer_write_creds(writer, cred);
si_property_writer_write_audit(writer, task);
rcu_read_unlock();
}
static inline void si_property_writer_write_current_creds(si_property_writer_t *writer)
{
si_property_writer_write_creds(writer, current_real_cred());
si_property_writer_write_audit(writer, current);
}
static inline void si_property_writer_write_cgroup(si_property_writer_t *writer, struct task_struct* task, uint32_t name_limit)
{
#if defined(HAVE_CGROUP_NAME) || defined(HAVE_CGROUP_KERNFS_NAME)
struct cgroup_subsys_state *subsys;
struct css_set *css;
struct cgroup *cgroup;
#ifdef HAVE_CGROUP_NAME
struct cgroup_name *cgroup_name;
#endif
#ifdef HAVE_CGROUP_KERNFS_NAME
struct kernfs_node *kn;
#endif
const char* name;
SiSizedString container_name;
rcu_read_lock();
css = KERNEL_READ(task, cgroups);
if (!css)
goto out;
// Currently using subsystem 0 which is the subsystem for cpuset
subsys = KERNEL_READ(css, subsys[0]);
if (!subsys)
goto out;
cgroup = KERNEL_READ(subsys, cgroup);
if (!cgroup)
goto out;
#ifdef HAVE_CGROUP_NAME
cgroup_name = KERNEL_READ(cgroup, name);
if (!cgroup_name)
goto out;
name = KERNEL_READ(cgroup_name, name);
#endif
#ifdef HAVE_CGROUP_KERNFS_NAME
kn = KERNEL_READ(cgroup, kn);
if (!kn)
goto out;
name = KERNEL_READ(kn, name);
#endif
if (!name)
goto out;
#ifndef BPF_PROGRAM
container_name.value = name;
container_name.length = strlen(name);
if (container_name.length > name_limit)
container_name.length = name_limit;
si_property_writer_write_cgroup_name(writer, container_name);
#else
si_property_writer_write_cgroup_name_ebpf(writer, name);
#endif
out:
rcu_read_unlock();
#else
(void) writer; (void) task; (void) name_limit;
#endif
}
static inline void si_property_writer_write_current_cgroup(si_property_writer_t *writer, int name_limit)
{
si_property_writer_write_cgroup(writer, current, name_limit);
}
static inline void si_property_writer_write_fsids(si_property_writer_t *writer)
{
si_property_writer_write_fsuid(writer, get_current_fsuid_compat());
si_property_writer_write_fsgid(writer, get_current_fsgid_compat());
}