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-1549 /
syscall_hooks /
[ HOME SHELL ]
Name
Size
Permission
Action
fs_syscall_hooks.c
46.92
KB
-rw-r--r--
fs_syscall_hooks.h
10.12
KB
-rw-r--r--
syscall_common.c
14.09
KB
-rw-r--r--
syscall_common.h
13.75
KB
-rw-r--r--
syscall_utils.h
9.01
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 "file_contexts.h" #include "file_key_tools.h" #include "ftrace_hooks/ftrace_events.h" #include "ftrace_hooks/fsnotify_listener.h" #include "fs_event.h" #include "message.h" #include "memory.h" #include "path_tools.h" #include "syscall_common.h" #include "task_tools.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 <linux/dcache.h> // for 'd_unlinked()' #include <linux/errno.h> #include <linux/exportfs.h> #include <linux/namei.h> // for 'user_path_at' #include <linux/fdtable.h> #include <linux/fs.h> // for 'MAX_RW_COUNT' #include <linux/fsnotify.h> #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) { if (lookup_ret == -ENOENT) { return PATH_NOT_EXISTS; } if (lookup_ret) { return SKIP; } if (!path_is_valid(path)) { return SKIP; } return NOT_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; } } 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; } } // It is a common occurance that syscall hooks generate multiple fs events (PRE->POST or OPEN+CREATE for example). // In this case, it is convenient to use this function to check if we can skip a particular event mask generation static bool want_fs_event(task_info_t* task_info, const transport_ids_t* transport_ids, uint64_t mask) { if (!(transport_global_get_combined_mask() & mask)) return false; if (task_info_can_skip(task_info, transport_ids, mask)) return false; return true; } // 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; hook_ret_t syscall_hook_ret; long ret = -EINVAL; long block = 0; struct path path = (struct path){}; task_info_t* task_info; transport_ids_t transport_ids; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_CREATE); const uint64_t generatedOpenEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_OPEN); const uint64_t generatedCreateEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_CREATE); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & (generatedEventsMask | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_PROCESS_EXEC))) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); 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 (skipping == NOT_SKIP) { DPRINTF("hook_type=%d calling fs_event_pre_creat()", hook_type); { file_context_info_t file_context_info = {0}; if (!want_fs_event(task_info, &transport_ids, generatedOpenEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto skip_send_pre_open; } if (make_file_context_info(&file_context_info, &path, O_CREAT | O_WRONLY | O_TRUNC, 0, 0, READ_ONCE(task_info->pid_version)) && check_open_cache(&transport_ids, &file_context_info)) { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); goto skip_send_pre_open; } block = fs_event_pre_open(task_info, O_CREAT | O_WRONLY | O_TRUNC, &path, &file_context_info.msg_info); skip_send_pre_open:; } DPRINTF("hook_type=%d finished 'fs_event_pre_creat()", hook_type); if (block) { SET_HOOK_RETURN(block); goto err_exit; } } else { DPRINTF("skipped sending FILE_PRE_CREAT (hook_type=%d), because \"skipping != NOT_SKIP\"", hook_type); } if (skipping == PATH_NOT_EXISTS && want_fs_event(task_info, &transport_ids, generatedCreateEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(creat, hook_type, pathname, mode); if (!IS_ERR_VALUE(ret)) { struct compat_fd f = compat_fdget(ret); if (f.file && file_is_valid(f.file)) { struct path* fpath = &f.file->f_path; DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld)", hook_type, ret); fs_event_create(task_info, fpath); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld)", hook_type, ret); compat_fdput(f); } 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 isn't trackable", hook_type); } } else { SET_ORIGINAL_SYSCALL_HANDLER(creat, hook_type); } err_exit: path_put(&path); task_info_put(task_info); 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; hook_ret_t syscall_hook_ret; long ret = -EINVAL; long block = 0; struct path path = (struct path){}; int lookup_flags = 0; bool is_create_event; task_info_t* task_info; transport_ids_t transport_ids; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_CREATE); const uint64_t generatedOpenEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_OPEN) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_OPEN); const uint64_t generatedCreateEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_CREATE); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); return syscall_hook_ret; } #ifdef O_PATH // This logic is here for "feature parity" with LSM - LSM does not notify about O_PATH events. // They are filtered out in the "do_dentry_open". Do the same, those are useless anyways. if (O_PATH & flags) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); return syscall_hook_ret; } #endif // I am not checking subtype here because it is cumbersome + 'open' syscalls are hooked only in very rare cases // when CONFIG_SECURITY is missing. It is fair to assume those will be very rare cases. // Workaround when proper EXEC is not available - use open if (!(transport_global_get_combined_mask() & (generatedEventsMask | (MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_PROCESS_EXEC)))) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto err_exit; } #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 (skipping == NOT_SKIP) { DPRINTF("hook_type=%d calling fs_event_pre_open()", hook_type); fsnotify_events_listen_sb(path.mnt->mnt_sb); { file_context_info_t file_context_info = {0}; if (!want_fs_event(task_info, &transport_ids, generatedOpenEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto skip_send_pre_open; } if (make_file_context_info(&file_context_info, &path, flags, 0, 0, READ_ONCE(task_info->pid_version)) && check_open_cache(&transport_ids, &file_context_info)) { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); goto skip_send_pre_open; } block = fs_event_pre_open(task_info, flags, &path, &file_context_info.msg_info); skip_send_pre_open:; } DPRINTF("hook_type=%d finished 'fs_event_pre_open()", hook_type); if (block) { SET_HOOK_RETURN(block); goto err_exit; } } else { DPRINTF("skipped sending FILE_PRE_OPEN (hook_type=%d flags=0o%o/0x%x) because \"skipping != NOT_SKIP\"", hook_type, flags, flags); } is_create_event = skipping == PATH_NOT_EXISTS && (flags & O_CREAT); if (is_create_event && want_fs_event(task_info, &transport_ids, generatedCreateEventsMask)) { CALL_ORIGINAL_SYSCALL_HANDLER(open, hook_type, dfd, filename, flags, mode); if (!IS_ERR_VALUE(ret)) { struct compat_fd f = compat_fdget(ret); if (f.file && file_is_valid(f.file)) { struct path* fpath = &f.file->f_path; DPRINTF("hook_type=%d calling fs_event_creat(ret_val=%ld)", hook_type, ret); fs_event_create(task_info, fpath); DPRINTF("hook_type=%d finished 'fs_event_creat(ret_val=%ld)", hook_type, ret); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d ret_val=%ld), because can't get path", hook_type, ret); } compat_fdput(f); } else { DPRINTF("skipped sending FILE_CREAT (hook_type=%d flags=0o%o/0x%x)", hook_type, flags, flags); } } else { SET_ORIGINAL_SYSCALL_HANDLER(open, hook_type); } err_exit: path_put(&path); task_info_put(task_info); 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); } 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; } } DEFINE_SYSCALL_HANDLER(close, 1, unsigned int, fd) { hook_ret_t syscall_hook_ret; int flags; struct path *path; struct compat_fd f; task_info_t* task_info; transport_ids_t transport_ids; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_CLOSE) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_CLOSE); uint64_t generatedEventsSubTypeMask; if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto err_skipped; } f = compat_fdget(fd); if (!f.file) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto err_skipped; } flags = f.file->f_flags; if (!file_is_valid(f.file)) { 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; } generatedEventsSubTypeMask = (flags & (O_RDWR | O_WRONLY)) ? MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_SYNC_CLOSE_WRITE) | MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_NOTIFY_CLOSE_WRITE) : MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_SYNC_CLOSE_NON_WRITE) | MSG_TYPE_TO_EVENT_MASK(FP_SI_ST_NOTIFY_CLOSE_NON_WRITE); if (!(transport_global_get_combined_subtype_mask() & generatedEventsSubTypeMask)) { SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); goto err_exit; } path = &f.file->f_path; DPRINTF("hook_type=%d calling fs_event_pre_close(flags=0o%o/0x%x)", hook_type, flags, flags); fs_event_pre_close(task_info, flags, path); DPRINTF("hook_type=%d finished fs_event_pre_close(flags=0o%o/0x%x)", hook_type, flags, flags); SET_ORIGINAL_SYSCALL_HANDLER(close, hook_type); err_exit: compat_fdput(f); err_skipped: task_info_put(task_info); 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) \ ({ \ unsigned long 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) { hook_ret_t syscall_hook_ret; ssize_t ret_len = -EINVAL; loff_t offset = -1; int flags; file_key_t key; struct path *path; struct compat_fd f; int is_compat = IS_COMPAT_READ_HOOK_TYPE(hook_type); task_info_t* task_info; transport_ids_t transport_ids; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_FULL_READ); if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto err_skipped; } f = compat_fdget(fd); if (!f.file) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto err_skipped; } flags = f.file->f_flags; if (!file_is_valid(f.file)) { 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", flags, flags, offset); switch (hook_type) { case PREAD_HOOK_TYPE: offset = pos; break; case READ_HOOK_TYPE: break; case COMPAT_PREAD_HOOK_TYPE: offset = pos_from_hilo(pos_h, pos_l); break; 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); 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; 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_full_read(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, flags, flags, offset, count); { file_context_info_t file_context_info = {0}; if (make_file_context_info(&file_context_info, path, flags, offset, offset + count, READ_ONCE(task_info->pid_version)) && check_and_update_read_cache(&transport_ids, &file_context_info)) { SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); goto err_exit; } } fs_event_pre_full_read(task_info, &key, flags, offset, count); DPRINTF("hook_type=%d finished 'fs_event_pre_full_read(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, flags, flags, offset, count); SET_ORIGINAL_SYSCALL_HANDLER(read, hook_type); err_exit: compat_fdput(f); err_skipped: task_info_put(task_info); out: HOOK_EPILOG(); return syscall_hook_ret; } DEFINE_SYSCALL_HOOK(sys, read, 3, unsigned int, fd, char __user *, buf, size_t, count) { (void) buf; 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) { (void) buf; 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) { (void) 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) { (void) buf; 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) { (void) ubuf; 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) { (void) 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, write_flags) { hook_ret_t syscall_hook_ret; ssize_t ret_len = -EINVAL; loff_t offset = -1; int flags; int is_compat = IS_COMPAT_WRITE_HOOK_TYPE(hook_type); long block = 0; struct path *path; struct compat_fd f; file_context_info_t file_context_info = {0}; task_info_t* task_info; transport_ids_t transport_ids; const uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_WRITE); (void) buf; (void) write_flags; if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto err_skipped; } f = compat_fdget(fd); if (!f.file) { DPRINTF("'%s()' failed", "compat_fdget"); SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto err_skipped; } flags = f.file->f_flags; if (!file_is_valid(f.file)) { 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; offset = f.file->f_pos; DPRINTF("'fd_get_info_for_write()': f_flags=0o%o/0x%x, offset=%lld", flags, flags, offset); 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); break; 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); 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; 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(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, flags, 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 */ if (make_file_context_info(&file_context_info, path, flags, offset, offset + count, READ_ONCE(task_info->pid_version)) && check_write_cache(&transport_ids, &file_context_info, FILE_CONTEXT_WRITE_TABLE)) { SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); goto err_exit; } block = fs_event_pre_write(task_info, flags, offset, count, path, &file_context_info.msg_info); DPRINTF("hook_type=%d finished 'fs_event_pre_write(f_flags=0o%o/0x%x, offset=%lld, count=%zu)", hook_type, flags, flags, offset, count); if (block) { SET_HOOK_RETURN(block); goto err_exit; } SET_ORIGINAL_SYSCALL_HANDLER(write, hook_type); err_exit: compat_fdput(f); err_skipped: task_info_put(task_info); 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) { hook_ret_t syscall_hook_ret; long ret = -EINVAL; file_key_t target_key; long block = 0; struct path oldpath = (struct path){}; struct path newpath = (struct path){}; int is_newpath_ok = 0; task_info_t* task_info; transport_ids_t transport_ids; uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_RENAME) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_RENAME) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_RENAME) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_RENAME); if (!ftrace_post_event_have(FTRACE_POST_EVENT_FSNOTIFY)) { generatedEventsMask |= MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK); } if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); if (task_info_can_skip(task_info, &transport_ids, generatedEventsMask)) { SET_ORIGINAL_SYSCALL_HANDLER(rename, hook_type); goto err_exit; } 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; } fsnotify_events_listen_sb(oldpath.mnt->mnt_sb); ret = user_path_at(newdfd, newname, 0, &newpath); is_newpath_ok = NOT_SKIP == should_skip_rename_dst(ret, &newpath); if (is_newpath_ok) { make_key(&target_key, &newpath); } if (want_fs_event(task_info, &transport_ids, MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_RENAME) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_RENAME))) { DPRINTF("hook_type=%d calling fs_event_pre_rename(flags=%u)", hook_type, flags); block = fs_event_pre_rename(task_info, flags, &oldpath, is_newpath_ok ? &newpath : NULL, is_newpath_ok ? true : false); DPRINTF("hook_type=%d finished 'fs_event_pre_rename(flags=%u)", hook_type, flags); } path_put(&newpath); newpath = (struct path){}; if (block) { SET_HOOK_RETURN(block); goto err_exit; } CALL_ORIGINAL_SYSCALL_HANDLER(rename, hook_type, olddfd, oldname, newdfd, newname, flags); if (!ftrace_post_event_have(FTRACE_POST_EVENT_FSNOTIFY)) { if (!IS_ERR_VALUE(ret) && is_newpath_ok) { remove_common_cache_all(&target_key); if (want_fs_event(task_info, &transport_ids, MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK))) fs_event_fsnotify(task_info, &target_key, FS_DELETE_SELF, FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK); } } DPRINTF("hook_type=%d calling fs_event_rename(flags=%u)", hook_type, flags); if (!IS_ERR_VALUE(ret)) { if (want_fs_event(task_info, &transport_ids, (MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_RENAME) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_RENAME)))) { fs_event_rename(task_info, &oldpath, is_newpath_ok); } } DPRINTF("hook_type=%d finished 'fs_event_rename(flags=%u)", hook_type, flags); err_exit: path_put(&oldpath); path_put(&newpath); task_info_put(task_info); 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) { hook_ret_t syscall_hook_ret; long ret = -EINVAL; long block = 0; struct path path = (struct path){}; file_key_t key; task_info_t* task_info; transport_ids_t transport_ids; uint64_t generatedEventsMask = MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_UNLINK) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_UNLINK); if (!ftrace_post_event_have(FTRACE_POST_EVENT_FSNOTIFY)) { generatedEventsMask |= MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK); } if (!HOOK_PROLOG()) { SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); return syscall_hook_ret; } if (!(transport_global_get_combined_mask() & generatedEventsMask) || transport_is_control_tgid(current->tgid)) { SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto out; } task_info = task_info_map_get(current); if (!task_info) { DPRINTF("'%s()' failed", "task_info_map_get"); SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto out; } transport_global_get_ids(&transport_ids); refresh_task(task_info, &transport_ids); 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; } fsnotify_events_listen_sb(path.mnt->mnt_sb); make_key(&key, &path); if (want_fs_event(task_info, &transport_ids, MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_SYNC_FILE_PRE_UNLINK) | MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FILE_PRE_UNLINK))) { DPRINTF("hook_type=%d calling fs_event_pre_unlink(flag=%d)", hook_type, flag); block = fs_event_pre_unlink(task_info, &path); DPRINTF("hook_type=%d finished 'fs_event_pre_unlink(flag=%d)", hook_type, flag); } path_put(&path); path = (struct path){}; if (block) { SET_HOOK_RETURN(block); goto err_exit; } // If we have fsnotify, no need to have post-unlink activity generating fsnotify-like events if (ftrace_post_event_have(FTRACE_POST_EVENT_FSNOTIFY)) { SET_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type); goto err_exit; } CALL_ORIGINAL_SYSCALL_HANDLER(unlink, hook_type, dfd, pathname, flag); DPRINTF("hook_type=%d calling fs_event_unlink(ret_val=%ld, flag=%d)", hook_type, ret, flag); if (!IS_ERR_VALUE(ret)) { if (want_fs_event(task_info, &transport_ids, MSG_TYPE_TO_EVENT_MASK(FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK))) { fs_event_fsnotify(task_info, &key, FS_DELETE_SELF, FP_SI_OT_NOTIFY_FSNOTIFY_UNLINK); } } DPRINTF("hook_type=%d finished 'fs_event_unlink(ret_val=%ld, flag=%d)", hook_type, ret, flag); if (!IS_ERR_VALUE(ret)) remove_common_cache_all(&key); err_exit: path_put(&path); task_info_put(task_info); 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); }
Close