Server IP : 103.169.32.36 / Your IP : 216.73.217.13 Web Server : Apache System : Linux web.dpmptsp 3.10.0-1160.119.1.el7.x86_64 #1 SMP Tue Jun 4 14:43:51 UTC 2024 x86_64 User : apache ( 48) PHP Version : 5.6.40 Disable Function : NONE MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /var/opt/eset/efs/eventd/eset_rtp/ |
Upload File : |
| Current File : /var/opt/eset/efs/eventd/eset_rtp/ertp_event.c |
/*
* eset_rtp (ESET Real-time file system protection module)
* Copyright (C) 1992-2023 ESET, spol. s r.o.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* In case of any questions, you can contact us at ESET, spol. s r.o., Einsteinova 24, 851 01 Bratislava, Slovakia.
*/
#include "ertp_event.h"
#include "ertp.h"
#include "ertp_cache.h"
#include "ertp_event_queue.h"
#include "ertp_logs.h"
#include "ertp_scanner.h"
#include <linux/cred.h>
#include <linux/slab.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 17, 0))
#include <linux/magic.h>
#endif
#ifndef FUSE_SUPER_MAGIC
#define FUSE_SUPER_MAGIC 0x65735546
#endif
#define ERTP_MAX_TIMEOUT 10000
#define ERTP_FD_PATH_STR_SIZE 128
#define ERTP_ID_STR_SIZE 32
#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 18, 0))
static int get_unused_fd(void) { return get_unused_fd_flags(0); }
#endif
static atomic_t ertp_event_ids = ATOMIC_INIT(0);
static struct kmem_cache *ertp_event_generic_allocator = NULL;
static struct kmem_cache *ertp_event_memory_allocator = NULL;
static struct kmem_cache *ertp_event_remove_allocator = NULL;
int ertp_event_allocator_init(void) {
int err = ertp_path_allocator_init();
if (err) {
return err;
}
ertp_event_generic_allocator = kmem_cache_create(
"ertp_event_generic_allocator", sizeof(struct ertp_event_generic),
__alignof__(struct ertp_event_generic), SLAB_RECLAIM_ACCOUNT, NULL);
ertp_event_memory_allocator = kmem_cache_create(
"ertp_event_memory_allocator", sizeof(struct ertp_event_memory),
__alignof__(struct ertp_event_memory), SLAB_RECLAIM_ACCOUNT, NULL);
ertp_event_remove_allocator = kmem_cache_create(
"ertp_event_remove_allocator", sizeof(struct ertp_event_remove),
__alignof__(struct ertp_event_remove), SLAB_RECLAIM_ACCOUNT, NULL);
if (!ertp_event_generic_allocator || !ertp_event_memory_allocator ||
!ertp_event_remove_allocator) {
return -ENOMEM;
}
return 0;
}
void ertp_event_allocator_deinit(void) {
if (ertp_event_generic_allocator) {
kmem_cache_destroy(ertp_event_generic_allocator);
}
if (ertp_event_memory_allocator) {
kmem_cache_destroy(ertp_event_memory_allocator);
}
if (ertp_event_remove_allocator) {
kmem_cache_destroy(ertp_event_remove_allocator);
}
ertp_path_allocator_deinit();
}
struct ertp_event *ertp_event_ref(struct ertp_event *event) {
if (!event || IS_ERR(event)) return NULL;
BUG_ON(atomic_read(&event->count) == 0);
atomic_inc(&event->count);
return event;
}
void ertp_event_unref(struct ertp_event *event) {
if (!event || IS_ERR(event)) return;
BUG_ON(atomic_read(&event->count) == 0);
if (!atomic_dec_and_test(&event->count)) return;
event->destroy(event);
}
const char *ertp_event_type_to_str(enum ertp_event_type event) {
switch (event) {
case ERTP_EVENT_TYPE_OPEN:
return "OPEN";
case ERTP_EVENT_TYPE_CLOSE:
return "CLOSE";
case ERTP_EVENT_TYPE_EXEC:
return "EXEC";
}
return "(unknown)";
}
#ifdef ERTP_DEBUG
static const char *ertp_event_result_to_str(int result) {
return result == ERTP_ALLOW ? "ALLOW" : "DENY";
}
#endif
static void ertp_event_generic_process_result(struct ertp_event *event_base,
uint32_t result,
bool do_not_cache) {
struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
ertp_pr_log(ERTP_LOG_EVENTS,
"event done (id: %d, ino: %lld, dev: %lld, type: %s, result: %s "
"%s, file: %s, process: %s)",
event_base->id, event->ino, event->dev,
ertp_event_type_to_str(event->type),
ertp_event_result_to_str(result),
do_not_cache ? "do_not_cache" : "-", event->file_path->ptr,
event->process_path->ptr);
event->result = result;
complete_all(&event->wait);
if (result == ERTP_ALLOW && !do_not_cache) {
ertp_cache_add(event);
}
}
static void ertp_event_memory_process_result(struct ertp_event *event_base,
uint32_t result,
bool do_not_cache) {
struct ertp_event_memory *event = ERTP_EVENT_MEMORY(event_base);
ertp_pr_log(ERTP_LOG_EVENTS,
"event done (id: %d, type: %s, result: %s %s, mem_dev file: %s, "
"process: %s)",
event_base->id, ertp_event_type_to_str(event->type),
ertp_event_result_to_str(result),
do_not_cache ? "do_not_cache" : "-", event->dev_info->name,
event->process_path->ptr);
event->result = result;
complete_all(&event->wait);
}
static void ertp_event_remove_process_result(struct ertp_event *event_base,
uint32_t result,
bool do_not_cache) {
struct ertp_event_remove *event = ERTP_EVENT_REMOVE(event_base);
ertp_pr_log(ERTP_LOG_EVENTS, "remove event done (id: %d)", event_base->id);
complete_all(&event->wait);
}
void ertp_event_process_scanner_response(
const struct eset_rtp_set_av_status *response) {
struct ertp_event *event =
ertp_event_queue_remove_scanned_event(response->id);
if (event) {
uint32_t result = ERTP_UNKNOWN;
switch (response->result) {
case ERTP_RESULT_ALLOW:
result = ERTP_ALLOW;
break;
case ERTP_RESULT_DENY:
result = ERTP_DENY;
break;
}
event->process_result(event, result,
response->flags & ERTP_AV_RESULT_FLAG_DO_NOT_CACHE);
ertp_event_unref(event);
}
}
void ertp_event_process_event_response(
const struct eset_rtp_remove_event_finished *response) {
struct ertp_event *event =
ertp_event_queue_remove_scanned_event(response->id);
if (event) {
ertp_event_unblock(event);
ertp_event_unref(event);
}
}
void ertp_event_unblock(struct ertp_event *event) {
event->process_result(event, ERTP_ALLOW, true);
}
static int ertp_event_generic_wait_for_result(struct ertp_event *event_base) {
long jiffies;
struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
jiffies = wait_for_completion_timeout(&event->wait,
msecs_to_jiffies(ERTP_MAX_TIMEOUT));
if (!jiffies) {
ertp_pr_warning(
"wait for scanner reply timeout, path: %s, event: %s, id: %d, pid: %d",
event->file_path->ptr, ertp_event_type_to_str(event->type),
event->base.id, event->pid);
return -ETIMEDOUT;
}
return 0;
}
static int ertp_event_memory_wait_for_result(struct ertp_event *event_base) {
long jiffies;
struct ertp_event_memory *event = ERTP_EVENT_MEMORY(event_base);
jiffies = wait_for_completion_timeout(&event->wait,
msecs_to_jiffies(ERTP_MAX_TIMEOUT));
if (!jiffies) {
ertp_pr_warning(
"wait for scanner reply timeout, mem_dev file: %s, event: %s, id: %d, "
"pid: %d",
event->dev_info->name, ertp_event_type_to_str(event->type),
event->base.id, event->pid);
return -ETIMEDOUT;
}
return 0;
}
static int ertp_event_remove_wait_for_result(struct ertp_event *event_base) {
long jiffies;
struct ertp_event_remove *event = ERTP_EVENT_REMOVE(event_base);
jiffies = wait_for_completion_timeout(&event->wait,
msecs_to_jiffies(ERTP_MAX_TIMEOUT));
if (!jiffies) {
ertp_pr_warning(
"wait for scanner reply timeout, event: REMOVE, id: %d, ino: %lld, "
"dev: %lld",
event->base.id, event->ino, event->dev);
return -ETIMEDOUT;
}
return 0;
}
static int ertp_open_event_file(struct ertp_event_generic *event) {
struct file *file;
int flags;
int fd;
fd = get_unused_fd();
if (fd < 0) return fd;
flags = O_RDONLY;
flags |= event->flags & O_LARGEFILE;
file = ertp_dentry_open(&event->path, flags);
if (IS_ERR(file) && !d_unlinked(event->path.dentry)) {
ertp_pr_log(ERTP_LOG_ERRORS, "cannot open dentry for event %d with path %s",
event->base.id, event->file_path->ptr);
file = filp_open(event->file_path->ptr, flags, 0);
}
if (IS_ERR(file)) {
ertp_pr_log(ERTP_LOG_ERRORS, "cannot open file for event %d with path %s",
event->base.id, event->file_path->ptr);
put_unused_fd(fd);
return PTR_ERR(file);
}
event->file = file;
event->fd = fd;
return 0;
}
static void ertp_close_event_file(struct ertp_event_generic *event) {
if (event->fd > 0) put_unused_fd(event->fd);
if (event->file) fput(event->file);
event->fd = -1;
event->file = NULL;
}
static int ertp_open_event_dev_file(struct ertp_event_memory *event) {
struct file *file;
int flags;
int fd;
char str_path[ERTP_FD_PATH_STR_SIZE];
fd = get_unused_fd();
if (fd < 0) {
return fd;
}
flags = O_RDONLY;
flags |= event->flags & O_LARGEFILE;
str_path[0] = '\0';
strlcat(str_path, "/dev/", ERTP_FD_PATH_STR_SIZE);
strlcat(str_path, event->dev_info->name, ERTP_FD_PATH_STR_SIZE);
file = filp_open(str_path, flags, 0);
if (!file || IS_ERR(file)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"cannot open mem_dev file for event %d with path %s",
event->base.id, str_path);
put_unused_fd(fd);
return 1;
}
event->file = file;
event->fd = fd;
return 0;
}
static void ertp_path_serialize(const struct ertp_path *path,
struct eset_rtp_flexible_member *flex,
void *buf, size_t buf_size, size_t *offset) {
size_t off = *offset;
if (unlikely(!path->ptr)) {
BUG();
}
if (unlikely(off >= buf_size)) {
BUG();
}
if (unlikely(path->size > buf_size - off)) {
BUG();
}
memcpy(buf + off, path->ptr, path->size);
flex->offset = (uint32_t)off;
flex->size = (uint32_t)path->size;
*offset = off + path->size;
}
static int serialize_generic_flex_members(
const struct ertp_event_generic *event,
struct eset_rtp_event_generic *buffer, size_t max_size, void **flex_data,
size_t *flex_size) {
size_t offset = 0;
void *buf = NULL;
size_t total_size = event->file_path->size + event->process_path->size;
if (unlikely(total_size > max_size)) {
return -ENOBUFS;
}
buf = kzalloc(total_size, GFP_KERNEL);
if (unlikely(!buf)) {
return -ENOMEM;
}
ertp_path_serialize(event->file_path, &buffer->file_path, buf, total_size,
&offset);
ertp_path_serialize(event->process_path, &buffer->process_path, buf,
total_size, &offset);
*flex_data = buf;
*flex_size = total_size;
return 0;
}
static int ertp_event_generic_serialize(struct ertp_event *event_base,
struct eset_rtp_event *buf,
size_t max_size, void **flex_data,
size_t *flex_size) {
int err = 0;
void *data = NULL;
size_t size;
struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
switch (event->type) {
case ERTP_EVENT_TYPE_OPEN:
buf->type = ERTP_TYPE_OPEN_EVENT;
break;
case ERTP_EVENT_TYPE_CLOSE:
buf->type = ERTP_TYPE_CLOSE_EVENT;
break;
case ERTP_EVENT_TYPE_EXEC:
buf->type = ERTP_TYPE_EXEC_EVENT;
break;
default:
BUG();
}
err = serialize_generic_flex_members(event, &buf->data.generic, max_size,
&data, &size);
if (err) {
goto error;
}
err = ertp_open_event_file(event);
if (unlikely(err)) {
goto error;
}
fd_install(event->fd, event->file);
buf->data.generic.id = event_base->id;
buf->data.generic.fd = event->fd;
buf->data.generic.pid = event->pid;
buf->data.generic.uid = event->uid.val;
buf->data.generic.gid = event->gid.val;
buf->data.generic.file_id = event->ino;
buf->data.generic.device_number = event->dev;
buf->data.generic.under_fuse = event->under_fuse;
buf->data.generic.file_size = event->file_size;
buf->data.generic.ctime = event->ctime.tv_sec;
buf->data.generic.mtime = event->mtime;
buf->data.generic.mode = event->mode;
buf->data.generic.flags = event->modified ? ERTP_EVENT_FLAG_MODIFIED : 0;
*flex_data = data;
*flex_size = size;
return 0;
error:
ertp_close_event_file(event);
kfree(data);
return err;
}
static int serialize_memory_flex_members(const struct ertp_event_memory *event,
struct eset_rtp_event_memory *buffer,
size_t max_size, void **flex_data,
size_t *flex_size) {
size_t offset = 0;
void *buf = NULL;
size_t total_size = event->process_path->size;
if (unlikely(total_size > max_size)) {
return -ENOBUFS;
}
buf = kzalloc(total_size, GFP_KERNEL);
if (unlikely(!buf)) {
return -ENOMEM;
}
ertp_path_serialize(event->process_path, &buffer->process_path, buf,
total_size, &offset);
*flex_data = buf;
*flex_size = total_size;
return 0;
}
static int ertp_event_memory_serialize(struct ertp_event *event_base,
struct eset_rtp_event *buf,
size_t max_size, void **flex_data,
size_t *flex_size) {
int err = 0;
void *data = NULL;
size_t size;
struct ertp_event_memory *event = ERTP_EVENT_MEMORY(event_base);
switch (event->type) {
case ERTP_EVENT_TYPE_EXEC:
buf->type = ERTP_TYPE_MEMORY_EXEC_EVENT;
break;
default:
BUG();
}
err = serialize_memory_flex_members(event, &buf->data.memory, max_size, &data,
&size);
if (err) {
goto error;
}
err = ertp_open_event_dev_file(event);
if (unlikely(err)) {
ertp_pr_log(ERTP_LOG_ERRORS, "error opening mem file %d for event %d\n",
err, event_base->id);
goto error;
}
fd_install(event->fd, event->file);
buf->data.memory.id = event_base->id;
buf->data.memory.fd = event->fd;
buf->data.memory.pid = event->pid;
buf->data.memory.uid = event->uid.val;
buf->data.memory.gid = event->gid.val;
buf->data.memory.file_size = event->size;
*flex_data = data;
*flex_size = size;
return 0;
error:
kfree(data);
return err;
}
static int ertp_event_remove_serialize(struct ertp_event *event_base,
struct eset_rtp_event *buf,
size_t max_size, void **flex_data,
size_t *flex_size) {
struct ertp_event_remove *event = ERTP_EVENT_REMOVE(event_base);
buf->type = ERTP_TYPE_REMOVE_EVENT;
buf->data.remove.id = event_base->id;
buf->data.remove.file_id = event->ino;
buf->data.remove.device_number = event->dev;
return 0;
}
static int ertp_event_generic_has_same_path(const struct ertp_event *event_base,
const struct path *path) {
const struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
return path_equal(&(event->path), path);
}
static int ertp_event_memory_has_same_path(const struct ertp_event *event_base,
const struct path *path) {
return false;
}
static int ertp_event_remove_has_same_path(const struct ertp_event *event_base,
const struct path *path) {
return false;
}
static int ertp_event_generic_has_same_info(
const struct ertp_event *event_base,
const struct ertp_event_generic_info *info) {
const struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
const struct inode *inode_ptr = ertp_get_inode(info->file);
bool is_same =
(event->ino == inode_ptr->i_ino &&
event->dev == new_encode_dev(inode_ptr->i_sb->s_dev) &&
ertp_timespec_equal(event->ctime, ertp_inode_get_ctime(inode_ptr)) &&
event->type == info->type);
return is_same;
}
static int ertp_event_memory_has_same_info(
const struct ertp_event *event_base,
const struct ertp_event_generic_info *info) {
return false;
}
static int ertp_event_remove_has_same_info(
const struct ertp_event *event_base,
const struct ertp_event_generic_info *info) {
return false;
}
static void ertp_event_generic_free(struct ertp_event *event_base) {
struct ertp_event_generic *event = ERTP_EVENT_GENERIC(event_base);
path_put(&event->path);
ertp_path_unref(event->file_path);
ertp_path_unref(event->process_path);
kmem_cache_free(ertp_event_generic_allocator, event);
}
static void ertp_event_memory_free(struct ertp_event *event_base) {
struct ertp_event_memory *event = ERTP_EVENT_MEMORY(event_base);
ertp_path_unref(event->process_path);
kmem_cache_free(ertp_event_memory_allocator, event);
}
static void ertp_event_remove_free(struct ertp_event *event_base) {
struct ertp_event_remove *event = ERTP_EVENT_REMOVE(event_base);
kmem_cache_free(ertp_event_remove_allocator, event);
}
static inline void ertp_event_base_init(struct ertp_event *event) {
INIT_LIST_HEAD(&event->req_list);
atomic_set(&event->count, 1);
event->id = atomic_inc_return(&ertp_event_ids);
if (unlikely(!event->destroy || !event->serialize ||
!event->wait_for_result || !event->process_result ||
!event->has_same_path || !event->has_same_info)) {
BUG();
}
}
struct ertp_event *ertp_event_generic_new(
const struct ertp_event_generic_info *params) {
struct kstat attr_info;
struct kstatfs fs_info;
int ret_val;
struct ertp_event *event_base;
struct ertp_event_generic *event =
kmem_cache_zalloc(ertp_event_generic_allocator, GFP_KERNEL);
if (unlikely(!event)) {
return ERR_PTR(-ENOMEM);
}
if (unlikely(!ertp_get_inode(params->file))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"cannot create generic event for file %s: inode not found",
params->file_path->ptr);
goto error;
}
ret_val = ertp_vfs_getattr(¶ms->file->f_path, &attr_info);
if (ret_val != 0) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"cannot create generic event for file %s: getattr failed with error %d",
params->file_path->ptr, ret_val);
goto error;
}
ret_val = ertp_vfs_statfs(¶ms->file->f_path, &fs_info);
if (ret_val != 0) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"cannot create generic event for file %s: statfs failed with error %d",
params->file_path->ptr, ret_val);
goto error;
}
event_base = ERTP_EVENT(event);
event_base->class = ERTP_EVENT_CLASS_GENERIC;
event_base->destroy = &ertp_event_generic_free;
event_base->serialize = &ertp_event_generic_serialize;
event_base->wait_for_result = &ertp_event_generic_wait_for_result;
event_base->process_result = &ertp_event_generic_process_result;
event_base->has_same_path = &ertp_event_generic_has_same_path;
event_base->has_same_info = &ertp_event_generic_has_same_info;
ertp_event_base_init(event_base);
init_completion(&event->wait);
event->type = params->type;
event->modified = params->modified;
event->file = NULL;
event->fd = -1;
path_get(¶ms->file->f_path);
event->path = params->file->f_path;
event->flags = params->file->f_flags;
event->under_fuse = (fs_info.f_type == FUSE_SUPER_MAGIC);
event->file_path = ertp_path_ref(params->file_path);
current_uid_gid(&event->uid, &event->gid);
event->pid = current->tgid;
event->process_path = ertp_path_ref(params->process_path);
;
event->ino = attr_info.ino;
event->dev = new_encode_dev(attr_info.dev);
event->file_size = attr_info.size;
event->ctime = attr_info.ctime;
event->mtime = attr_info.mtime.tv_sec;
event->mode = attr_info.mode;
return event_base;
error:
kmem_cache_free(ertp_event_generic_allocator, event);
return ERR_PTR(-ENODATA);
}
struct ertp_event *ertp_event_memory_new(
const struct ertp_event_memory_info *params) {
struct ertp_event *event_base;
struct ertp_event_memory *event;
char id_str[ERTP_ID_STR_SIZE];
event = kmem_cache_zalloc(ertp_event_memory_allocator, GFP_KERNEL);
if (unlikely(!event)) {
return ERR_PTR(-ENOMEM);
}
event_base = ERTP_EVENT(event);
event_base->class = ERTP_EVENT_CLASS_MEMORY;
event_base->destroy = &ertp_event_memory_free;
event_base->serialize = &ertp_event_memory_serialize;
event_base->wait_for_result = &ertp_event_memory_wait_for_result;
event_base->process_result = &ertp_event_memory_process_result;
event_base->has_same_path = &ertp_event_memory_has_same_path;
event_base->has_same_info = &ertp_event_memory_has_same_info;
ertp_event_base_init(event_base);
snprintf(id_str, ERTP_ID_STR_SIZE, "%d", event_base->id);
if (ertp_mem_dev_create(id_str, event, ERTP_MAX_TIMEOUT)) {
goto error;
}
init_completion(&event->wait);
event->type = params->type;
current_uid_gid(&event->uid, &event->gid);
event->pid = current->tgid;
event->process_path = ertp_path_ref(params->process_path);
event->size = params->size;
event->memory = params->memory_to_scan;
return event_base;
error:
kmem_cache_free(ertp_event_memory_allocator, event);
return ERR_PTR(-ENODATA);
}
struct ertp_event *ertp_event_remove_new(const struct path *path) {
struct ertp_event *event_base;
struct ertp_event_remove *event;
struct kstat stat;
int ret_attr;
if (unlikely(!path)) {
ertp_pr_log(ERTP_LOG_ERRORS, "cannot create remove event: invalid path");
return ERR_PTR(-EINVAL);
}
ret_attr = ertp_vfs_getattr(path, &stat);
if (ret_attr != 0) {
ertp_pr_log(ERTP_LOG_ERRORS,
"cannot create remove event: getattr failed with error %d",
ret_attr);
return ERR_PTR(-ENODATA);
}
event = kmem_cache_zalloc(ertp_event_remove_allocator, GFP_KERNEL);
if (!event) {
return ERR_PTR(-ENOMEM);
}
event_base = ERTP_EVENT(event);
event_base->class = ERTP_EVENT_CLASS_REMOVE;
event_base->destroy = &ertp_event_remove_free;
event_base->serialize = &ertp_event_remove_serialize;
event_base->wait_for_result = &ertp_event_remove_wait_for_result;
event_base->process_result = &ertp_event_remove_process_result;
event_base->has_same_path = &ertp_event_remove_has_same_path;
event_base->has_same_info = &ertp_event_remove_has_same_info;
ertp_event_base_init(event_base);
init_completion(&event->wait);
event->ino = stat.ino;
event->dev = new_encode_dev(stat.dev);
return event_base;
}