Linux server.thearyasamaj.org 4.18.0-553.56.1.el8_10.x86_64 #1 SMP Tue Jun 10 05:00:59 EDT 2025 x86_64
Apache
: 103.90.241.146 | : 216.73.216.222
Cant Read [ /etc/named.conf ]
5.6.40
ftpuser@mantra.thearyasamaj.org
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
README
+ Create Folder
+ Create File
/
usr /
src /
file_protector-1.1-1505 /
syscall_hooks /
[ HOME SHELL ]
Name
Size
Permission
Action
fs_syscall_hooks.c
41.7
KB
-rw-r--r--
fs_syscall_hooks.h
6.8
KB
-rw-r--r--
syscall_common.c
16.34
KB
-rw-r--r--
syscall_common.h
13.06
KB
-rw-r--r--
syscall_utils.h
8.16
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : 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 inline int kstat_to_skip_state(struct kstat *stat) { if ((S_ISREG(stat->mode)) || (S_ISDIR(stat->mode))) return NOT_SKIP; else return SKIP; } static int should_skip_path_at(int dfd, const char __user *name) { int ret = -EINVAL; struct kstat stat; ret = vfs_fstatat_impl(dfd, name, &stat, 0); if (ret == 0) { return kstat_to_skip_state(&stat); } if (ret == -ENOENT) return PATH_NOT_EXISTS; return SKIP; } static int should_skip_file_path(int lookup_ret, struct path *path) { int ret = -EINVAL; struct kstat stat; if (lookup_ret == -ENOENT) { return PATH_NOT_EXISTS; } if (lookup_ret) { return SKIP; } ret = vfs_getattr_compat(path, &stat); if (ret == 0) { return kstat_to_skip_state(&stat); } return SKIP; } static inline int should_skip_path(const char __user *name) { return should_skip_path_at(AT_FDCWD, name); } // WARN: on success spinlock 'files->file_lock' is being held static struct file *fd_get_file(unsigned int fd, struct files_struct *files) { struct file *file = NULL; spin_lock(&files->file_lock); #ifdef HAVE_FCHECK_FILES file = fcheck_files(files, fd); #else // fcheck_files is changed to files_lookup_fd_locked after 5.11.0 file = files_lookup_fd_locked(files, fd); #endif if (!file) { spin_unlock(&files->file_lock); return ERR_PTR(-ENOENT); } return file; } /* * Call this func only after path filtering is finished, as there is no semantic * checkings or true path traversal (i.e. "socket:[68591]" is considered by this * func as the normal relative path). * * The rule is simple: if there is "/" at the very beginning of a path, * then it is an absolute path, otherwise -- relative and func will return the * buffer filled with the path to current working directory of this thread. * * After successful return, caller should free memory via 'put_pwd()' */ static char *get_pwd_at(int dfd, char *path) { struct path pwd; struct file *file = NULL; char *pwd_buf = NULL, *pwd_ptr = NULL; size_t buf_size = PATH_MAX, path_size = 0; if (path[0] == '/') return NULL; // NULL is a valid argument for 'fs_event_...()' if (dfd == AT_FDCWD) { get_fs_pwd(current->fs, &pwd); } else { file = fd_get_file(dfd, current->files); if (IS_ERR(file)) return (char *)file; pwd = file->f_path; path_get(&pwd); spin_unlock(¤t->files->file_lock); } if ((pwd_buf = mem_alloc(buf_size)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "pwd_buf"); path_put(&pwd); return ERR_PTR(-ENOMEM); } // 'd_path()' may return error pwd_ptr = d_path(&pwd, pwd_buf, buf_size); if (IS_ERR(pwd_ptr)) { path_put(&pwd); mem_free(pwd_buf); return pwd_ptr; } path_size = strlen(pwd_ptr) + 1; memmove(pwd_buf, pwd_ptr, path_size); path_put(&pwd); return pwd_buf; } static inline char *get_pwd(char *path) { return get_pwd_at(AT_FDCWD, path); } static inline void put_pwd(char *pwd) { mem_free(pwd); } 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; } \ } // 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; long ret = -EINVAL; long pathname_len = 0; char *dst_name_buf = NULL; char *pwd = ERR_PTR(-EINVAL); long block = 0; struct path path = (struct path){}; 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); if (!(transport_global_get_combined_mask() & generatedEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(creat, hook_type, pathname, mode); goto out; } ret = user_path_at(AT_FDCWD, pathname, 0, &path); skipping = should_skip_file_path(ret, &path); if ((skipping == SKIP) || ((pathname_len = strnlen_user(pathname, PATH_MAX)) > PATH_MAX)) { DPRINTF("skipped sending FILE_(PRE)_CREAT, hook_type=%d, because pathname (__user ptr=%p) is %s", hook_type, pathname, (pathname_len > PATH_MAX) ? "too long" : "not trackable"); CALL_ORIGINAL_SYSCALL_HANDLER(creat, hook_type, pathname, mode); goto err_exit; } if ((dst_name_buf = mem_alloc(pathname_len)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); ret = -ENOMEM; goto err_exit; } if ((ret = strncpy_from_user(dst_name_buf, pathname, pathname_len)) < 0) { DPRINTF("'%s()' for '%s' failed %ld", "strncpy_from_user", "dst_name_buf", ret); goto err_free_name_buf; } if (skipping == NOT_SKIP) { pwd = get_pwd(dst_name_buf); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_pre_creat(pwd=%s, path=%s)", hook_type, pwd, dst_name_buf); block = fs_event_pre_create(pwd, dst_name_buf, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_creat(pwd=%s, path=%s)", hook_type, pwd, dst_name_buf); if (block) { ret = block; goto err_put_pwd; } } else { DPRINTF("skipped sending FILE_PRE_CREAT (hook_type=%d, path=%s), because can't get 'pwd'", hook_type, dst_name_buf); } } else { DPRINTF("skipped sending FILE_PRE_CREAT (hook_type=%d, path=%s), because \"skipping != NOT_SKIP\"", hook_type, dst_name_buf); } CALL_ORIGINAL_SYSCALL_HANDLER(creat, hook_type, pathname, mode); if ((skipping == NOT_SKIP) || (should_skip_path(pathname) == NOT_SKIP)) { if (IS_ERR(pwd)) pwd = get_pwd(dst_name_buf); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld, pwd=%s, path=%s)", hook_type, ret, pwd, dst_name_buf); fs_event_create(ret, pwd, dst_name_buf); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld, pwd=%s, path=%s)", hook_type, ret, pwd, dst_name_buf); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld, path=%s), because can't get 'pwd'", hook_type, ret, dst_name_buf); } } else { DPRINTF("skipped sending FILE_CREAT, hook_type=%d, because path=%s isn't trackable", hook_type, dst_name_buf); } err_put_pwd: if (!IS_ERR(pwd)) put_pwd(pwd); err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: return ret; } DEFINE_SYSCALL_HOOK(sys, creat, 2, const char __user *, pathname, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(creat, CREAT_HOOK_TYPE, pathname, mode); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, creat, 2, const char __user *, pathname, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(creat, IA32_CREAT_HOOK_TYPE, pathname, mode); HOOK_EPILOG(); return ret; } enum open_hook_type { OPEN_HOOK_TYPE, IA32_OPEN_HOOK_TYPE, OPENAT_HOOK_TYPE, IA32_OPENAT_HOOK_TYPE }; DEFINE_GET_ORIGINAL_SYSCALL_HANDLER(open) { switch (hook_type) { \ case OPEN_HOOK_TYPE: \ return SYSCALL_ORIG_PRE(sys, open); break; \ case IA32_OPEN_HOOK_TYPE: \ return IA32_SYSCALL_ORIG_PRE(ia32_sys, open); break; \ case OPENAT_HOOK_TYPE: \ return SYSCALL_ORIG_PRE(sys, openat); break; \ case IA32_OPENAT_HOOK_TYPE: \ return IA32_SYSCALL_ORIG_PRE(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; long ret = -EINVAL; long filename_len = 0; char *dst_name_buf = NULL; char *pwd = ERR_PTR(-EINVAL); long block = 0; struct path path = (struct path){}; int lookup_flags = 0; 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); 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)) || ((filename_len = strnlen_user(filename, PATH_MAX)) > PATH_MAX)) { DPRINTF("skipped sending FILE_(PRE)_OPEN hook_type=%d because filename (__user ptr=%p) is %s", hook_type, filename, (filename_len > PATH_MAX) ? "too long" : "not trackable"); SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto err_exit; } if ((dst_name_buf = mem_alloc(filename_len)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); ret = -ENOMEM; goto err_exit; } if ((ret = strncpy_from_user(dst_name_buf, filename, filename_len)) < 0) { DPRINTF("'%s()' for '%s' failed %ld", "strncpy_from_user", "dst_name_buf", ret); goto err_free_name_buf; } if (skipping == NOT_SKIP) { pwd = get_pwd_at(dfd, dst_name_buf); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_pre_open(pwd=%s path=%s flags=0o%o/0x%x)", hook_type, pwd, dst_name_buf, flags, flags); block = fs_event_pre_open(pwd, dst_name_buf, flags, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_open(pwd=%s path=%s flags=0o%o/0x%x)", hook_type, pwd, dst_name_buf, flags, flags); if (block) { ret = block; goto err_put_pwd; } } else { DPRINTF("skipped sending FILE_PRE_OPEN (hook_type=%d path=%s flags=0o%o/0x%x) because can't get 'pwd'", hook_type, dst_name_buf, flags, flags); } } 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_buf, flags, flags); } if (skipping == PATH_NOT_EXISTS && (flags & O_CREAT)) { pwd = get_pwd_at(dfd, dst_name_buf); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld, pwd=%s, path=%s)", hook_type, ret, pwd, dst_name_buf); fs_event_create(ret, pwd, dst_name_buf); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld, pwd=%s, path=%s)", hook_type, ret, pwd, dst_name_buf); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld, path=%s), because can't get 'pwd'", hook_type, ret, dst_name_buf); } } else { DPRINTF("skipped sending FILE_PRE_CREAT (hook_type=%d path=%s flags=0o%o/0x%x)", hook_type, dst_name_buf, flags, flags); } SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); err_put_pwd: if (!IS_ERR(pwd)) put_pwd(pwd); err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: return ret; } DEFINE_SYSCALL_HOOK(sys, open, 3, const char __user *, filename, int, flags, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(open, OPEN_HOOK_TYPE, AT_FDCWD, filename, flags, mode); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, open, 3, const char __user *, filename, int, flags, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(open, IA32_OPEN_HOOK_TYPE, AT_FDCWD, filename, flags, mode); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, openat, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(open, OPENAT_HOOK_TYPE, dfd, filename, flags, mode); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, openat, 4, int, dfd, const char __user *, filename, int, flags, umode_t, mode) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(open, IA32_OPENAT_HOOK_TYPE, dfd, filename, flags, mode); HOOK_EPILOG(); return ret; } struct fd_mini_info { umode_t mode; int unlinked; int flags; unsigned long magic; }; static int fd_get_mini_info(unsigned long fd, struct fd_mini_info *info) { struct file *file = NULL; struct path path = (struct path){}; struct super_block* sb = NULL; struct files_struct *files = current->files; file = fd_get_file(fd, files); if (IS_ERR(file)) { info->mode = 0; info->unlinked = 0; info->flags = 0; info->magic = 0; return PTR_ERR(file); } info->mode = file_inode_compat(file)->i_mode; sb = file_inode_compat(file)->i_sb; info->flags = file->f_flags; info->magic = sb ? sb->s_magic : 0; path = file->f_path; path_get(&path); spin_unlock(&files->file_lock); info->unlinked = d_unlinked(path.dentry); path_put(&path); return 0; } static int fd_get_info(unsigned long fd, char *name_buf, size_t buf_size, char **filename_ptr, loff_t *offset, struct path *ppath) { int ret = 0; struct file *file = NULL; struct path path = (struct path){}; struct files_struct *files = current->files; file = fd_get_file(fd, files); if (IS_ERR(file)) return PTR_ERR(file); *offset = file->f_pos; path = file->f_path; path_get(&path); spin_unlock(&files->file_lock); *filename_ptr = d_path(&path, name_buf, buf_size); if (IS_ERR(*filename_ptr)) ret = PTR_ERR(*filename_ptr); *ppath = path; return ret; } enum close_hook_type { CLOSE_HOOK_TYPE, IA32_CLOSE_HOOK_TYPE, }; DEFINE_CALL_ORIGINAL_SYSCALL_HANDLER(close, 1, unsigned int, fd) { switch (hook_type) { case CLOSE_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, close), fd); break; case IA32_CLOSE_HOOK_TYPE: return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, close), fd); 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) { long ret = -EINVAL; char *name_buf = NULL, *filename = NULL; char *pwd = ERR_PTR(-EINVAL); loff_t offset = -1; struct fd_mini_info info; struct path path = (struct path){}; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_CLOSE); if (!(transport_global_get_combined_mask() & generatedEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(close, hook_type, fd); goto out; } if ((ret = fd_get_mini_info(fd, &info))) { DPRINTF("'%s()' failed %ld", "fd_mini_info", ret); CALL_ORIGINAL_SYSCALL_HANDLER(close, hook_type, fd); goto err_exit; } 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); CALL_ORIGINAL_SYSCALL_HANDLER(close, hook_type, fd); goto err_exit; } if ((name_buf = mem_alloc(PATH_MAX)) == NULL) { IPRINTF("'%s()' for '%s' failed", "mem_alloc", "name_buf"); ret = -ENOMEM; goto err_exit; } if ((ret = fd_get_info(fd, name_buf, PATH_MAX, &filename, &offset, &path))) { DPRINTF("'%s()' [%u] failed %ld", "fd_get_info", fd, ret); goto err_free_name_buf; } DPRINTF("'fd_get_info()': filename=%s, f_flags=0o%o/0x%x, offset=%lld", filename, info.flags, info.flags, offset); pwd = get_pwd_at(fd, filename); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_pre_close(pwd=%s path=%s flags=0o%o/0x%x)", hook_type, pwd, filename, info.flags, info.flags); fs_event_pre_close(pwd, filename, info.flags, &path); DPRINTF("hook_type=%d finished fs_event_pre_close(pwd=%s path=%s flags=0o%o/0x%x)", hook_type, pwd, filename, info.flags, info.flags); } else { DPRINTF("skipped sending FILE_PRE_CLOSE(hook_type=%d ret_val=%ld path=%s flags=0o%o/0x%x) because can't get 'pwd'", hook_type, ret, filename, info.flags, info.flags); } CALL_ORIGINAL_SYSCALL_HANDLER(close, hook_type, fd); if (!IS_ERR(pwd)) put_pwd(pwd); path_put(&path); err_free_name_buf: if (name_buf != NULL) mem_free(name_buf); err_exit: out: return ret; } DEFINE_SYSCALL_HOOK(sys, close, 1, unsigned int, fd) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(close, CLOSE_HOOK_TYPE, fd); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, close, 1, unsigned int, fd) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(close, IA32_CLOSE_HOOK_TYPE, fd); HOOK_EPILOG(); return ret; } 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) // 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; } DEFINE_CALL_ORIGINAL_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) { switch (hook_type) { \ case WRITE_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, write), fd, buf, count); case IA32_WRITE_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(IA32_SYSCALL_ORIG(ia32_sys, write), fd, buf, count); case PWRITE_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, pwrite64), fd, buf, count, pos); case WRITEV_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, writev), fd, vec, vlen); case PWRITEV_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, pwritev), fd, vec, vlen, pos_l, pos_h); case PWRITEV2_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(SYSCALL_ORIG(sys, pwritev2), fd, vec, vlen, pos_l, pos_h, flags); case COMPAT_PWRITE_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(COMPAT_SYSCALL_ORIG(compat_sys, pwrite64), fd, buf, count, pos_l, pos_h); case COMPAT_WRITEV_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(COMPAT_SYSCALL_ORIG(compat_sys, writev), (compat_ulong_t)fd, cvec, cvlen); case COMPAT_PWRITEV_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(COMPAT_SYSCALL_ORIG(compat_sys, pwritev), (compat_ulong_t)fd, cvec, cvlen, pos_l, pos_h); case COMPAT_PWRITEV2_HOOK_TYPE: \ return CALL_ORIGINAL_SYSCALL_HANDLER_PTR(COMPAT_SYSCALL_ORIG(compat_sys, pwritev2), (compat_ulong_t)fd, cvec, cvlen, pos_l, pos_h, flags); default: return -EINVAL; } \ } /* * Get total length of all I/O vectors passed to '*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; \ }) 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) { long ret = -EINVAL; ssize_t ret_len = -EINVAL; char *name_buf = NULL, *filename = NULL; loff_t offset = -1; struct fd_mini_info info; int is_compat = IS_COMPAT_WRITE_HOOK_TYPE(hook_type); long block = 0; struct path path = (struct path){}; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_FILE_PRE_WRITE) | MSG_TYPE_TO_EVENT_MASK(MT_FILE_WRITE); if (!(transport_global_get_combined_mask() & generatedEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(write, hook_type, fd, buf, count, pos, vec, vlen, pos_l, pos_h, cvec, cvlen, flags); goto out; } if ((ret = fd_get_mini_info(fd, &info))) { // TODO: maybe also fall to 'untrackable' branch or even fail as // we do in case of error in 'fd_get_info()'? DPRINTF("'%s()' failed %ld", "fd_get_mode_and_unlinked", ret); } else 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); CALL_ORIGINAL_SYSCALL_HANDLER(write, hook_type, fd, buf, count, pos, vec, vlen, pos_l, pos_h, cvec, cvlen, flags); goto err_exit; } if ((name_buf = mem_alloc(PATH_MAX)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "name_buf"); ret = -ENOMEM; goto err_exit; } /* * 'd_path()' is used inside the 'fd_get_info()', it appends '\0' beforehand, * so 'name_buf' is always NUL-terminated */ if ((ret = fd_get_info(fd, name_buf, PATH_MAX, &filename, &offset, &path))) { DPRINTF("'%s()' failed %ld", "fd_get_info", ret); goto err_free_name_buf; } DPRINTF("'fd_get_info()': filename=%s, f_flags=0o%o/0x%x, offset=%lld, mode=0o%o/0x%x", filename, 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) { ret = ret_len; goto err_free_name_buf; } count = ret_len; break; default: DPRINTF("unknown write hook type: %d", hook_type); goto err_free_name_buf; } DPRINTF("hook_type=%d calling fs_event_pre_write(filename=%s, f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, filename, 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(filename, info.flags, offset, count, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_write(filename=%s, f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, filename, info.flags, info.flags, offset, count); if (block) { ret = block; goto err_free_name_buf; } CALL_ORIGINAL_SYSCALL_HANDLER(write, hook_type, fd, buf, count, pos, vec, vlen, pos_l, pos_h, cvec, cvlen, flags); DPRINTF("hook_type=%d calling fs_event_write(ret_val=%ld, filename=%s, f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, ret, filename, 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 */ fs_event_write(ret, filename, info.flags, offset, count); DPRINTF("hook_type=%d finished 'fs_event_write(ret_val=%ld, filename=%s, f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, ret, filename, info.flags, info.flags, offset, count); err_free_name_buf: path_put(&path); mem_free(name_buf); err_exit: out: return ret; } DEFINE_SYSCALL_HOOK(sys, write, 3, unsigned int, fd, const char __user *, buf, size_t, count) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, WRITE_HOOK_TYPE, fd, buf, count, 0, NULL, 0, 0, 0, NULL, 0, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, write, 3, unsigned int, fd, const char __user *, buf, size_t, count) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, IA32_WRITE_HOOK_TYPE, fd, buf, count, 0, NULL, 0, 0, 0, NULL, 0, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, pwrite64, 4, unsigned int, fd, const char __user *, buf, size_t, count, loff_t, pos) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, PWRITE_HOOK_TYPE, fd, buf, count, pos, NULL, 0, 0, 0, NULL, 0, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, writev, 3, unsigned long, fd, const struct iovec __user *, vec, unsigned long, vlen) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, WRITEV_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, 0, 0, NULL, 0, 0); HOOK_EPILOG(); return ret; } 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) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, PWRITEV_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, pos_l, pos_h, NULL, 0, 0); HOOK_EPILOG(); return ret; } 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) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, PWRITEV2_HOOK_TYPE, fd, NULL, 0, 0, vec, vlen, pos_l, pos_h, NULL, 0, flags); HOOK_EPILOG(); return ret; } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, pwrite64, 5, unsigned int, fd, const char __user *, ubuf, u32, count, u32, poslo, u32, poshi) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, COMPAT_PWRITE_HOOK_TYPE, fd, ubuf, count, 0, NULL, 0, poslo, poshi, NULL, 0, 0); HOOK_EPILOG(); return ret; } DEFINE_COMPAT_SYSCALL_HOOK(compat_sys, writev, 3, compat_ulong_t, fd, const struct compat_iovec __user *, vec, compat_ulong_t, vlen) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, COMPAT_WRITEV_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, 0, 0, vec, vlen, 0); HOOK_EPILOG(); return ret; } 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) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, COMPAT_PWRITEV_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, pos_low, pos_high, vec, vlen, 0); HOOK_EPILOG(); return ret; } 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) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(write, COMPAT_PWRITEV2_HOOK_TYPE, fd, NULL, 0, 0, NULL, 0, pos_low, pos_high, vec, vlen, flags); HOOK_EPILOG(); return ret; } static inline char *get_pwd_newname_at(int newdfd, char *dst_newname_buf, char* pwd_oldname, int *is_pwd_oldnew_equal) { if (dst_newname_buf[0] == '/') { // 'dst_newname_buf' is absolute -- no 'pwd' is needed return NULL; } else { // 'dst_newname_buf' is relative -- we need to retrieve 'pwd' if ((pwd_oldname != NULL) && (!IS_ERR(pwd_oldname))) { /* ('dst_oldname_buf' is relative) and * ('pwd_oldname = get_pwd(dst_oldname_buf)' was successful) */ *is_pwd_oldnew_equal = 1; return pwd_oldname; } else { /* ('dst_oldname_buf' is absolute) or (relative and * 'pwd_oldname = get_pwd(dst_oldname_buf)' wasn't successful) */ return get_pwd_at(newdfd, dst_newname_buf); } } } 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_SYSCALL_HANDLER(rename, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { long ret = -EINVAL; int is_pwd_oldnew_equal = 0; long oldname_len = 0, newname_len = 0; char *dst_oldname_buf = NULL, *pwd_oldname = ERR_PTR(-EINVAL); char *dst_newname_buf = NULL, *pwd_newname = ERR_PTR(-EINVAL); long block = 0; struct path oldpath = (struct path){}; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_RENAME) | MSG_TYPE_TO_EVENT_MASK(MT_PRE_RENAME); if (!(transport_global_get_combined_mask() & generatedEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(rename, hook_type, olddfd, oldname, newdfd, newname, flags); goto out; } ret = user_path_at(olddfd, oldname, 0, &oldpath); if ((should_skip_file_path(ret, &oldpath) != NOT_SKIP) || ((oldname_len = strnlen_user(oldname, PATH_MAX)) > PATH_MAX) || ((newname_len = strnlen_user(newname, PATH_MAX)) > PATH_MAX)) { DPRINTF("skipped sending FILE_(PRE)_RENAME, hook_type=%d, because %s", hook_type, ((oldname_len > PATH_MAX) || (newname_len > PATH_MAX)) ? "'oldname' or/and 'newname' is too long" : "'oldname' isn't trackable"); CALL_ORIGINAL_SYSCALL_HANDLER(rename, hook_type, olddfd, oldname, newdfd, newname, flags); goto err_exit; } if ((dst_oldname_buf = mem_alloc(oldname_len + newname_len)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_oldname_buf"); ret = -ENOMEM; goto err_exit; } if ((ret = strncpy_from_user(dst_oldname_buf, oldname, oldname_len)) < 0) { DPRINTF("'%s()' for '%s' failed %ld", "strncpy_from_user", "dst_oldname_buf", ret); goto err_free_name_buf; } dst_newname_buf = dst_oldname_buf + oldname_len; if ((ret = strncpy_from_user(dst_newname_buf, newname, newname_len)) < 0) { DPRINTF("'%s()' for '%s' failed %ld", "strncpy_from_user", "dst_newname_buf", ret); goto err_free_name_buf; } pwd_oldname = get_pwd_at(olddfd, dst_oldname_buf); pwd_newname = get_pwd_newname_at(newdfd, dst_newname_buf, pwd_oldname, &is_pwd_oldnew_equal); if ((!IS_ERR(pwd_oldname)) && (!IS_ERR(pwd_newname))) { DPRINTF("hook_type=%d calling fs_event_pre_rename(pwd_oldname=%s, oldname=%s, pwd_newname=%s, newname=%s, flags=%u)", hook_type, pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags); block = fs_event_pre_rename(pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags, &oldpath); DPRINTF("hook_type=%d finished 'fs_event_pre_rename(pwd_oldname=%s, oldname=%s, pwd_newname=%s, newname=%s, flags=%u)", hook_type, pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags); if (block) { ret = block; goto err_put_pwd; } } else { DPRINTF("skipped sending FILE_PRE_RENAME (hook_type=%d, oldname=%s, newname=%s), because can't get%s%s", hook_type, dst_oldname_buf, dst_newname_buf, IS_ERR(pwd_oldname) ? " 'pwd_oldname'" : "", IS_ERR(pwd_newname) ? " 'pwd_newname'" : ""); } CALL_ORIGINAL_SYSCALL_HANDLER(rename, hook_type, olddfd, oldname, newdfd, newname, flags); if ((!IS_ERR(pwd_oldname)) && (!IS_ERR(pwd_newname))) { DPRINTF("hook_type=%d calling fs_event_rename(ret_val=%ld, pwd_oldname=%s, oldname=%s, pwd_newname=%s, newname=%s, flags=%u)", hook_type, ret, pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags); fs_event_rename(ret, pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags); DPRINTF("hook_type=%d finished 'fs_event_rename(ret_val=%ld, pwd_oldname=%s, oldname=%s, pwd_newname=%s, newname=%s, flags=%u)", hook_type, ret, pwd_oldname, dst_oldname_buf, pwd_newname, dst_newname_buf, flags); } else { DPRINTF("skipped sending FILE_RENAME (hook_type=%d, oldname=%s, newname=%s), because can't get 'pwd'", hook_type, dst_oldname_buf, dst_newname_buf); } err_put_pwd: if (!IS_ERR(pwd_oldname)) put_pwd(pwd_oldname); if (!is_pwd_oldnew_equal && !IS_ERR(pwd_newname)) put_pwd(pwd_newname); err_free_name_buf: mem_free(dst_oldname_buf); err_exit: path_put(&oldpath); out: return ret; } DEFINE_SYSCALL_HOOK(sys, rename, 2, const char __user *, oldname, const char __user *, newname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, RENAME_HOOK_TYPE, AT_FDCWD, oldname, AT_FDCWD, newname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, rename, 2, const char __user *, oldname, const char __user *, newname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, IA32_RENAME_HOOK_TYPE, AT_FDCWD, oldname, AT_FDCWD, newname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, renameat, 4, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, RENAMEAT_HOOK_TYPE, olddfd, oldname, newdfd, newname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, renameat, 4, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, IA32_RENAMEAT_HOOK_TYPE, olddfd, oldname, newdfd, newname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, renameat2, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, RENAMEAT2_HOOK_TYPE, olddfd, oldname, newdfd, newname, flags); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, renameat2, 5, int, olddfd, const char __user *, oldname, int, newdfd, const char __user *, newname, unsigned int, flags) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(rename, IA32_RENAMEAT2_HOOK_TYPE, olddfd, oldname, newdfd, newname, flags); HOOK_EPILOG(); return ret; } 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_SYSCALL_HANDLER(unlink, 3, int, dfd, const char __user *, pathname, int, flag) { long ret = -EINVAL; long pathname_len = 0; char *dst_name_buf = NULL; char *pwd = ERR_PTR(-EINVAL); long block = 0; struct path path = (struct path){}; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(MT_UNLINK) | MSG_TYPE_TO_EVENT_MASK(MT_PRE_UNLINK); if (!(transport_global_get_combined_mask() & generatedEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type, dfd, pathname, flag); goto out; } ret = user_path_at(dfd, pathname, 0, &path); if ((should_skip_file_path(ret, &path) != NOT_SKIP) || ((pathname_len = strnlen_user(pathname, PATH_MAX)) > PATH_MAX)) { DPRINTF("skipped sending FILE_(PRE)_UNLINK, hook_type=%d, because pathname (__user ptr=%p) is %s", hook_type, pathname, (pathname_len > PATH_MAX) ? "too long" : "not trackable"); CALL_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type, dfd, pathname, flag); goto err_exit; } if ((dst_name_buf = mem_alloc(pathname_len)) == NULL) { DPRINTF("'%s()' for '%s' failed", "mem_alloc", "dst_name_buf"); ret = -ENOMEM; goto err_exit; } if ((ret = strncpy_from_user(dst_name_buf, pathname, pathname_len)) < 0) { DPRINTF("'%s()' for '%s' failed %ld", "strncpy_from_user", "dst_name_buf", ret); goto err_free_name_buf; } pwd = get_pwd_at(dfd, dst_name_buf); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_pre_unlink(pwd=%s, path=%s, flag=%d)", hook_type, pwd, dst_name_buf, flag); block = fs_event_pre_unlink(pwd, dst_name_buf, flag, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_unlink(pwd=%s, path=%s, flag=%d)", hook_type, pwd, dst_name_buf, flag); if (block) { ret = block; goto err_put_pwd; } } else { DPRINTF("skipped sending FILE_PRE_UNLINK (hook_type=%d, path=%s), because can't get 'pwd'", hook_type, dst_name_buf); } CALL_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type, dfd, pathname, flag); if (!IS_ERR(pwd)) { DPRINTF("hook_type=%d calling fs_event_unlink(ret_val=%ld, pwd=%s, path=%s, flag=%d)", hook_type, ret, pwd, dst_name_buf, flag); fs_event_unlink(ret, pwd, dst_name_buf, flag); DPRINTF("hook_type=%d finished 'fs_event_unlink(ret_val=%ld, pwd=%s, path=%s, flag=%d)", hook_type, ret, pwd, dst_name_buf, flag); } else { DPRINTF("skipped sending FILE_UNLINK (hook_type=%d, path=%s), because can't get 'pwd'", hook_type, dst_name_buf); } err_put_pwd: if (!IS_ERR(pwd)) put_pwd(pwd); err_free_name_buf: mem_free(dst_name_buf); err_exit: path_put(&path); out: return ret; } DEFINE_SYSCALL_HOOK(sys, unlink, 1, const char __user *, pathname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(unlink, UNLINK_HOOK_TYPE, AT_FDCWD, pathname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, unlink, 1, const char __user *, pathname) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(unlink, IA32_UNLINK_HOOK_TYPE, AT_FDCWD, pathname, 0); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(sys, unlinkat, 3, int, dfd, const char __user *, pathname, int, flag) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(unlink, UNLINKAT_HOOK_TYPE, dfd, pathname, flag); HOOK_EPILOG(); return ret; } DEFINE_SYSCALL_HOOK(ia32_sys, unlinkat, 3, int, dfd, const char __user *, pathname, int, flag) { long ret = -EINVAL; HOOK_PROLOG(); ret = CALL_SYSCALL_HANDLER(unlink, IA32_UNLINKAT_HOOK_TYPE, dfd, pathname, flag); HOOK_EPILOG(); return ret; }
Close