/
var
/
lib
/
dkms
/
file_protector
/
1.1-1507
/
source
/
syscall_hooks
/
File Upload :
llllll
Current File: //var/lib/dkms/file_protector/1.1-1507/source/syscall_hooks/fs_syscall_hooks.c
/** @file @brief File system calls hooks @details Copyright (c) 2017-2022 Acronis International GmbH @author Roman Anufriev (Roman.Anufriev@acronis.com) @since $Id: $ */ #include "fs_syscall_hooks.h" #include "compat.h" #include "debug.h" #include "fs_event.h" #include "memory.h" #include "syscall_common.h" #include "transport.h" #include "transport_protocol.h" #include <asm/ia32_unistd.h> // for ia32_sys_call_table '__NR_ia32_*' system call function indices #include <asm/processor-flags.h> #include <linux/dcache.h> // for 'd_unlinked()' #include <linux/errno.h> #include <linux/namei.h> // for 'user_path_at' #include <linux/fdtable.h> #include <linux/fs.h> // for 'MAX_RW_COUNT' #include <linux/fs_struct.h> // for 'get_fs_pwd()' #include <linux/pagemap.h> // for 'PAGE_CACHE_MASK' #include <linux/types.h> // bool, [u]int(8|16|32|64)_t, pid_t /* * For some syscalls (e.g. 'open()') we want to differentiate between three (not * two: SKIP and NOT_SKIP) possible cases for filtering optimizations: * * 1) SKIP: file exists and 'stat()' said that it's not regular, or 'stat()' * failed for reasons other than ENOENT => we can skip sending our events * * 2) NOT_SKIP: file exists and 'stat()' said that it's regular (or whatever type * of file we interested in) => we definitely need to send our events * * 3) PATH_NOT_EXIST: file doesn't exist (ENOENT) and: * 3.a) ~SKIP: file can't be created (i.e. user didn't pass 'O_CREAT' to syscall) => we * can skip sending our events, as there is no info for us in PRE, and POST will fail. * 3.b) ~NOT_SKIP: file can be created (i.e. user passed 'O_CREAT' to syscall) => we can skip PRE, * but want to execute 'stat()' before POST, as user may successfully create the file * and we need to determine whether we interested in it on not. * * TODO: introduce considering 'O_EXCL' as an optimization for predicting result of 'stat()' * and thus avoid excessive invoking of 'stat()' */ enum skip_state { PATH_NOT_EXISTS = -1, // not exists ('stat()' = ENOENT) => if O_CREAT -- not skip, else -- skip NOT_SKIP, // path exists ('stat()' = 0) and normal (macros are good) => definitely not skip SKIP // path exists ('stat()' = 0) and not normal (macros are bad) or any error beside ENOENT => definitely skip }; static int should_skip_file_path(int lookup_ret, struct path *path) { struct dentry *dentry; struct inode *inode; umode_t mode; if (lookup_ret == -ENOENT) { return PATH_NOT_EXISTS; } if (lookup_ret) { return SKIP; } dentry = path->dentry; if (!dentry) { return SKIP; } inode = dentry->d_inode; if (!inode) { return SKIP; } if (!inode->i_sb) { return SKIP; } mode = inode->i_mode; if ((S_ISREG(mode)) || (S_ISDIR(mode))) return NOT_SKIP; else return SKIP; } static int should_skip_rename_dst(int lookup_ret, struct path *path) { struct dentry *dentry; struct inode *inode; if (lookup_ret) { return SKIP; } dentry = path->dentry; if (!dentry) { return SKIP; } inode = dentry->d_inode; if (inode) { return NOT_SKIP; } else { return SKIP; } } static inline bool file_ok(struct file* file) { struct dentry *dentry; struct inode *inode; if (!file) return false; dentry = file->f_path.dentry; if (!dentry) return false; inode = dentry->d_inode; if (!inode) return false; if (!inode->i_sb) return false; return true; } static inline void make_key_from_inode(file_key_t* key, const struct inode *inode) { key->ptr = (uint64_t) inode; key->ino = (uint64_t) inode->i_ino; key->gen = (uint64_t) inode->i_generation; key->dev = (uint64_t) inode->i_sb->s_dev; } static inline void make_key(file_key_t* key, const struct path *path) { return make_key_from_inode(key, path->dentry->d_inode); } enum creat_hook_type { CREAT_HOOK_TYPE, IA32_CREAT_HOOK_TYPE }; DEFINE_CALL_ORIGINAL_SYSCALL_HANDLER(creat, 2, const char __user *, pathname, umode_t, mode) { switch (hook_type) { case CREAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, creat), pathname, mode); case IA32_CREAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, creat), pathname, mode); default: return -EINVAL; } } DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(creat) { switch (hook_type) { case CREAT_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, creat); case IA32_CREAT_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, creat); default: return -EINVAL; } } // man creat: 'creat()' is equivalent to 'open()' with flags equal to "O_CREAT|O_WRONLY|O_TRUNC". DEFINE_SYSCALL_HANDLER(creat, 2, const char __user *, pathname, umode_t, mode) { enum skip_state skipping = 0; syscall_hook_ret_t syscall_hook_ret; long ret = -EINVAL; char *dst_name_buf = NULL; char *dst_name; // a pointer inside 'dst_name_buf' long block = 0; struct path path = (struct path){}; file_key_t key; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_EXEC) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_CREATE) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_CREATE_EX) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_OPEN_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto out; } ret = user_path_at(AT_FDCWD, pathname, 0, &path); skipping = should_skip_file_path(ret, &path); if (skipping == SKIP) { DPRINTF("skipped sending FILE_(PRE)_CREAT, hook_type=%d, because pathname (__user ptr=%p) is not trackable", hook_type, pathname); SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto err_exit; } if ((dst_name_buf = mem_alloc(PATH_MAX)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); SET_HOOK_RETURN(-ENOMEM); goto err_exit; } if (skipping == NOT_SKIP) { // If 'skipping' is 'NOT_SKIP' path must exist so use it dst_name = d_path(&path, dst_name_buf, PATH_MAX); if (IS_ERR(dst_name)) { DPRINTF("'d_path()' failure %i", ret); SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto err_free_name_buf; } DPRINTF("hook_type=%d calling fs_event_pre_creat(path=%s)", hook_type, dst_name); make_key(&key, &path); block = fs_event_pre_create(dst_name, &path); block = block ? block : fs_event_pre_open_ex(dst_name, &key, O_CREAT | O_WRONLY | O_TRUNC, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_creat(path=%s)", hook_type, dst_name); if (block) { SET_HOOK_RETURN(block); goto err_free_name_buf; } } else { DPRINTF("skipped sending FILE_PRE_CREAT (hook_type=%d, path=%s), because \"skipping != NOT_SKIP\"", hook_type, dst_name); } if (skipping == PATH_NOT_EXISTS) { CALL_ORIGINAL_SYSCALL_HANDLER(creat, hook_type, pathname, mode); } else { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); } if (skipping == PATH_NOT_EXISTS && !IS_ERR_VALUE(ret)) { struct compat_fd f = compat_fdget(ret); if (file_ok(f.file)) { struct path* fpath = &f.file->f_path; make_key(&key, fpath); dst_name = d_path(fpath, dst_name_buf, PATH_MAX); compat_fdput(f); if (!IS_ERR(dst_name)) { DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld, path=%s)", hook_type, ret, dst_name); fs_event_create_ex(ret, dst_name, &key, O_CREAT | O_WRONLY | O_TRUNC); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld, path=%s)", hook_type, ret, dst_name); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld), because can't get path", hook_type, ret); } } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld), because can't get fd", hook_type, ret); } } else { DPRINTF("skipped sending FILE_CREAT, hook_type=%d, because path=%s isn't trackable", hook_type, dst_name); } err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, creat, 2, const char __user *, pathname, umode_t, mode) { return CALL_SYSCALL_HANDLER(creat, CREAT_HOOK_TYPE, pathname, mode); } DEFINE_SYSCALL_HOOK(ia32_sys, creat, 2, const char __user *, pathname, umode_t, mode) { return CALL_SYSCALL_HANDLER(creat, IA32_CREAT_HOOK_TYPE, pathname, mode); } enum open_hook_type { OPEN_HOOK_TYPE, IA32_OPEN_HOOK_TYPE, OPENAT_HOOK_TYPE, IA32_OPENAT_HOOK_TYPE }; DEFINE_CALL_ORIGINAL_SYSCALL_HANDLER(open, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { switch (hook_type) { case OPEN_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, open), filename, flags, mode); break; case IA32_OPEN_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, open), filename, flags, mode); break; case OPENAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, openat), dfd, filename, flags, mode); break; case IA32_OPENAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, openat), dfd, filename, flags, mode); break; default: return -EINVAL; } } DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(open) { switch (hook_type) { case OPEN_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, open); break; case IA32_OPEN_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, open); break; case OPENAT_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, openat); break; case IA32_OPENAT_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, openat); break; default: return -EINVAL; } } DEFINE_SYSCALL_HANDLER(open, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { enum skip_state skipping = 0; syscall_hook_ret_t syscall_hook_ret; long ret = -EINVAL; char *dst_name_buf = NULL; char *dst_name; long block = 0; file_key_t key; struct path path = (struct path){}; int lookup_flags = 0; bool is_create_event; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_EXEC) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_CREATE_EX) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_OPEN_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto out; } #if defined(O_DIRECTORY) && defined(LOOKUP_DIRECTORY) if (flags & O_DIRECTORY) lookup_flags |= LOOKUP_DIRECTORY; #endif #if defined(O_NOFOLLOW) && defined(LOOKUP_FOLLOW) if (!(flags & O_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; #endif ret = user_path_at(dfd, filename, lookup_flags, &path); skipping = should_skip_file_path(ret, &path); if ((skipping == SKIP) || ((skipping == PATH_NOT_EXISTS) && !(flags & O_CREAT))) { DPRINTF("skipped sending FILE_(PRE)_OPEN hook_type=%d because filename (__user ptr=%p) is not trackable", hook_type, filename); SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto err_exit; } if ((dst_name_buf = mem_alloc(PATH_MAX)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); SET_HOOK_RETURN(-ENOMEM); goto err_exit; } if (skipping == NOT_SKIP) { // If 'skipping' is 'NOT_SKIP' path must exist so use it dst_name = d_path(&path, dst_name_buf, PATH_MAX); if (IS_ERR(dst_name)) { DPRINTF("'d_path()' failure %i", ret); SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto err_free_name_buf; } DPRINTF("hook_type=%d calling fs_event_pre_open(path=%s flags=0o%o/0x%x)", hook_type, dst_name, flags, flags); make_key(&key, &path); block = fs_event_pre_open(dst_name, flags, &path); block = block ? block : fs_event_pre_open_ex(dst_name, &key, flags, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_open(path=%s flags=0o%o/0x%x)", hook_type, dst_name, flags, flags); if (block) { SET_HOOK_RETURN(block); goto err_free_name_buf; } } else { DPRINTF("skipped sending FILE_PRE_OPEN (hook_type=%d path=%s flags=0o%o/0x%x) because \"skipping != NOT_SKIP\"", hook_type, dst_name, flags, flags); } is_create_event = skipping == PATH_NOT_EXISTS && (flags & O_CREAT); if (is_create_event) { CALL_ORIGINAL_SYSCALL_HANDLER(open, hook_type, dfd, filename, flags, mode); } else { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); } if (is_create_event && !IS_ERR_VALUE(ret)) { struct compat_fd f = compat_fdget(ret); if (file_ok(f.file)) { struct path* fpath = &f.file->f_path; make_key(&key, fpath); dst_name = d_path(fpath, dst_name_buf, PATH_MAX); compat_fdput(f); if (!IS_ERR(dst_name)) { DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld, path=%s)", hook_type, ret, dst_name); fs_event_create_ex(ret, dst_name, &key, flags); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld, path=%s)", hook_type, ret, dst_name); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld), because can't get path", hook_type, ret); } } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld), because can't get fd", hook_type, ret); } } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d path=%s flags=0o%o/0x%x)", hook_type, dst_name, flags, flags); } err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, open, 3, const char __user *, filename, int, flags, umode_t, mode) { return CALL_SYSCALL_HANDLER(open, OPEN_HOOK_TYPE, AT_FDCWD, filename, flags, mode); } DEFINE_SYSCALL_HOOK(ia32_sys, open, 3, const char __user *, filename, int, flags, umode_t, mode) { return CALL_SYSCALL_HANDLER(open, IA32_OPEN_HOOK_TYPE, AT_FDCWD, filename, flags, mode); } DEFINE_SYSCALL_HOOK(sys, openat, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { return CALL_SYSCALL_HANDLER(open, OPENAT_HOOK_TYPE, dfd, filename, flags, mode); } DEFINE_SYSCALL_HOOK(ia32_sys, openat, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { return CALL_SYSCALL_HANDLER(open, IA32_OPENAT_HOOK_TYPE, dfd, filename, flags, mode); } struct fd_mini_info { umode_t mode; int unlinked; int flags; unsigned long magic; }; static void fd_get_mini_info(const struct file *file, struct fd_mini_info *info) { struct inode *inode = file_inode_compat(file); info->mode = inode->i_mode; info->flags = file->f_flags; info->magic = inode->i_sb->s_magic; info->unlinked = d_unlinked(file->f_path.dentry); } enum close_hook_type { CLOSE_HOOK_TYPE, IA32_CLOSE_HOOK_TYPE, }; DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(close) { switch (hook_type) { case CLOSE_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, close); break; case IA32_CLOSE_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, close); break; default: return -EINVAL; } } #ifndef NSFS_MAGIC #define NSFS_MAGIC 0x6e736673 #endif #ifndef O_ACCMODE #define O_ACCMODE 00000003 #endif static int magic_ok(int magic) { return magic != NSFS_MAGIC; } DEFINE_SYSCALL_HANDLER(close, 1, unsigned int, fd) { syscall_hook_ret_t syscall_hook_ret; long ret = -EINVAL; char *name_buf = NULL, *filename = NULL; struct fd_mini_info info; struct path *path; struct compat_fd f; file_key_t key; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_CLOSE_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto out; } f = compat_fdget(fd); if (!file_ok(f.file)) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto out; } fd_get_mini_info(f.file, &info); if (!S_ISREG(info.mode) || (info.unlinked) || !magic_ok(info.magic) || !(info.flags & O_ACCMODE)) { DPRINTF("skipped sending FILE_(PRE)_CLOSE hook_type=%d, because filename (fd=%lu) isn't trackable", hook_type, fd); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto err_exit; } if ((name_buf = mem_alloc(PATH_MAX)) == NULL) { IPRINTF("'%s()' for '%s' failed", "mem_alloc", "name_buf"); SET_HOOK_RETURN(-ENOMEM); goto err_exit; } path = &f.file->f_path; filename = d_path(path, name_buf, PATH_MAX); if (IS_ERR(filename)) { ret = PTR_ERR(filename); DPRINTF("'%s()' [%u] failed %ld", "d_path", fd, ret); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto err_free_name_buf; } DPRINTF("'fd_get_info_for_path()': filename=%s, f_flags=0o%o/0x%x", filename, info.flags, info.flags); make_key(&key, path); DPRINTF("hook_type=%d calling fs_event_pre_close_ex(path=%s flags=0o%o/0x%x)", hook_type, filename, info.flags, info.flags); fs_event_pre_close_ex(filename, info.flags, path, &key); DPRINTF("hook_type=%d finished fs_event_pre_close_ex(path=%s flags=0o%o/0x%x)", hook_type, filename, info.flags, info.flags); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); err_free_name_buf: if (name_buf != NULL) mem_free(name_buf); err_exit: compat_fdput(f); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, close, 1, unsigned int, fd) { return CALL_SYSCALL_HANDLER(close, CLOSE_HOOK_TYPE, fd); } DEFINE_SYSCALL_HOOK(ia32_sys, close, 1, unsigned int, fd) { return CALL_SYSCALL_HANDLER(close, IA32_CLOSE_HOOK_TYPE, fd); } #define IS_COMPAT_READ_HOOK_TYPE(read_hook_type) ((read_hook_type) >= 512) // from fs/read_write.c: static inline loff_t pos_from_hilo(unsigned long high, unsigned long low) { #ifndef HALF_LONG_BITS #define HALF_LONG_BITS (BITS_PER_LONG / 2) #endif return (((loff_t)high << HALF_LONG_BITS) << HALF_LONG_BITS) | low; } /* * Get total length of all I/O vectors passed to '*readv()' and '*writev()' * * RETURN VALUE: ssize_t */ #define get_total_len(vec, vlen) \ ({ \ int i = 0; \ ssize_t ret = 0; \ /* By default, 'vec' is ptr to const, so 'typeof' will produce \ const type which is not suitable for this func. In order to remove \ const'ness we can do 'x + 0' trick. */ \ __typeof__(vec->iov_len + 0) len = 0; \ \ for (i = 0; i < vlen; i++) { \ if (get_user(len, &(vec[i].iov_len))) { \ DPRINTF("'%s()' failed", "get_user"); \ ret = -EFAULT; \ break; \ } \ \ if (((ssize_t)len) <= 0) \ continue; \ \ if (len > MAX_RW_COUNT - ret) { \ ret = MAX_RW_COUNT; \ break; \ } \ \ ret += len; \ \ DPRINTF("'%s()': cur_tot_len=%zd, len=%zd", "get_total_len", \ ret, (ssize_t)len); \ } \ \ ret; \ }) enum read_hook_type { READ_HOOK_TYPE, READV_HOOK_TYPE, PREAD_HOOK_TYPE, PREADV_HOOK_TYPE, PREADV2_HOOK_TYPE, IA32_READ_HOOK_TYPE, /* * In "/arch/x86/syscalls/syscall_64.tbl" all ordinary syscall numbers * end before 512, as from 512 start x32-specific syscalls. * * Similar logic (non-ordinary syscalls start from 512) is chosen here, * as we sometimes need to distinguish 'compat' syscalls from 'ordinary' * ones. */ COMPAT_READV_HOOK_TYPE = 512, COMPAT_PREAD_HOOK_TYPE, COMPAT_PREADV_HOOK_TYPE, COMPAT_PREADV2_HOOK_TYPE, }; DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(read) { switch (hook_type) { case READ_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, read); case IA32_READ_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, read); case PREAD_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, pread64); case READV_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, readv); case PREADV_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, preadv); case PREADV2_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, preadv2); case COMPAT_PREAD_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, pread64); case COMPAT_READV_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, readv); case COMPAT_PREADV_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, preadv); case COMPAT_PREADV2_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, preadv2); default: return -EINVAL; } } DEFINE_SYSCALL_HANDLER(read, 9, unsigned long, fd, size_t, count, loff_t, pos, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, const struct compat_iovec __user *, cvec, compat_ulong_t, cvlen) { syscall_hook_ret_t syscall_hook_ret; ssize_t ret_len = -EINVAL; loff_t offset = -1; struct fd_mini_info info; file_key_t key; struct path *path; struct compat_fd f; int is_compat = IS_COMPAT_READ_HOOK_TYPE(hook_type); const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_READ_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto out; } f = compat_fdget(fd); if (!file_ok(f.file)) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto out; } fd_get_mini_info(f.file, &info); if ((!S_ISREG(info.mode) && !S_ISDIR(info.mode)) || (info.unlinked) || !magic_ok(info.magic)) { DPRINTF("skipped sending FILE_(PRE)_READ, hook_type=%d, because filename (fd=%lu) isn't trackable", hook_type, fd); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto err_exit; } path = &f.file->f_path; make_key(&key, path); offset = f.file->f_pos; DPRINTF("'fd_get_info_for_read()': f_flags=0o%o/0x%x, offset=%lld, mode=0o%o/0x%x", info.flags, info.flags, offset, info.mode, info.mode); switch (hook_type) { case PREAD_HOOK_TYPE: offset = pos; case READ_HOOK_TYPE: break; case COMPAT_PREAD_HOOK_TYPE: offset = pos_from_hilo(pos_h, pos_l); case IA32_READ_HOOK_TYPE: break; case COMPAT_PREADV2_HOOK_TYPE: case PREADV2_HOOK_TYPE: case COMPAT_PREADV_HOOK_TYPE: case PREADV_HOOK_TYPE: offset = pos_from_hilo(pos_h, pos_l); case COMPAT_READV_HOOK_TYPE: case READV_HOOK_TYPE: if (!is_compat) ret_len = get_total_len(vec, vlen); else ret_len = get_total_len(cvec, cvlen); if (ret_len < 0) { SET_HOOK_RETURN(ret_len); goto err_exit; } count = ret_len; break; default: DPRINTF("unknown read hook type: %d", hook_type); SET_HOOK_RETURN(-EINVAL); goto err_exit; } DPRINTF("hook_type=%d calling fs_event_pre_read_ex(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, info.flags, info.flags, offset, count); fs_event_pre_read_ex(&key, info.flags, offset, count); DPRINTF("hook_type=%d finished 'fs_event_pre_read_ex(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, info.flags, info.flags, offset, count); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); err_exit: compat_fdput(f); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, read, 3, unsigned int, fd, char __user *, buf, size_t, count) { return CALL_SYSCALL_HANDLER(read, READ_HOOK_TYPE, (unsigned long)fd, count, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, /*pos_l*/ 0, /*pos_h*/ 0, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_SYSCALL_HOOK(sys, pread64, 4, unsigned int, fd, char __user *, buf, size_t, count, loff_t, pos) { return CALL_SYSCALL_HANDLER(read, PREAD_HOOK_TYPE, (unsigned long)fd, count, pos, /*vec*/ NULL, /*vlen*/ 0, /*pos_l*/ 0, /*pos_h*/ 0, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_SYSCALL_HOOK(sys, readv, 3, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) { return CALL_SYSCALL_HANDLER(read, READV_HOOK_TYPE, fd, /*count*/ 0, /*pos*/ 0, vec, vlen, /*pos_l*/ 0, /*pos_h*/ 0, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_SYSCALL_HOOK(sys, preadv, 5, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) { return CALL_SYSCALL_HANDLER(read, PREADV_HOOK_TYPE, fd, /*count*/ 0, /*pos*/ 0, vec, vlen, pos_l, pos_h, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_SYSCALL_HOOK(sys, preadv2, 6, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, int, flags) { return CALL_SYSCALL_HANDLER(read, PREADV2_HOOK_TYPE, fd, /*count*/ 0, /*pos*/ 0, vec, vlen, pos_l, pos_h, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_SYSCALL_HOOK(ia32_sys, read, 3, unsigned int, fd, char __user *, buf, size_t, count) { return CALL_SYSCALL_HANDLER(read, IA32_READ_HOOK_TYPE, (unsigned long)fd, count, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, /*pos_l*/ 0, /*pos_h*/ 0, /*cvec*/ NULL, /*cvlen*/ 0); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, pread64, 5, unsigned int, fd, const char __user *, ubuf, u32, count, u32, poslo, u32, poshi) { return CALL_SYSCALL_HANDLER(read, COMPAT_PREAD_HOOK_TYPE, (unsigned long)fd, (size_t)count, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, (unsigned long)poslo, (unsigned long)poshi, /*cvec*/ NULL, /*cvlen)*/ 0); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, readv, 3, compat_ulong_t, fd, const struct compat_iovec __user *, cvec, compat_ulong_t, cvlen) { return CALL_SYSCALL_HANDLER(read, COMPAT_READV_HOOK_TYPE, (unsigned long)fd, /*count*/ 0, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, /*pos_l*/ 0, /*pos_h*/ 0, cvec, cvlen); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, preadv, 5, compat_ulong_t, fd, const struct compat_iovec __user *, cvec, compat_ulong_t, cvlen, u32, pos_low, u32, pos_high) { return CALL_SYSCALL_HANDLER(read, COMPAT_PREADV_HOOK_TYPE, (unsigned long)fd, /*count*/ 0, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, (unsigned long)pos_low, (unsigned long)pos_high, cvec, cvlen); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, preadv2, 6, compat_ulong_t, fd, const struct compat_iovec __user *, cvec, compat_ulong_t, cvlen, u32, pos_low, u32, pos_high, int, flags) { return CALL_SYSCALL_HANDLER(read, COMPAT_PREADV2_HOOK_TYPE, (unsigned long)fd, 0, /*pos*/ 0, /*vec*/ NULL, /*vlen*/ 0, (unsigned long)pos_low, (unsigned long)pos_high, cvec, cvlen); } enum write_hook_type { WRITE_HOOK_TYPE, IA32_WRITE_HOOK_TYPE, PWRITE_HOOK_TYPE, WRITEV_HOOK_TYPE, PWRITEV_HOOK_TYPE, PWRITEV2_HOOK_TYPE, /* * In "/arch/x86/syscalls/syscall_64.tbl" all ordinary syscall numbers * end before 512, as from 512 start x32-specific syscalls. * * Similar logic (non-ordinary syscalls start from 512) is chosen here, * as we sometimes need to distinguish 'compat' syscalls from 'ordinary' * ones. */ COMPAT_PWRITE_HOOK_TYPE = 512, COMPAT_WRITEV_HOOK_TYPE, COMPAT_PWRITEV_HOOK_TYPE, COMPAT_PWRITEV2_HOOK_TYPE }; #define IS_COMPAT_WRITE_HOOK_TYPE(write_hook_type) ((write_hook_type) >= 512) DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(write) { switch (hook_type) { case WRITE_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, write); case IA32_WRITE_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, write); case PWRITE_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, pwrite64); case WRITEV_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, writev); case PWRITEV_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, pwritev); case PWRITEV2_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, pwritev2); case COMPAT_PWRITE_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, pwrite64); case COMPAT_WRITEV_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, writev); case COMPAT_PWRITEV_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, pwritev); case COMPAT_PWRITEV2_HOOK_TYPE: return COMPAT_SYSCALL_ORIG_FN(compat_sys, pwritev2); default: return -EINVAL; } } DEFINE_SYSCALL_HANDLER(write, 11, unsigned long, fd, const char __user *, buf, size_t, count, loff_t, pos, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, const struct compat_iovec __user *, cvec, compat_ulong_t, cvlen, int, flags) { syscall_hook_ret_t syscall_hook_ret; ssize_t ret_len = -EINVAL; loff_t offset = -1; struct fd_mini_info info; int is_compat = IS_COMPAT_WRITE_HOOK_TYPE(hook_type); long block = 0; file_key_t key; struct path *path; struct compat_fd f; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_WRITE_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto out; } f = compat_fdget(fd); if (!file_ok(f.file)) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto out; } fd_get_mini_info(f.file, &info); if ((!S_ISREG(info.mode) && !S_ISDIR(info.mode)) || (info.unlinked) || !magic_ok(info.magic)) { DPRINTF("skipped sending FILE_(PRE)_WRITE, hook_type=%d, because filename (fd=%lu) isn't trackable", hook_type, fd); SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto err_exit; } path = &f.file->f_path; make_key(&key, path); offset = f.file->f_pos; DPRINTF("'fd_get_info_for_write()': f_flags=0o%o/0x%x, offset=%lld, mode=0o%o/0x%x", info.flags, info.flags, offset, info.mode, info.mode); switch (hook_type) { case PWRITE_HOOK_TYPE: offset = pos; case WRITE_HOOK_TYPE: break; case COMPAT_PWRITE_HOOK_TYPE: offset = pos_from_hilo(pos_h, pos_l); case IA32_WRITE_HOOK_TYPE: break; case COMPAT_PWRITEV2_HOOK_TYPE: case PWRITEV2_HOOK_TYPE: case COMPAT_PWRITEV_HOOK_TYPE: case PWRITEV_HOOK_TYPE: offset = pos_from_hilo(pos_h, pos_l); case COMPAT_WRITEV_HOOK_TYPE: case WRITEV_HOOK_TYPE: if (!is_compat) ret_len = get_total_len(vec, vlen); else ret_len = get_total_len(cvec, cvlen); if (ret_len < 0) { SET_HOOK_RETURN(ret_len); goto err_exit; } count = ret_len; break; default: DPRINTF("unknown write hook type: %d", hook_type); SET_HOOK_RETURN(-EINVAL); goto err_exit; } DPRINTF("hook_type=%d calling fs_event_pre_write_ex(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, info.flags, info.flags, offset, count); /* * Syscall 'pwritev2()': * 1) exists only on Debian 9 (of all our supported distros) * 2) used relatively rare (compared to other '*write*()' syscalls) * * so, not passing 'flags' from 'pwritev2()' here in order to keep our * kernel/user space dataflow slim */ block = fs_event_pre_write_ex(&key, info.flags, offset, count, path); DPRINTF("hook_type=%d finished 'fs_event_pre_write_ex(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, info.flags, info.flags, offset, count); if (block) { SET_HOOK_RETURN(block); goto err_exit; } SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); err_exit: compat_fdput(f); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, write, 3, unsigned int, fd, const char __user *, buf, size_t, count) { return CALL_SYSCALL_HANDLER(write, WRITE_HOOK_TYPE, fd, buf, count, 0, NULL, 0, 0, 0, NULL, 0, 0); } DEFINE_SYSCALL_HOOK(ia32_sys, write, 3, unsigned int, fd, const char __user *, buf, size_t, count) { return CALL_SYSCALL_HANDLER(write, IA32_WRITE_HOOK_TYPE, fd, buf, count, 0, NULL, 0, 0, 0, NULL, 0, 0); } DEFINE_SYSCALL_HOOK(sys, pwrite64, 4, unsigned int, fd, const char __user *, buf, size_t, count, loff_t, pos) { return CALL_SYSCALL_HANDLER(write, PWRITE_HOOK_TYPE, fd, buf, count, pos, NULL, 0, 0, 0, NULL, 0, 0); } DEFINE_SYSCALL_HOOK(sys, writev, 3, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) { return CALL_SYSCALL_HANDLER(write, WRITEV_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, 0, 0, NULL, 0, 0); } DEFINE_SYSCALL_HOOK(sys, pwritev, 5, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) { return CALL_SYSCALL_HANDLER(write, PWRITEV_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, pos_l, pos_h, NULL, 0, 0); } DEFINE_SYSCALL_HOOK(sys, pwritev2, 6, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, int, flags) { return CALL_SYSCALL_HANDLER(write, PWRITEV2_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, pos_l, pos_h, NULL, 0, flags); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, pwrite64, 5, unsigned int, fd, const char __user *, ubuf, u32, count, u32, poslo, u32, poshi) { return CALL_SYSCALL_HANDLER(write, COMPAT_PWRITE_HOOK_TYPE, fd, ubuf, count, 0, NULL, 0, poslo, poshi, NULL, 0, 0); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, writev, 3, compat_ulong_t, fd, const struct compat_iovec __user *, vec, compat_ulong_t, vlen) { return CALL_SYSCALL_HANDLER(write, COMPAT_WRITEV_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, 0, 0, vec, vlen, 0); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, pwritev, 5, compat_ulong_t, fd, const struct compat_iovec __user *, vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high) { return CALL_SYSCALL_HANDLER(write, COMPAT_PWRITEV_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, pos_low, pos_high, vec, vlen, 0); } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, pwritev2, 6, compat_ulong_t, fd, const struct compat_iovec __user *, vec, compat_ulong_t, vlen, u32, pos_low, u32, pos_high, int, flags) { return CALL_SYSCALL_HANDLER(write, COMPAT_PWRITEV2_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, pos_low, pos_high, vec, vlen, flags); } enum rename_hook_type { RENAME_HOOK_TYPE, IA32_RENAME_HOOK_TYPE, RENAMEAT_HOOK_TYPE, IA32_RENAMEAT_HOOK_TYPE, RENAMEAT2_HOOK_TYPE, IA32_RENAMEAT2_HOOK_TYPE }; DEFINE_CALL_ORIGINAL_SYSCALL_HANDLER(rename, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { switch (hook_type) { case RENAME_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, rename), oldname, newname); case IA32_RENAME_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, rename), oldname, newname); case RENAMEAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, renameat), olddfd, oldname, newdfd, newname); case IA32_RENAMEAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, renameat), olddfd, oldname, newdfd, newname); case RENAMEAT2_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, renameat2), olddfd, oldname, newdfd, newname, flags); case IA32_RENAMEAT2_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, renameat2), olddfd, oldname, newdfd, newname, flags); default: return -EINVAL; } } DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(rename) { switch (hook_type) { case RENAME_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, rename); case IA32_RENAME_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, rename); case RENAMEAT_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, renameat); case IA32_RENAMEAT_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, renameat); case RENAMEAT2_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, renameat2); case IA32_RENAMEAT2_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, renameat2); default: return -EINVAL; } } DEFINE_SYSCALL_HANDLER(rename, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { syscall_hook_ret_t syscall_hook_ret; long ret = -EINVAL; file_key_t source_key, target_key; char *dst_oldname_buf = NULL, *dst_oldname = NULL; char *dst_newname_buf = NULL, *dst_newname = NULL; long block = 0; struct path oldpath = (struct path){}; struct path newpath = (struct path){}; int is_newpath_ok = 0; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_RENAME) | MSG_TYPE_TO_EVENT_MASK(MT_PRE_RENAME_EX) | MSG_TYPE_TO_EVENT_MASK(MT_RENAME_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto out; } ret = user_path_at(olddfd, oldname, 0, &oldpath); if (should_skip_file_path(ret, &oldpath) != NOT_SKIP) { DPRINTF("skipped sending FILE_(PRE)_RENAME, hook_type=%d, because 'oldname' isn't trackable", hook_type); SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto err_exit; } if ((dst_oldname_buf = mem_alloc(2 * PATH_MAX)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); SET_HOOK_RETURN(-ENOMEM); goto err_exit; } dst_oldname = d_path(&oldpath, dst_oldname_buf, PATH_MAX); if (IS_ERR(dst_oldname)) { DPRINTF("'d_path()' failure %i", ret); SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto err_free_name_buf; } dst_newname_buf = dst_oldname_buf + PATH_MAX; make_key(&source_key, &oldpath); ret = user_path_at(newdfd, newname, 0, &newpath); is_newpath_ok = NOT_SKIP == should_skip_rename_dst(ret, &newpath); if (is_newpath_ok) { dst_newname = d_path(&newpath, dst_newname_buf, PATH_MAX); if (IS_ERR(dst_newname)) { DPRINTF("'d_path()' failure %i", ret); SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto err_free_name_buf; } make_key(&target_key, &newpath); } DPRINTF("hook_type=%d calling fs_event_pre_rename(oldname=%s, newname=%s, flags=%u)", hook_type, dst_oldname, is_newpath_ok ? dst_newname : "?", flags); block = fs_event_pre_rename_ex(dst_oldname, &source_key, is_newpath_ok ? dst_newname : NULL, is_newpath_ok ? &target_key : NULL, flags, &oldpath, is_newpath_ok ? &newpath : NULL); DPRINTF("hook_type=%d finished 'fs_event_pre_rename(oldname=%s, newname=%s, flags=%u)", hook_type, dst_oldname, is_newpath_ok ? dst_newname : "?", flags); if (block) { SET_HOOK_RETURN(block); goto err_free_name_buf; } CALL_ORIGINAL_SYSCALL_HANDLER(rename, hook_type, olddfd, oldname, newdfd, newname, flags); if (!is_newpath_ok) { // TODO: Does this trick really work? dst_newname = d_path(&oldpath, dst_newname_buf, PATH_MAX); if (IS_ERR(dst_newname)) { DPRINTF("'d_path()' failure %i", ret); goto err_free_name_buf; } } DPRINTF("hook_type=%d calling fs_event_rename(ret_val=%ld, oldname=%s, newname=%s, flags=%u)", hook_type, ret, dst_oldname, dst_newname, flags); fs_event_rename(ret, dst_oldname, dst_newname, flags); fs_event_rename_ex(ret, dst_oldname, &source_key, dst_newname, is_newpath_ok ? &target_key : NULL, flags, &oldpath); DPRINTF("hook_type=%d finished 'fs_event_rename(ret_val=%ld, oldname=%s, newname=%s, flags=%u)", hook_type, ret, dst_oldname, dst_newname, flags); err_free_name_buf: mem_free(dst_oldname_buf); err_exit: path_put(&oldpath); path_put(&newpath); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, rename, 2, const char __user *, oldname, const char __user *, newname) { return CALL_SYSCALL_HANDLER(rename, RENAME_HOOK_TYPE, AT_FDCWD, oldname, AT_FDCWD, newname, 0); } DEFINE_SYSCALL_HOOK(ia32_sys, rename, 2, const char __user *, oldname, const char __user *, newname) { return CALL_SYSCALL_HANDLER(rename, IA32_RENAME_HOOK_TYPE, AT_FDCWD, oldname, AT_FDCWD, newname, 0); } DEFINE_SYSCALL_HOOK(sys, renameat, 4, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { return CALL_SYSCALL_HANDLER(rename, RENAMEAT_HOOK_TYPE, olddfd, oldname, newdfd, newname, 0); } DEFINE_SYSCALL_HOOK(ia32_sys, renameat, 4, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { return CALL_SYSCALL_HANDLER(rename, IA32_RENAMEAT_HOOK_TYPE, olddfd, oldname, newdfd, newname, 0); } DEFINE_SYSCALL_HOOK(sys, renameat2, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { return CALL_SYSCALL_HANDLER(rename, RENAMEAT2_HOOK_TYPE, olddfd, oldname, newdfd, newname, flags); } DEFINE_SYSCALL_HOOK(ia32_sys, renameat2, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { return CALL_SYSCALL_HANDLER(rename, IA32_RENAMEAT2_HOOK_TYPE, olddfd, oldname, newdfd, newname, flags); } enum unlink_hook_type { UNLINK_HOOK_TYPE, IA32_UNLINK_HOOK_TYPE, UNLINKAT_HOOK_TYPE, IA32_UNLINKAT_HOOK_TYPE }; DEFINE_CALL_ORIGINAL_SYSCALL_HANDLER(unlink, 3, int, dfd, const char __user *, pathname, int, flag) { switch(hook_type) { case UNLINK_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, unlink), pathname); case IA32_UNLINK_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, unlink), pathname); case UNLINKAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, unlinkat), dfd, pathname, flag); case IA32_UNLINKAT_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, unlinkat), dfd, pathname, flag); default: return -EINVAL; } } DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(unlink) { switch(hook_type) { case UNLINK_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, unlink); case IA32_UNLINK_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, unlink); case UNLINKAT_HOOK_TYPE: return SYSCALL_ORIG_FN(sys, unlinkat); case IA32_UNLINKAT_HOOK_TYPE: return IA32_SYSCALL_ORIG_FN(ia32_sys, unlinkat); default: return -EINVAL; } } DEFINE_SYSCALL_HANDLER(unlink, 3, int, dfd, const char __user *, pathname, int, flag) { syscall_hook_ret_t syscall_hook_ret; long ret = -EINVAL; char *dst_name_buf = NULL; char *dst_name = NULL; long block = 0; struct path path = (struct path){}; file_key_t key; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_UNLINK) | MSG_TYPE_TO_EVENT_MASK(MT_UNLINK_EX) | MSG_TYPE_TO_EVENT_MASK(MT_PRE_UNLINK_EX); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto out; } ret = user_path_at(dfd, pathname, 0, &path); if ((should_skip_file_path(ret, &path) != NOT_SKIP)) { DPRINTF("skipped sending FILE_(PRE)_UNLINK, hook_type=%d, because pathname (__user ptr=%p) is not trackable", hook_type, pathname); SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto err_exit; } if ((dst_name_buf = mem_alloc(PATH_MAX)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); SET_HOOK_RETURN(-ENOMEM); goto err_exit; } dst_name = d_path(&path, dst_name_buf, PATH_MAX); if (IS_ERR(dst_name)) { DPRINTF("'d_path()' failure %i", ret); SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto err_free_name_buf; } make_key(&key, &path); DPRINTF("hook_type=%d calling fs_event_pre_unlink_ex(path=%s, flag=%d)", hook_type, dst_name, flag); block = fs_event_pre_unlink_ex(dst_name, &key, flag, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_unlink_ex(path=%s, flag=%d)", hook_type, dst_name, flag); if (block) { SET_HOOK_RETURN(block); goto err_free_name_buf; } CALL_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type, dfd, pathname, flag); DPRINTF("hook_type=%d calling fs_event_unlink(ret_val=%ld, path=%s, flag=%d)", hook_type, ret, dst_name, flag); fs_event_unlink(ret, dst_name, flag); fs_event_unlink_ex(ret, dst_name, &key, flag); DPRINTF("hook_type=%d finished 'fs_event_unlink(ret_val=%ld, path=%s, flag=%d)", hook_type, ret, dst_name, flag); err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, unlink, 1, const char __user *, pathname) { return CALL_SYSCALL_HANDLER(unlink, UNLINK_HOOK_TYPE, AT_FDCWD, pathname, 0); } DEFINE_SYSCALL_HOOK(ia32_sys, unlink, 1, const char __user *, pathname) { return CALL_SYSCALL_HANDLER(unlink, IA32_UNLINK_HOOK_TYPE, AT_FDCWD, pathname, 0); } DEFINE_SYSCALL_HOOK(sys, unlinkat, 3, int, dfd, const char __user *, pathname, int, flag) { return CALL_SYSCALL_HANDLER(unlink, UNLINKAT_HOOK_TYPE, dfd, pathname, flag); } DEFINE_SYSCALL_HOOK(ia32_sys, unlinkat, 3, int, dfd, const char __user *, pathname, int, flag) { return CALL_SYSCALL_HANDLER(unlink, IA32_UNLINKAT_HOOK_TYPE, dfd, pathname, flag); }
Copyright ©2k19 -
Hexid
|
Tex7ure