opt
/
kaspersky
/
kav4fs
/
src
/
kernel
/
module.linux
➕ New
📤 Upload
✎ Editing:
interface_monitor.c
← Back
/* * This source file is a part of a Kaspersky Antivirus Monitor. * Copyright (C) Kaspersky Lab, 1997-2010 * See License.txt for details * */ #include "module.h" #include "watchdog.h" #include <linux/string.h> #include <linux/delay.h> /* monitor daemon functions */ static void delayed_close(struct work_struct* work); static int monitor_daemon_open(struct inode* inode, struct file* filp); static ssize_t monitor_daemon_read(struct file* file, char __user *ubuf, size_t len, loff_t* offset); static ssize_t monitor_daemon_write(struct file* file, const char __user *ubuf, size_t len, loff_t* offset); static int monitor_daemon_close(struct inode* inode, struct file* filp); static unsigned int monitor_daemon_poll(struct file* file, poll_table* wait); #ifndef USE_REDIRFS static int monitor_daemon_ioctl(struct inode* inode, struct file* filp, unsigned cmd, unsigned long addr); #else static long monitor_daemon_ioctl(struct file* filp, unsigned cmd, unsigned long addr); #endif static long handle_monitor_ioctl_old(unsigned cmd, unsigned long addr); static void Monitor_watchdog_trigger(void); /* monitor compatibility functions to work with old versions interface */ static int monitor_open_old(struct inode* inode, struct file* filp); static int monitor_close_old(struct inode* inode, struct file* filp); #ifndef USE_REDIRFS static int monitor_ioctl_old(struct inode* inode, struct file* filp, unsigned cmd, unsigned long addr); #else static long monitor_ioctl_old(struct file* filp, unsigned cmd, unsigned long addr); #endif #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)) static long monitor_compat_ioctl_old(struct file* filp, unsigned cmd, unsigned long addr); static long monitor_daemon_compat_ioctl(struct file* filp, unsigned int cmd, unsigned long addr); #endif /* Compatibulity with old product versions */ /* FIXME - old product version of registration now is turned off */ struct file_operations kavmonitor_file_operations = { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 27)) .owner = THIS_MODULE, #endif .open = monitor_open_old, #ifndef USE_REDIRFS .ioctl = monitor_ioctl_old, #else .unlocked_ioctl = monitor_ioctl_old, #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)) .compat_ioctl = monitor_compat_ioctl_old, #endif #endif .release = monitor_close_old }; /* monitor daemon interface */ struct file_operations kavdaemon_file_operations = { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 27)) .owner = THIS_MODULE, #endif .open = monitor_daemon_open, #ifndef USE_REDIRFS .ioctl = monitor_daemon_ioctl, #else .unlocked_ioctl = monitor_daemon_ioctl, #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)) .compat_ioctl = monitor_daemon_compat_ioctl, #endif #endif .release = monitor_daemon_close, .poll = monitor_daemon_poll, .read = monitor_daemon_read, .write = monitor_daemon_write }; static int monitor_module_stop(void); static void file_checked(void* ctx, const CheckFileResultInfoData* data); static void monitor_shutdown(void* ctx); static ProtocolCallbacks m_callbacks = { .check_file = NULL, .notify_file = NULL, .samba_connect = NULL, .check_file_result = file_checked, .shutdown = monitor_shutdown, .check_sign = NULL }; static ProtocolAllocator m_alloc = { .alloc = (protocol_mem_allocator)vmalloc, .free = (protocol_mem_dealloc)vfree }; atomic_t monitor_started = ATOMIC_INIT(0); /* set if avir monitor is started */ atomic_t monitor_opened = ATOMIC_INIT(0); /* set if avir monitor is started */ static TransportBuffer send_buff; static TransportBuffer recv_buff; static DECLARE_MUTEX(send_buff_mtx); static DECLARE_MUTEX(recv_buff_mtx); static struct work_struct shutdown_work; static wait_queue_head_t queue_empty_waitq; static struct workqueue_struct *kav_workqueue; int Monitor_module_init(void) { m_config.version = 1; m_config.max_fname_len = DEFAULT_MAX_FNAME_LEN; m_config.deny_too_long_fname = DEFAULT_DENY_TOO_LONG_FNAME; atomic_xchg(&monitor_started, 0); atomic_xchg(&monitor_opened, 0); init_waitqueue_head(&queue_empty_waitq); kav_workqueue = create_workqueue("kavworkqueue"); if (!kav_workqueue) { printk("klflt: failed to create kavworkqueue\n"); return -1; } ProtocolCallbacksInit(&m_callbacks); SetProtocolAllocator(&m_alloc); sema_init(&send_buff_mtx, 1); sema_init(&recv_buff_mtx, 1); Monitor_cache_init(); return 0; }; void Monitor_module_cleanup(void) { if (atomic_read(&monitor_started)) { monitor_module_stop(); } Monitor_watchdog_cleanup(); Monitor_intercept_destroy(); Monitor_cache_finit(); flush_workqueue(kav_workqueue); destroy_workqueue(kav_workqueue); return; } void Monitor_module_send_data(check_req_data_t* reqd) { check_req_queue_add(reqd); wake_up(&queue_empty_waitq); } /* to avoid "sorry, unimplemented: inlining failed in call" when building on mandriva * CS4 */ static int __TransportBufferAddPacket(TransportBuffer* buf, const PackedBuffer* packet) { u_int32_t net_len = htonl(packet->length); if (!fs_transport_buffer_check_size(buf, sizeof(u_int32_t) + packet->length)) { return 0; } fs_transport_buffer_append_raw(buf, (char*)&net_len, sizeof(u_int32_t)); fs_transport_buffer_append_raw(buf, (char*)packet->data, packet->length); return 1; } static u_int32_t __TransportBufferIsReady(const TransportBuffer* buf) { u_int32_t len; if (buf->length < sizeof(u_int32_t)) return 0; len = fs_transport_buffer_get_length(buf) + sizeof(u_int32_t); if (buf->length < len) return 0; return len; } static u_int32_t __TransportBufferGetNextPacket(TransportBuffer* buf, TransportBufferEntry* el) { u_int32_t len = __TransportBufferIsReady(buf); if (!len) return 0; el->begin = buf->start + sizeof(u_int32_t); el->end = buf->start + len; buf->start += len; buf->length -= len; return len; } /* Requirement: send_buff_mtx acquired */ static void SendBuffAddReq(check_req_data_t* reqd) { int err = 0; int request = reqd->request; char *filename = NULL, *fname_part = NULL; PackedBuffer req_buff = {.data = NULL, .length = 0}; struct dentry* dentry = NULL; if (reqd->parent) { dentry = d_alloc_name(reqd->parent, reqd->name); if (dentry) { filename = get_path_by_dentry(dentry, reqd->vfsmnt, &err, &fname_part); dput(dentry); } } else { filename = kstrdup(reqd->name, GFP_KERNEL); } if (-ENAMETOOLONG == err) { reqd->request = NOTIFY_FILE; reqd->file_op_type = FILE_NAME_TOO_LONG; filename = fname_part ? fname_part : kstrdup(reqd->name, GFP_KERNEL); } else if (0 != err) { reqd->request = 0; } switch (reqd->request) { case CHECK_FILE: if (1 == m_config.version) { err = MakeCheckFileReq(&req_buff, reqd->euid, reqd->egid, reqd->pid, reqd->queue_id, filename, reqd->file_op_type, reqd->flags, reqd->dev, reqd->ino) ? 0 : -ENOMEM; } else { err = MakeCheckFileReq2( &req_buff, m_config.version, reqd->euid, reqd->egid, reqd->pid, reqd->queue_id, filename, reqd->file_op_type, reqd->flags, reqd->dev, reqd->ino, reqd->mtime, reqd->size, reqd->owner_uid, reqd->file_mode, reqd->f_type, NULL) ? 0 : -ENOMEM; } break; case CHECK_SIGN: if (1 == m_config.version) { err = MakeCheckSignReq(&req_buff, reqd->euid, reqd->egid, reqd->queue_id, filename) ? 0 : -ENOMEM; } else { err = MakeCheckSignReq2(&req_buff, m_config.version, reqd->euid, reqd->egid, reqd->queue_id, filename) ? 0 : -ENOMEM; } break; case NOTIFY_FILE: if (1 == m_config.version) { err = MakeNotifyFileReq(&req_buff, filename, reqd->file_op_type, reqd->dev, reqd->ino) ? 0 : -ENOMEM; } else { err = MakeNotifyFileReq2(&req_buff, m_config.version, filename, reqd->file_op_type, reqd->dev, reqd->ino) ? 0 : -ENOMEM; } break; case 0: printk("klflt: SendBuffer detected request with id==0\n"); break; default: printk(KERN_ERR "klflt: unexpected reqd request(%d)\n", reqd->request); err = -EINVAL; break; } if (!err) { if (!__TransportBufferAddPacket(&send_buff, &req_buff)) err = -ENOMEM; FinitPackedBuffer(&req_buff); } if (request == CHECK_FILE || request == CHECK_SIGN) { if (reqd->request == NOTIFY_FILE && reqd->file_op_type == FILE_NAME_TOO_LONG) err = -ENAMETOOLONG; if (err) { if ((-ENAMETOOLONG == err) && !m_config.deny_too_long_fname) err = 0; Monitor_queue_set_answer(reqd->queue_id, err); Monitor_queue_wakeup(reqd->queue_id); } } if (filename) kfree(filename); return; } /* Requirement: send_buff_mtx acquired */ static void SendBuffFill(size_t count) { while (send_buff.length < count) { check_req_data_t* reqd = check_req_queue_get(); if (!reqd) break; SendBuffAddReq(reqd); check_req_queue_put(reqd); } } /* Requirement: send_buff_mtx acquired */ static int ReadyForRead(const TransportBuffer* buf) { if (buf->length) return 1; return !check_req_queue_empty(); } static void Monitor_watchdog_trigger(void) { printk(KERN_ERR "klflt: no answer received from watchdog. Aborting\n"); INIT_WORK(&shutdown_work, delayed_close); if (!queue_work(kav_workqueue, &shutdown_work)) { printk("klflt: failed to schedule watchdog trigger work\n"); } } static struct monitor_watchdog_ops watchdog_operations = { .invoke = Monitor_watchdog_trigger }; static int monitor_module_stop(void) { DEBUG_MSG("Stop monitor device pid=%d", current->pid); if (!atomic_read(&monitor_started)) { return -EALREADY; } atomic_xchg(&monitor_started, 0); Monitor_watchdog_stop(); Monitor_kernel_finit(); Monitor_queue_cleanup(); TransportBufferFinit(&send_buff); TransportBufferFinit(&recv_buff); Monitor_intercept_destroy(); return 0; } static int monitor_daemon_open(struct inode* inode, struct file* filp) { int err = 0; int retry = 20000; file_info f_info; DEBUG_MSG("Open monitor device pid=%d", current->pid); while (atomic_read(&monitor_opened) && --retry) msleep(1); if (atomic_read(&monitor_opened)) return -EACCES; Monitor_kernel_init(); DEBUG_MSG("Register monitor with pid=%d", current->pid); get_exec_info((struct task_struct __rcu *)current, &f_info); err = Monitor_kernel_register_file(&f_info); if (0 != err) return -err; Monitor_ins_use(); #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)) register_ioctl32_conversion(MONITOR_CONFIG_CMD, NULL); register_ioctl32_conversion(MONITOR_CONFIG2_CMD, NULL); register_ioctl32_conversion(MONITOR_START, NULL); register_ioctl32_conversion(MONITOR_CLEAR_CACHE_CMD, NULL); #endif if (!TransportBufferInit(&send_buff) || !TransportBufferInit(&recv_buff)) { printk(KERN_ERR "kav4fs_oas: Failed to initialize buffers"); Monitor_dec_use(); Monitor_kernel_finit(); return -EACCES; } atomic_xchg(&monitor_opened, 1); Monitor_watchdog_init(&watchdog_operations); return 0; } static ssize_t monitor_daemon_read(struct file* file, char __user *ubuf, size_t count, loff_t* offset) { int len = 0; int err = 0; // DEBUG_MSG("%s: count=%d",__FUNCTION__,count); if (down_trylock(&send_buff_mtx)) { /* somebody else has it now */ if ((file->f_flags & O_NONBLOCK) != 0) return -EAGAIN; if (down_interruptible(&send_buff_mtx)) return -ERESTARTSYS; } do { if ((file->f_flags & O_NONBLOCK) != 0) { err = -EAGAIN; if (!ReadyForRead(&send_buff)) goto out_no_read; } else { err = -ERESTARTSYS; Monitor_ins_use(); if (wait_event_interruptible(queue_empty_waitq, ReadyForRead(&send_buff))) goto out_no_read; Monitor_dec_use(); } if (!atomic_read(&monitor_started)) { count = 0; break; } SendBuffFill(count); } while (!send_buff.length); len = MIN(count, send_buff.length); err = -EFAULT; if (copy_to_user(ubuf, (char *)(send_buff.start), len)) goto out_no_read; err = 0; send_buff.start += len; send_buff.length -= len; out_no_read: up(&send_buff_mtx); return err ? err : len; } static ssize_t monitor_daemon_write(struct file* file, const char __user *ubuf, size_t count, loff_t* offset) { if (!atomic_read(&monitor_started)) return -EINVAL; if (down_trylock(&recv_buff_mtx)) { /* somebody else has it now */ if ((file->f_flags & O_NONBLOCK) != 0) return -EAGAIN; if (down_interruptible(&recv_buff_mtx)) return -ERESTARTSYS; } if (!fs_transport_buffer_check_size(&recv_buff, count)) { count = fs_transport_buffer_get_free_size(&recv_buff); if (!count) return -ENOBUFS; } if (copy_from_user(recv_buff.start + recv_buff.length, ubuf, count)) { up(&recv_buff_mtx); return -EINVAL; } recv_buff.length += count; { TransportBufferEntry answer; while (__TransportBufferGetNextPacket(&recv_buff, &answer)) { ProcessProtocolRequest((ProtocolData*)answer.begin, NULL); } } up(&recv_buff_mtx); return count; } static void delayed_close(struct work_struct* work) { if (!atomic_read(&monitor_opened)) { printk("delayed_close: alerady closed\n"); return; } if (atomic_read(&monitor_started)) { monitor_module_stop(); } #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)) unregister_ioctl32_conversion(MONITOR_CONFIG_CMD); unregister_ioctl32_conversion(MONITOR_CONFIG2_CMD); unregister_ioctl32_conversion(MONITOR_START); unregister_ioctl32_conversion(MONITOR_CLEAR_CACHE_CMD); #endif Monitor_dec_use(); atomic_xchg(&monitor_opened, 0); } static int monitor_daemon_close(struct inode* inode, struct file* filp) { DEBUG_MSG("%s", __FUNCTION__); if (!atomic_read(&monitor_opened)) { return -EALREADY; } INIT_WORK(&shutdown_work, delayed_close); if (!queue_work(kav_workqueue, &shutdown_work)) { printk("klflt: failed to schedule shutdown work\n"); } return 0; } static unsigned int monitor_daemon_poll(struct file* file, poll_table* wait) { unsigned int ret = POLLOUT | POLLWRNORM; poll_wait(file, &queue_empty_waitq, wait); if (down_interruptible(&send_buff_mtx)) return -ERESTARTSYS; if (ReadyForRead(&send_buff)) ret = POLLIN | POLLRDNORM; up(&send_buff_mtx); return ret; } #ifndef USE_REDIRFS static int monitor_daemon_ioctl(struct inode* inode, struct file* filp, unsigned cmd, unsigned long addr) #else static long monitor_daemon_ioctl(struct file* filp, unsigned cmd, unsigned long addr) #endif { file_info f_info; char* path; switch (cmd) { case MONITOR_REGISTER_FILE_CMD: DEBUG_MSG("Register file command pid=%d", current->pid); if (!get_file_from_userspace((char __user *)addr, &f_info, &path)) return -EINVAL; MEM_FREE(path); return -Monitor_kernel_register_file(&f_info); case MONITOR_REGISTER_PROC_CMD: { monitor_register_proc_ioctl __user *params = (monitor_register_proc_ioctl __user *)addr; int pid; DEBUG_MSG("Register proc command pid=%d", current->pid); if (get_user(pid, ¶ms->pid)) { DEBUG_MSG("cannot get pid"); return -EFAULT; } if (!pid) { DEBUG_MSG("pid is not set"); return -EFAULT; } if (!get_file_from_userspace((char __user*)params->path, &f_info, &path)) { DEBUG_MSG("path is not set"); return -EFAULT; } DEBUG_MSG("Register proc command path=%s pid=%d", path, pid); MEM_FREE(path); return -Monitor_kernel_register_proc((pid_t)pid, &f_info); } case MONITOR_CONFIG_CMD: { monitor_config_ioctl __user *params; int cache_size; params = (monitor_config_ioctl __user*)addr; if (!params) return -EFAULT; if (get_user(cache_size, ¶ms->cache_size)) return -EFAULT; m_config.cache_enable = cache_size ? 1 : 0; Monitor_cache_start(cache_size); if (!atomic_read(&monitor_started)) { if (Monitor_intercept_init()) return -EFAULT; atomic_xchg(&monitor_started, 1); } } return 0; case MONITOR_CONFIG2_CMD: { monitor_config2_ioctl __user* params; int cache_size; uint32_t version; uint32_t max_fname_len; int8_t deny_too_long_fname; params = (monitor_config2_ioctl __user*)addr; if (!params) return -EFAULT; if (get_user(cache_size, ¶ms->cache_size)) return -EFAULT; if (get_user(version, ¶ms->version)) return -EFAULT; if (get_user(max_fname_len, ¶ms->max_fname_len)) return -EFAULT; if (get_user(deny_too_long_fname, ¶ms->deny_too_long_fname)) return -EFAULT; if (version > FS_PROTOCOL_VERSION) return -EOPNOTSUPP; m_config.version = version; m_config.cache_enable = cache_size ? 1 : 0; m_config.max_fname_len = max_fname_len; m_config.deny_too_long_fname = deny_too_long_fname; Monitor_cache_start(cache_size); if (m_config.version < 3 && !atomic_read(&monitor_started)) { if (Monitor_intercept_init()) return -EFAULT; atomic_xchg(&monitor_started, 1); } } return 0; case MONITOR_START: if (m_config.version < 3) return -EOPNOTSUPP; if (atomic_read(&monitor_started)) return -EBUSY; if (Monitor_intercept_init()) return -EFAULT; atomic_xchg(&monitor_started, 1); return 0; case MONITOR_PING_CMD: return Monitor_intercept_ping(); case MONITOR_CLEAR_CACHE_CMD: Monitor_cache_clear(); return 0; } return -EINVAL; } static int monitor_open_old(struct inode* inode, struct file* filp) { return 0; } static int monitor_close_old(struct inode* inode, struct file* filp) { return 0; } #ifndef USE_REDIRFS static int monitor_ioctl_old(struct inode* inode, struct file* filp, unsigned cmd, unsigned long addr) #else static long monitor_ioctl_old(struct file* filp, unsigned cmd, unsigned long addr) #endif { return handle_monitor_ioctl_old(cmd, addr); } #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)) static long monitor_compat_ioctl_old(struct file* filp, unsigned int cmd, unsigned long addr) { return (long)handle_monitor_ioctl_old(cmd, addr); } static long monitor_daemon_compat_ioctl(struct file* filp, unsigned int cmd, unsigned long addr) { return monitor_daemon_ioctl(filp, cmd, addr); } #endif #if defined(CONFIG_X86_64) && (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)) static int monitor_ioctl32_old(unsigned int fd, unsigned int cmd, unsigned long addr, struct file* file) { return handle_monitor_ioctl_old(cmd, addr); } #endif static long handle_monitor_ioctl_old(unsigned cmd, unsigned long addr) { file_info f_info; int err; struct vfsmount* vfsmnt; struct dentry* dentry; if (!atomic_read(&monitor_started)) return 0; switch (cmd) { case MONITOR_REGISTER_CMD_COMPAT: case MONITOR_GROUP_CMD_COMPAT: err = get_exec_path(current, &vfsmnt, &dentry); if (err) return err; else fill_file_info(dentry, &f_info); if (!Monitor_kernel_check_file_sign(get_current_euid(), get_current_egid(), vfsmnt, dentry)) return -EINVAL; err = -Monitor_kernel_register_file(&f_info); return err; } return -EINVAL; } static void file_checked(void* ctx, const CheckFileResultInfoData* data) { DEBUG_MSG("%s: queue=%d", __FUNCTION__, data->queue_id); Monitor_queue_set_answer(data->queue_id, data->result); Monitor_queue_wakeup(data->queue_id); } static void monitor_shutdown(void* ctx) { DEBUG_MSG("%s:", __FUNCTION__); // DONT_FIXME: PLEASE NEVER SHUTDOWN }
💾 Save Changes
Cancel
📤 Upload File
×
Select File
Upload
Cancel
➕ Create New
×
Type
📄 File
📁 Folder
Name
Create
Cancel
✎ Rename Item
×
Current Name
New Name
Rename
Cancel
🔐 Change Permissions
×
Target File
Permission (e.g., 0755, 0644)
0755
0644
0777
Apply
Cancel