/**
@file transport_protocol.h
@brief SI-based protocol
@details Copyright (c) 2024 Acronis International GmbH
@author Denis Kopyrin (denis.kopyrin@acronis.com)
@since $Id: $
*/
#pragma once
// Note: This file is shared between kernel and user space transport code.
// Note: Linux kernel build system defines '__KERNEL__'
#if defined __KERNEL__
#include <linux/types.h> // bool, [u]int(8|16|32|64)_t, pid_t
#else
#include <stdbool.h> // bool
#include <stdint.h> // [u]int(8|16|32|64)_t
#include <sys/types.h> // pid_t
#endif
#if !defined PACKED
#define PACKED __attribute__((packed))
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define TRANSPORT_DEVICE_NAME "apl_transport"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
Each message is either a 'query' or a 'reply'. Each message contains
an identifier ('id') necessary to match 'reply' to corresponding
'query'. Zero 'id' in 'query' means that 'reply' is not necessary.
'reply' with zero 'id' is not used.
*/
typedef uint64_t msg_id_t;
typedef enum {
// reserved 0 as an error type
AT_PING = 1,
// 8 was reserved but now used for 'AT_GET_VERSION'
// as it would behave fine - it was not used by ioctl or write
AT_GET_VERSION = 8,
AT_OPEN_FILE_FROM_MSG,
AT_OPEN_FILE_BY_PATH,
// 11 and 12 are reserved
AT_INIT_SHARED_DATA_QUEUE = 13,
AT_WAIT_SHARED_DATA_QUEUE,
// 15 is reserved
AT_FILE_CONTEXT_ADD = 16,
AT_GET_MNT_ID_OFFSET,
AT_SET_LISTENING_MASK_GLOBAL,
AT_SET_LISTENING_MASK_PROCESS,
AT_GET_PROCESS_INFO,
AT_GET_PROCESS_PID_VERSION,
// Provides mask of events that will be sent if subtype mask intersects
// Default is all bits enabled.
AT_SET_LISTENING_SUBTYPE_INCLUSION_MASK,
// Providers mask of events that will be dropped if subtypes mask intersects.
// Default is all bits disabled.
AT_SET_LISTENING_SUBTYPE_EXCLUSION_MASK,
AT_LAST,
} action_type_t;
typedef enum {
RT_ERROR = 0,
// reserved till 8
RT_OPENED_FILE = 9,
RT_VERSION_INFO,
RT_DATA_QUEUE_OFFSETS,
RT_GET_MNT_ID_OFFSET,
RT_GET_PROCESS_INFO,
RT_GET_PROCESS_PID_VERSION,
} return_type_t;
typedef uint8_t msg_type_img_t;
typedef struct PACKED {
msg_id_t id;
msg_type_img_t type; // msg_type_t
bool reply;
uint8_t payload[];
} request_msg_img_t;
inline static
msg_id_t msg_img_id(const request_msg_img_t *msg_img)
{
return msg_img->id;
}
inline static
msg_type_img_t msg_img_type(const request_msg_img_t *msg_img)
{
return msg_img->type;
}
inline static
bool msg_img_is_reply(const request_msg_img_t *msg_img)
{
return msg_img->reply;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
Generic 'reply' message
*/
// Note: Empty 'reply' may be used as shortcut for 'default' reply
/*
// Legacy 'reply_img_t'
typedef struct PACKED {
uint8_t reply[];
} reply_img_t;
*/
typedef enum {
RT_ALLOW,
RT_BLOCK,
} reply_type_t;
typedef uint8_t reply_type_img_t;
typedef struct PACKED {
reply_type_img_t type;
uint8_t payload[];
} reply_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
'transport' testing
'pong' message is 'high level' reply to 'ping' message. 'pong'
message returns 'payload' from 'ping' message.
'ping' and 'pong' messages can be sent in both 'synchronous' (i.e
with *_reply') and 'asynchronous' (i.e without *_reply') modes.
'ping_reply' and 'pong_reply' are respective 'low level'
confirmations for 'ping' and 'pong' messages. They do not return
'payload'. Instead they return some arbitrary 'reply'.
'ping' will set 'properties_number' and copy 'payload' as is, it is
up to sender to interpret 'payload' correctly.
*/
typedef struct PACKED {
uint64_t process_uid;
uint32_t properties_number;
uint8_t payload[];
} ping_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
task_info_map management
*/
typedef uint32_t pid_img_t;
typedef uint32_t uid_img_t;
typedef uint32_t gid_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// AT_OPEN_FILE_FROM_MSG
typedef struct PACKED {
// useful for 'rename*' message that may refer to 2 files
int num;
} open_file_from_msg_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// AT_OPEN_FILE_BY_PATH
typedef struct PACKED {
int flags;
int mode;
char path[];
} open_file_by_path_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// RT_OPENED_FILE
typedef struct PACKED {
int fd;
} opened_file_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// AT_SET_LISTENING_MASK_PROCESS
typedef struct PACKED {
pid_img_t pid;
// You may provide pid_version==0 or unique_pid==0 to avoid checking those
// but it is unlikely that it is what you actually need.
// If unique_pid==0, a different process on pid wrapping might be filtered,
// if pid_version==0, a different process exec instance might be filtered.
uint64_t pid_version;
uint64_t unique_pid;
uint64_t events_mask;
} process_set_listening_mask_img_t;
// Complex events are events that are made from pre and post events together.
// It is very common that on LSM there is full information about the file path
// but on POST event there is only 'inode' which does not provide full path or mount.
// Userspace combines those 2 events to create a POST full event using 'replay cache'.
// Driver is capable of sending 'CLOSE' events
#define DRIVER_FEATURE_CLOSE_EVENT (1 << 0)
// Driver is using AUDIT events to generate Login/Logout events
#define DRIVER_FEATURE_AUDIT_EVENTS (1 << 1)
// Driver is using 'fsnotify' to send 'POST' events
#define DRIVER_FEATURE_FSNOTIFY_EVENTS (1 << 2)
// Driver is using LSM 'struct path' to instantiate OPEN events instead of syscall hooks
// This also means that driver is sending 'FSNOTIFY_CREATE' events for complex 'CREATE'.
// It is not explicitly specified how 'inode' 'CREATE' is done - it can be done via 'fsnotify' or LSM 'inode_create'.
// A usual flow is getting 'inode' CREATE event and enriching it with 'path' from LSM 'file_open' event.
// This flag requires 'DRIVER_FEATURE_FSNOTIFY_EVENTS' to be set.
#define DRIVER_FEATURE_LSM_FILE_OPEN_COMPLEX_CREATE (1 << 3)
// Driver is using LSM with CONFIG_SECURITY_PATH to instantiate non-OPEN complex events
// instead of syscall hooks that are replayed using 'fsnotify' callbacks.
// There are currently 2 events that fall in this category - 'rename' and 'unlink/rmdir'.
// They needed to ensure those operations did succeed for POST callbacks.
// Userspace client is expected to handle PRE LSM events and verify it using POST 'fsnotify'.
// This flag requires 'DRIVER_FEATURE_FSNOTIFY_EVENTS' and 'DRIVER_FEATURE_LSM_GENERIC' to be set.
#define DRIVER_FEATURE_LSM_SECURITY_PATH_COMPLEX_POST (1 << 4)
// Driver is leveraging proc_connector hooks to have sleepable process events.
// This allows to have a more stable process events and required from 'cmdline' in EXEC events.
#define DRIVER_FEATURE_SLEEPABLE_PROCESS_EVENTS (1 << 5)
// Driver is using syscall hooks instead of LSM for events that requires complex POST.
// Such events are currently 'rename' and 'unlink/rmdir'.
// Should not be set with 'DRIVER_FEATURE_LSM_SECURITY_PATH_COMPLEX_POST'
#define DRIVER_FEATURE_FS_SYSCALL_HOOKS_COMPLEX_POST (1 << 6)
// Driver is using LSM to generate generic events like 'settime' that do not require 'CONFIG_SECURITY_PATH'
#define DRIVER_FEATURE_LSM_GENERIC (1 << 7)
// Driver is using LSM with CONFIG_SECURITY_PATH to instantiate events that do not require 'fsnotify'
// For example those events are 'truncate', 'link',...
// This flag requires 'DRIVER_FEATURE_LSM_GENERIC' to be set.
#define DRIVER_FEATURE_LSM_SECURITY_PATH_SIMPLE_PRE (1 << 8)
// Driver is providing 'mmap' events using LSM.
// This flag requires 'DRIVER_FEATURE_LSM_GENERIC' to be set.
#define DRIVER_FEATURE_LSM_FILE_MMAP (1 << 9)
// Driver can provide command line for EXEC events.
#define DRIVER_FEATURE_COMMAND_LINE_IN_EXEC (1 << 10)
// Driver provides proper absolute paths in events without chroot
#define DRIVER_FEATURE_ABSOLUTE_PATHS (1 << 11)
// Driver is using 'rw_verify_area' to send read/write events
#define DRIVER_FEATURE_RW_VERIFY_AREA_EVENTS (1 << 12)
// Driver is using LSM to send network events.
// This flag requires 'DRIVER_FEATURE_LSM_GENERIC' to be set.
#define DRIVER_FEATURE_LSM_NETWORK (1 << 13)
// Driver is capable of using kretprobe hooks
#define DRIVER_FEATURE_KRETPROBE_HOOKS (1 << 14)
// Driver can handle process failed lookups
#define DRIVER_FEATURE_FAILED_LOOKUP (1 << 15)
// Driver sends OS accelerated copy file events for 'sendfile'
#define DRIVER_FEATURE_COPYFILE_SPLICE (1 << 16)
// Driver sends OS accelerated copy file events for 'copy_file_range'
#define DRIVER_FEATURE_COPYFILE_COPY_FILE_RANGE (1 << 17)
// Driver sends OS accelerated copy file events for 'FICLONE'
#define DRIVER_FEATURE_COPYFILE_CLONE (1 << 18)
// RT_VERSION_INFO
typedef struct PACKED {
// Max Action allowed to be called in 'ioctl'/'write' calls
// If AT_GET_VERSION errors out itself, 'max_action' should be AT_GET_VERSION
uint8_t max_action;
// Features exposed by the module that are outside of 'ioctl'/'write' scope
uint64_t features;
} version_info_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#define MSG_TYPE_TO_EVENT_MASK(mt) (1ULL << (mt))
// AT_SET_LISTENING_MASK_GLOBAL
// AT_SET_LISTENING_SUBTYPE_INCLUSION_MASK
// AT_SET_LISTENING_SUBTYPE_EXCLUSION_MASK
typedef struct PACKED {
// Mask of events as per 'LEGACY_MSG_TYPE_TO_EVENT_MASK' or per 'MSG_TYPE_TO_EVENT_MASK'
uint64_t events_mask;
} events_mask_img_t;
typedef struct PACKED
{
int mntId;
int fd;
} mnt_info_t;
// AT_GET_MNT_ID_OFFSET
typedef struct PACKED {
uint32_t count;
char data[];
} mnt_info_img_t;
// RT_GET_MNT_ID_OFFSET
typedef struct PACKED {
bool ok;
} mnt_info_ret_img_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
typedef uint64_t offset_img_t; // Note: 'loff_t' is 'signed'
typedef uint64_t size_img_t;
// Technically 'file_key_t' is not used in transport but it is oftentimes represented the same way
// in userspace and kernel space. So it is defined here for convenience.
typedef struct
{
uint64_t ptr;
uint64_t ino;
uint64_t gen;
uint64_t dev;
} file_key_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
typedef enum {
IOCTL_READ_MSG = 1,
IOCTL_WRITE_MSG = 2,
IOCTL_WRITE_AND_READ_MSG = 3,
IOCTL_READ_VERSION = 4,
} ioctl_cmd_t;
typedef uint16_t ioctl_size_img_t;
typedef struct PACKED {
ioctl_size_img_t capacity; // payload capacity;
ioctl_size_img_t size; // actual payload size;
uint8_t payload[];
} ioctl_hdr_t;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Shared Data queue protocol
// Entry description in the mmap'd buffer
// !!! This structure should be exactly the same as 'SiEvent' start !!!
typedef struct {
uint32_t size;
uint8_t data[];
} data_queue_entry_t;
// Params passed to the data_queue init
// AT_INIT_SHARED_DATA_QUEUE
typedef struct PACKED {
uint32_t size;
} data_queue_params_t;
// Those offsets correspond to values in the 'mmap' buffer
//
/*
struct shared_data_queue {
uint32_t size ____cacheline_aligned_in_smp;
uint32_t head ____cacheline_aligned_in_smp;
uint32_t tail ____cacheline_aligned_in_smp;
struct data_queue_entry entries[] ____cacheline_aligned_in_smp;
};
*/
// RT_DATA_QUEUE_OFFSETS
typedef struct {
uint32_t size; // as size of the whole 'queue'
uint32_t headOff;
uint32_t tailOff;
uint32_t entriesOff;
} data_queue_offsets_t;
typedef struct PACKED {
pid_img_t pid; // pid_t
} get_pid_info_img_t;
typedef get_pid_info_img_t get_process_info_img_t;
typedef get_pid_info_img_t get_process_pid_version_img_t;
// RT_GET_PROCESS_PID_VERSION
// Returns 'generated' fields for the process from task info map
typedef struct PACKED {
uint64_t pid_version;
uint64_t artificial_process_start_timestamp;
} process_pid_version_ret_img_t;
// New events are like SiEvent
// Will need to be 'recoded' to public SiEvent if necessary
typedef enum {
#define FP_SI_MSG_TYPE(x) FP_SI_OT_##x,
#include "transport_message_types_x.h"
#undef FP_SI_MSG_TYPE
} msg_type_t;
typedef enum {
// open is opening file to modify it. Usually it is WRITE but it can be O_TRUNC as well
FP_SI_ST_SYNC_OPEN_MODIFY,
// open has read permission
// event will be risen only if either 'MODIFY' or 'OPEN' is on, O_PATH case is not sent
FP_SI_ST_SYNC_OPEN_READ,
// O_CREAT is on
FP_SI_ST_SYNC_OPEN_MAY_CREATE,
FP_SI_ST_NOTIFY_OPEN_MODIFY,
FP_SI_ST_NOTIFY_OPEN_READ,
FP_SI_ST_NOTIFY_OPEN_MAY_CREATE,
// fsnotify only provides enough if file was opened with WRITE permissions so
// the best distinguising factor here is WRITE or !WRITE
FP_SI_ST_SYNC_CLOSE_WRITE,
FP_SI_ST_SYNC_CLOSE_NON_WRITE,
FP_SI_ST_NOTIFY_CLOSE_WRITE,
FP_SI_ST_NOTIFY_CLOSE_NON_WRITE,
// mmap WRITE is set only when mapping can writeback data to file
FP_SI_ST_SYNC_MMAP_WRITE,
FP_SI_ST_SYNC_MMAP_NON_WRITE,
FP_SI_ST_NOTIFY_MMAP_WRITE,
FP_SI_ST_NOTIFY_MMAP_NON_WRITE,
// open is related to 'opendir' call
FP_SI_ST_NOTIFY_OPENDIR,
FP_SI_ST_SYNC_OPENDIR,
// close is related to 'closedir' call
FP_SI_ST_NOTIFY_CLOSEDIR,
FP_SI_ST_SYNC_CLOSEDIR,
// flag that is set for all 'special' files operations - !regular && !dir
FP_SI_ST_SPECIAL,
FP_SI_ST_COUNT,
} msg_subtype_t;
typedef enum {
SI_PI_PRIVATE_PROPERTY_START = 0x4000,
SI_PI_PRIVATE_FSUID,
SI_PI_PRIVATE_FSGID,
SI_PI_PRIVATE_PARENT_THREAD_ID,
SI_PI_PRIVATE_OBJECT_FILE_GENERATION,
SI_PI_PRIVATE_TARGET_FILE_GENERATION,
SI_PI_PRIVATE_OBJECT_FILE_PTR,
SI_PI_PRIVATE_TARGET_FILE_PTR,
SI_PI_PRIVATE_OBJECT_FILE_MOUNT_ID,
SI_PI_PRIVATE_TARGET_FILE_MOUNT_ID,
SI_PI_PRIVATE_SEQUENCE_NUMBER,
SI_PI_PRIVATE_TARGET_FILE_HANDLE,
SI_PI_PRIVATE_TARGET_EXISTS,
SI_PI_PRIVATE_OTHER_THREAD_ID,
SI_PI_PRIVATE_OTHER_PROCESS_ID,
SI_PI_PRIVATE_OTHER_PROCESS_ID_VERSION,
SI_PI_PRIVATE_PARENT_PROCESS_ID_VERSION,
SI_PI_PRIVATE_OTHER_ARTIFICIAL_PROCESS_START_TIMESTAMP,
SI_PI_PRIVATE_FILE_MODE,
SI_PI_PRIVATE_FILE_SIZE,
SI_PI_PRIVATE_FILE_UID,
SI_PI_PRIVATE_FILE_GID,
SI_PI_PRIVATE_TARGET_FILE_ATTRIBUTES,
SI_PI_PRIVATE_TARGET_FILE_MODE,
SI_PI_PRIVATE_TARGET_FILE_SIZE,
SI_PI_PRIVATE_TARGET_FILE_UID,
SI_PI_PRIVATE_TARGET_FILE_GID,
SI_PI_PRIVATE_TARGET_FILE_MODIFICATION_TIME,
SI_PI_PRIVATE_TARGET_FILE_CHANGE_TIME,
SI_PI_PRIVATE_TARGET_FILE_ACCESS_TIME,
SI_PI_PRIVATE_VOLUME_MAGIC,
SI_PI_PRIVATE_COMMAND_LINE_TRUNCATED,
SI_PI_PRIVATE_OTHER_PROCESS_START_TIMESTAMP,
SI_PI_PRIVATE_TARGET_FILE_BIRTH_TIME,
SI_PI_PRIVATE_SOCKET_ADDRESS_IP,
SI_PI_PRIVATE_LOG_STR,
SI_PI_PRIVATE_SOCKET_LOCAL_ADDRESS_IP,
SI_PI_PRIVATE_OBJECT_FILE_DENTRY_PTR,
SI_PI_PRIVATE_OBJECT_FILE_DENTRY_NAME_PTR,
FP_SI_PI_MAX_PRIVATE_PROPERTY_ID,
} si_private_property_id_t;
#define FP_SI_CT_WANT_REPLY 0xffff
|