/
usr
/
src
/
file_protector-1.1-1506
/
transport
/
File Upload :
llllll
Current File: //usr/src/file_protector-1.1-1506/transport/fs_event.c
/** @file @brief File system events messages @details Copyright (c) 2017-2021 Acronis International GmbH @author Mikhail Krivtsov (mikhail.krivtsov@acronis.com) @since $Id: $ */ #include "fs_event.h" #include "compat.h" #include "debug.h" #include "memory.h" #include "message.h" #include "task_info_map.h" #include "transport.h" #include "transport_protocol.h" // 'linux/cred.h' appeared in 'stable/v2.6.27' #include <linux/fcntl.h> // for O_CREAT, etc. flags #include <linux/kernel.h> // for macroses such as 'ARRAY_SIZE' #include <linux/limits.h> // PATH_MAX // 'fs_event_img_t' accessors #define FSE_IMG_PID(fs_event_img) *(&(fs_event_img)->pid) #define FSE_IMG_TID(fs_event_img) *(&(fs_event_img)->tid) #define FSE_IMG_PAYLOAD(fs_event_img) (void *) (fs_event_img)->payload #define PATH_FILTERING_ENABLED // comment this #define to disable path filtering #ifdef PATH_FILTERING_ENABLED #define DEFINE_FILTER_MASK(path) {path, sizeof(path) - 1} typedef struct filter_mask { char *filter_mask_path; size_t filter_mask_len; } filter_mask_t; filter_mask_t filter_masks[] = { DEFINE_FILTER_MASK("/sys"), DEFINE_FILTER_MASK("/proc"), DEFINE_FILTER_MASK("/dev") }; #define FILTER_MASKS_NUMB ARRAY_SIZE(filter_masks) #endif // PATH_FILTERING_ENABLED static msg_t *fs_event_msg_new(msg_type_t msg_type, size_t payload_size) { msg_t *msg; size_t fs_event_img_size; size_t msg_img_size; DPRINTF("msg_type=%i/%s payload_size=%zu", msg_type, msg_type_to_string(msg_type), payload_size); fs_event_img_size = sizeof(fs_event_img_t) + payload_size; msg_img_size = sizeof(msg_img_t) + fs_event_img_size; msg = msg_new_type(msg_img_size, msg_type); if (msg) { msg_img_t *msg_img = MSG_IMG(msg); fs_event_img_t *fse_img = IMG_PAYLOAD(msg_img); /* * userspace kernel * getpid() current->tgid * gettid() current->pid */ fse_img->pid = current->tgid; fse_img->tid = current->pid; get_current_fsuid_fsgid_compat(&fse_img->fsuid, &fse_img->fsgid); } DPRINTF("msg=%p", msg); return msg; } #ifdef PATH_FILTERING_ENABLED /* * FIXME: This function is actually useless in case of relative path or any path * that contains "..", because we do not normalize paths, so arbitrary amount of * ".." can eventually point us to any file inside any directory. */ static bool is_path_filtered(const char *pathname) { size_t i = 0; for (i = 0; i < FILTER_MASKS_NUMB; i++) { // if 'path' is in 'filter_mask' folder (strncmp = 0) -> it is filtered if (((bool)strncmp(pathname, filter_masks[i].filter_mask_path, filter_masks[i].filter_mask_len)) == 0) { DPRINTF("pathname '%s' is filtered by filter_mask='%s'", pathname, filter_masks[i].filter_mask_path); return 1; } } DPRINTF("pathname '%s' isn't filtered by filter_masks", pathname); return 0; } #else static inline bool is_path_filtered(const char *pathname) { return 0; } #endif // PATH_FILTERING_ENABLED static inline bool is_msg_filtered(const char *pathname) { task_status_t status = task_info_status_get(current->tgid); if (TS_IGNORE == status) { DPRINTF("process has 'ignore' status"); return true; } if (TS_WHITE == status) { DPRINTF("process has 'white' status"); return true; } if (is_path_filtered(pathname)) { return true; } return false; } static msg_t *fs_rw_msg_new(msg_type_t msg_type, long ret_val, const char *pwd, const char *pathname, unsigned int flags, loff_t offset, size_t count) { msg_t *msg; size_t path_size, pwd_size; size_t fs_event_rw_img_size; DPRINTF("msg_type=%i/%s ret_val=%ld pwd=%s pathname=%s " "flags=0o%o/0x%X offset=0x%llX/%lli count=%zu/0x%zX", msg_type, msg_type_to_string(msg_type), ret_val, pwd, pathname, flags, flags, (long long)offset, (long long)offset, count, count); /* * FIXME: adding this logic around 'pwd' just as a temporary solution, * until we replace 'is_path_filtered()' with a more clever function * (also see comment to 'is_path_filtered()') */ if (is_msg_filtered((pwd != NULL) ? pwd : pathname)) { msg = NULL; goto out; } pwd_size = pwd ? strlen(pwd) + 1 : 0; path_size = strlen(pathname) + 1; fs_event_rw_img_size = sizeof(fs_event_rw_img_t) + pwd_size + path_size; msg = fs_event_msg_new(msg_type, fs_event_rw_img_size); if (msg) { msg_img_t *msg_img = MSG_IMG(msg); fs_event_img_t *fse_img = IMG_PAYLOAD(msg_img); fs_event_rw_img_t *rw_img = FSE_IMG_PAYLOAD(fse_img); rw_img->ret_val = ret_val; rw_img->flags = flags; rw_img->offset = offset; rw_img->count = count; if (pwd != NULL) { memcpy(rw_img->path, pwd, pwd_size); // replace '\0' to '/' rw_img->path[pwd_size - 1] = '/'; memcpy(rw_img->path + pwd_size, pathname, path_size); DPRINTF("constructed abs_path=%s", rw_img->path); } else { memcpy(rw_img->path, pathname, path_size); } } out: DPRINTF("msg=%p", msg); return msg; } inline static msg_t *move_path_in_msg(msg_t *msg, struct path *path) { if (msg) { thread_safe_path_store_move_directly(&msg->path, path); } return msg; } inline static msg_t *fs_pre_create_msg_new(const char *pwd, const char *pathname, struct path *path) { /* man 2 creat: creat() is equivalent to open() with * flags equal to O_CREAT|O_WRONLY|O_TRUNC * * To faciliate userspace business logic optimisation, send this flags here. */ return move_path_in_msg(fs_rw_msg_new(MT_FILE_PRE_CREATE, 0, pwd, pathname, O_CREAT | O_WRONLY | O_TRUNC, 0, 0), path); } inline static msg_t *fs_create_msg_new(long ret_val, const char *pwd, const char *pathname) { /* man 2 creat: creat() is equivalent to open() with * flags equal to O_CREAT|O_WRONLY|O_TRUNC * * To faciliate userspace business logic optimisation, send this flags here. */ return fs_rw_msg_new(MT_FILE_CREATE, ret_val, pwd, pathname, O_CREAT | O_WRONLY | O_TRUNC, 0, 0); } inline static msg_t *fs_pre_open_msg_new(const char *pwd, const char *filename, unsigned int flags, struct path *path) { return move_path_in_msg(fs_rw_msg_new(MT_FILE_PRE_OPEN, 0, pwd, filename, flags, 0, 0), path); } inline static msg_t *fs_open_msg_new(long ret_val, const char *pwd, const char *filename, unsigned int flags) { return fs_rw_msg_new(MT_FILE_OPEN, ret_val, pwd, filename, flags, 0, 0); } inline static msg_t *fs_pre_close_msg_new(const char *pwd, const char *filename, unsigned int flags, struct path *path) { return move_path_in_msg(fs_rw_msg_new(MT_FILE_PRE_CLOSE, 0, pwd, filename, flags, 0, 0), path); } inline static msg_t *fs_pre_write_msg_new(const char *filename, unsigned int f_flags, loff_t offset, size_t count, struct path *path) { /* * We don't need 'pwd' in 'write()' here, because current implementation * of 'sys_write_hook()' gets file info from the file descriptor, that * holds absolute path. */ return move_path_in_msg(fs_rw_msg_new(MT_FILE_PRE_WRITE, 0, /*pwd*/NULL, filename, f_flags, offset, count), path); } inline static msg_t *fs_write_msg_new(long ret_val, const char *filename, unsigned int f_flags, loff_t offset, size_t count) { /* * We don't need 'pwd' in 'write()' here, because current implementation * of 'sys_write_hook()' gets file info from the file descriptor, that * holds absolute path. */ return fs_rw_msg_new(MT_FILE_WRITE, ret_val, /*pwd*/NULL, filename, f_flags, offset, count); } static long send_msg_sync_unref_and_get_block(msg_t *msg) { long block = 0; if (msg) { send_msg_sync(msg); // If message was interrupted, the process was killed. // For the consistency, this means that syscall must be // blocked, otherwise APL might fail to backup. // Thankfully, process will not care about the // syscall result as it will be dead anyways. if (msg->block) block = -EPERM; if (msg->interrupted) block = -EINTR; thread_safe_path_clear(&msg->path); msg_unref(msg); } return block; } long fs_event_pre_create(const char *pwd, const char *pathname, struct path *path) { detect_exec(); if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_CREATE)) { return send_msg_sync_unref_and_get_block(fs_pre_create_msg_new(pwd, pathname, path)); } else { return 0; } } void fs_event_create(long ret_val, const char *pwd, const char *pathname) { if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_CREATE)) { send_msg_sync_unref(fs_create_msg_new(ret_val, pwd, pathname)); } } long fs_event_pre_open(const char *pwd, const char *filename, unsigned int flags, struct path *path) { detect_exec(); if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_OPEN)) { return send_msg_sync_unref_and_get_block(fs_pre_open_msg_new(pwd, filename, flags, path)); } else { return 0; } } void fs_event_open(long ret_val, const char *pwd, const char *filename, unsigned int flags) { if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_OPEN)) { send_msg_sync_unref(fs_open_msg_new(ret_val, pwd, filename, flags)); } } void fs_event_pre_close(const char *pwd, const char *filename, unsigned int flags, struct path *path) { detect_exec(); // TODO DK: This should be asynchronous but with 'reply'? // TODO DK: This is problematic with current 'thread_safe' path approach if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_CLOSE)) { send_msg_sync_unref(fs_pre_close_msg_new(pwd, filename, flags, path)); } } long fs_event_pre_write(const char *filename, unsigned int f_flags, loff_t offset, size_t count, struct path *path) { detect_exec(); if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_WRITE)) { return send_msg_sync_unref_and_get_block(fs_pre_write_msg_new(filename, f_flags, offset, count, path)); } else { return 0; } } void fs_event_write(long ret_val, const char *filename, unsigned int f_flags, loff_t offset, size_t count) { if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_FILE_WRITE)) { send_msg_sync_unref(fs_write_msg_new(ret_val, filename, f_flags, offset, count)); } } static inline int is_rename_msg_filtered(const char *oldname, const char *newname) { if (is_msg_filtered(oldname)) { return true; } if (is_path_filtered(newname)) { return true; } return false; } static msg_t *fs_event_rename_msg_new_impl(msg_type_t msg_type, long ret_val, const char* pwd_oldname, const char* oldname, const char* pwd_newname, const char* newname, unsigned int flags) { msg_t *msg; size_t oldname_size, newname_size; size_t pwd_oldname_size, pwd_newname_size; size_t fs_event_rename_img_size; DPRINTF("msg_type=%i/%s, ret_val=%ld, pwd_oldname = %s, oldname = %s, " "pwd_newname = %s, newname = %s, " "flags = %u", msg_type, msg_type_to_string(msg_type), ret_val, pwd_oldname, oldname, pwd_newname, newname, flags); /* * FIXME: adding this logic around 'pwd' just as a temporary solution, * until we replace 'is_path_filtered()' with a more clever function * (also see comment to 'is_path_filtered()') */ if (is_rename_msg_filtered((pwd_oldname != NULL) ? pwd_oldname : oldname, (pwd_newname != NULL) ? pwd_newname : newname)) { msg = NULL; goto out; } pwd_oldname_size = pwd_oldname ? strlen(pwd_oldname) + 1 : 0; pwd_newname_size = pwd_newname ? strlen(pwd_newname) + 1 : 0; oldname_size = strlen(oldname) + 1; newname_size = strlen(newname) + 1; fs_event_rename_img_size = sizeof(fs_event_rename_img_t) + pwd_oldname_size + pwd_newname_size + oldname_size + newname_size; msg = fs_event_msg_new(msg_type, fs_event_rename_img_size); if (msg) { msg_img_t *msg_img = MSG_IMG(msg); fs_event_img_t *fse_img = IMG_PAYLOAD(msg_img); fs_event_rename_img_t *rename_img = FSE_IMG_PAYLOAD(fse_img); size_t newname_offset = oldname_size + pwd_oldname_size; rename_img->newname_offset = newname_offset; if (pwd_oldname != NULL) { memcpy(rename_img->names, pwd_oldname, pwd_oldname_size); // replace '\0' to '/' rename_img->names[pwd_oldname_size - 1] = '/'; memcpy(rename_img->names + pwd_oldname_size, oldname, oldname_size); DPRINTF("constructed abs_oldpath=%s", rename_img->names); } else { memcpy(rename_img->names, oldname, oldname_size); } if (pwd_newname != NULL) { memcpy(rename_img->names + newname_offset, pwd_newname, pwd_newname_size); // replace '\0' to '/' rename_img->names[newname_offset + pwd_newname_size - 1] = '/'; memcpy(rename_img->names + newname_offset + pwd_newname_size, newname, newname_size); DPRINTF("constructed abs_newpath=%s", rename_img->names + newname_offset); } else { memcpy(rename_img->names + newname_offset, newname, newname_size); } rename_img->ret_val = ret_val; rename_img->flags = flags; } out: DPRINTF("msg=%p", msg); return msg; } inline static msg_t *fs_event_pre_rename_msg_new(const char* pwd_oldname, const char* oldname, const char* pwd_newname, const char* newname, unsigned int flags, struct path *path) { return move_path_in_msg(fs_event_rename_msg_new_impl(MT_PRE_RENAME, 0, pwd_oldname, oldname, pwd_newname, newname, flags), path); } inline static msg_t *fs_event_rename_msg_new(long ret_val, const char* pwd_oldname, const char* oldname, const char* pwd_newname, const char* newname, unsigned int flags) { return fs_event_rename_msg_new_impl(MT_RENAME, ret_val, pwd_oldname, oldname, pwd_newname, newname, flags); } long fs_event_pre_rename(const char* pwd_oldname, const char* oldname, const char* pwd_newname, const char* newname, unsigned int flags, struct path *path) { detect_exec(); if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_PRE_RENAME)) { return send_msg_sync_unref_and_get_block( fs_event_pre_rename_msg_new(pwd_oldname, oldname, pwd_newname, newname, flags, path)); } else { return 0; } } void fs_event_rename(long ret_val, const char* pwd_oldname, const char* oldname, const char* pwd_newname, const char* newname, unsigned int flags) { if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_RENAME)) { send_msg_sync_unref(fs_event_rename_msg_new(ret_val, pwd_oldname, oldname, pwd_newname, newname, flags)); } } static msg_t *fs_event_unlink_msg_new_impl(msg_type_t msg_type, long ret_val, const char *pwd, const char *pathname, int flag) { msg_t *msg; size_t path_size, pwd_size; size_t fs_event_unlink_img_size; DPRINTF("msg_type=%i/%s, ret_val=%ld, pwd = %s, pathname = %s, flag = %d", msg_type, msg_type_to_string(msg_type), ret_val, pwd, pathname, flag); /* * FIXME: adding this logic around 'pwd' just as a temporary solution, * until we replace 'is_path_filtered()' with a more clever function * (also see comment to 'is_path_filtered()') */ if (is_msg_filtered((pwd != NULL) ? pwd : pathname)) { msg = NULL; goto out; } pwd_size = pwd ? strlen(pwd) + 1 : 0; path_size = strlen(pathname) + 1; fs_event_unlink_img_size = sizeof(fs_event_unlink_img_t) + pwd_size + path_size; msg = fs_event_msg_new(msg_type, fs_event_unlink_img_size); if (msg) { msg_img_t *msg_img = MSG_IMG(msg); fs_event_img_t *fse_img = IMG_PAYLOAD(msg_img); fs_event_unlink_img_t *unlink_img = FSE_IMG_PAYLOAD(fse_img); unlink_img->ret_val = ret_val; unlink_img->flag = flag; if (pwd != NULL) { memcpy(unlink_img->path, pwd, pwd_size); // replace '\0' to '/' unlink_img->path[pwd_size - 1] = '/'; memcpy(unlink_img->path + pwd_size, pathname, path_size); DPRINTF("constructed abs_path=%s", unlink_img->path); } else { memcpy(unlink_img->path, pathname, path_size); } } out: DPRINTF("msg=%p", msg); return msg; } inline static msg_t *fs_event_pre_unlink_msg_new(const char *pwd, const char *pathname, int flag, struct path *path) { return move_path_in_msg(fs_event_unlink_msg_new_impl(MT_PRE_UNLINK, 0, pwd, pathname, flag), path); } inline static msg_t *fs_event_unlink_msg_new(long ret_val, const char *pwd, const char* pathname, int flag) { return fs_event_unlink_msg_new_impl(MT_UNLINK, ret_val, pwd, pathname, flag); } long fs_event_pre_unlink(const char *pwd, const char* pathname, int flag, struct path *path) { detect_exec(); if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_PRE_UNLINK)) { return send_msg_sync_unref_and_get_block(fs_event_pre_unlink_msg_new(pwd, pathname, flag, path)); } else { return 0; } } void fs_event_unlink(long ret_val, const char *pwd, const char* pathname, int flag) { if (transport_global_get_combined_mask() & MSG_TYPE_TO_EVENT_MASK(MT_UNLINK)) { send_msg_sync_unref(fs_event_unlink_msg_new(ret_val, pwd, pathname, flag)); } }
Copyright ©2k19 -
Hexid
|
Tex7ure