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_dev.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.h"
#include "ertp_cache.h"
#include "ertp_event.h"
#include "ertp_event_queue.h"
#include "ertp_excludes.h"
#include "ertp_logs.h"
#include "ertp_scanner.h"
#include <linux/cred.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)
#if defined(RHEL_RELEASE_CODE) && defined(RHEL_RELEASE_VERSION)
#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(8, 1))
#define ertp_access_ok(addr, size) access_ok(addr, size)
#else
#define ertp_access_ok(addr, size) access_ok(VERIFY_WRITE, addr, size)
#endif
#else
#define ertp_access_ok(addr, size) access_ok(VERIFY_WRITE, addr, size)
#endif
#else
#define ertp_access_ok(addr, size) access_ok(addr, size)
#endif
static int ertp_dev_open(struct inode *inode, struct file *file) {
int ret;
if (!(file->f_mode & FMODE_WRITE)) return -EINVAL;
ret = ertp_scanner_register(current->tgid);
if (ret) return ret;
return 0;
}
static int ertp_dev_release(struct inode *inode, struct file *file) {
if (ertp_scanner_is_registered()) {
ertp_scanner_unregister();
ertp_event_queue_clear();
}
return 0;
}
static long ertp_dev_handle_get_event(struct eset_rtp_get_event __user *arg) {
long err = 0;
struct ertp_event *event = NULL;
struct eset_rtp_get_event query;
struct eset_rtp_event tmp_query_event;
void *flex_buf = NULL;
size_t flex_buf_sz = 0;
unsigned long n;
if (unlikely(arg == NULL)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: NULL argument provided");
return -EFAULT;
}
n = copy_from_user(&query, arg, sizeof(struct eset_rtp_get_event));
if (unlikely(n != 0)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: copy_from_user failed");
return -EFAULT;
}
if (unlikely(query.version != ERTP_PROTOCOL_VERSION)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: invalid version of protocol (%u), "
"this version of kernel module requires version: %u",
query.version, ERTP_PROTOCOL_VERSION);
return -EINVAL;
}
if (unlikely(query.event == NULL || query.data == NULL ||
!ertp_access_ok(query.event, sizeof(struct eset_rtp_event)) ||
!ertp_access_ok(query.data, query.data_size))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: invalid pointer for eset_rtp_event");
return -EFAULT;
}
event = ertp_event_queue_get_next();
if (!event) {
return -EWOULDBLOCK;
}
err = event->serialize(event, &tmp_query_event, query.data_size, &flex_buf,
&flex_buf_sz);
if (unlikely(err)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: event serialization failed");
if (err == -ENOBUFS) {
ertp_pr_log(ERTP_LOG_EVENTS,
"provided buffer for event with id %d was too small",
event->id);
if (!ertp_event_queue_reinsert_not_sent_event(event)) {
goto error;
}
goto end;
}
goto error;
}
if (flex_buf && flex_buf_sz > 0) {
n = copy_to_user(query.data, flex_buf, flex_buf_sz);
if (unlikely(n != 0)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: copy_to_user of flex data failed");
err = -EFAULT;
goto error;
}
}
n = copy_to_user(query.event, &tmp_query_event,
sizeof(struct eset_rtp_event));
if (unlikely(n != 0)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_GET_EVENT failed: copy_to_user of event failed");
err = -EFAULT;
goto error;
}
ertp_pr_log(ERTP_LOG_EVENTS, "event (%s) with id %d was sent to user space",
event->class == ERTP_EVENT_CLASS_GENERIC ? "GENERIC" : "REMOVE",
event->id);
goto end;
error:
if (unlikely(err)) {
struct ertp_event *event_removed =
ertp_event_queue_remove_scanned_event(event->id);
ertp_event_unref(event_removed);
ertp_pr_log(ERTP_LOG_EVENTS,
"event (%s) with id %d was not sent to user space",
event->class == ERTP_EVENT_CLASS_GENERIC ? "GENERIC" : "REMOVE",
event->id);
ertp_event_unblock(event);
}
end:
ertp_event_unref(event);
kfree(flex_buf);
return err;
}
static long ertp_dev_handle_set_av_status(
struct eset_rtp_set_av_status __user *arg) {
struct eset_rtp_set_av_status status;
if (unlikely(copy_from_user(&status, arg,
sizeof(struct eset_rtp_set_av_status)))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_SET_AV_STATUS failed: copy_from_user failed");
return -EFAULT;
}
if (unlikely(status.version != ERTP_PROTOCOL_VERSION)) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"ERTP_OP_SET_AV_STATUS failed: invalid version of protocol (%u), this version\
of kernel module requires version: %u",
status.version, ERTP_PROTOCOL_VERSION);
return -EINVAL;
}
ertp_event_process_scanner_response(&status);
return 0;
}
static long ertp_dev_handle_set_event_response(
struct eset_rtp_remove_event_finished __user *arg) {
struct eset_rtp_remove_event_finished response;
if (unlikely(copy_from_user(&response, arg,
sizeof(struct eset_rtp_remove_event_finished)))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_REMOVE_EVENT_FINISHED failed: copy_from_user failed");
return -EFAULT;
}
if (unlikely(response.version != ERTP_PROTOCOL_VERSION)) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"ERTP_OP_REMOVE_EVENT_FINISHED failed: invalid version of protocol (%u), this version\
of kernel module requires version: %u",
response.version, ERTP_PROTOCOL_VERSION);
return -EINVAL;
}
ertp_event_process_event_response(&response);
return 0;
}
static char *alloc_excluded_path(const char *path) {
char *result = NULL;
long path_length;
if (path == NULL) {
return ERR_PTR(-EINVAL);
}
path_length = strnlen_user(path, PATH_MAX);
if (path_length <= 1 || path_length > PATH_MAX) {
return ERR_PTR(-EINVAL);
}
result = kmalloc(path_length, GFP_KERNEL);
if (!result) {
return ERR_PTR(-ENOMEM);
}
if (copy_from_user(result, path, path_length)) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_SET_EXCLUSIONS failed: copy_from_user failed");
kfree(result);
return ERR_PTR(-EFAULT);
}
return result;
}
static void free_excluded_paths(char **paths, uint32_t size) {
uint32_t i;
for (i = 0; i < size; ++i) {
kfree(paths[i]);
}
}
static long load_excluded_paths(char **paths, uint32_t size) {
uint32_t i;
for (i = 0; i < size; ++i) {
char *path = alloc_excluded_path(paths[i]);
if (unlikely(IS_ERR(path))) {
long err = PTR_ERR(path);
free_excluded_paths(paths, i);
return err;
}
paths[i] = path;
}
return 0;
}
static long ertp_dev_handle_set_exclusions(
struct eset_rtp_set_exclusions __user *arg) {
long err = 0;
struct eset_rtp_set_exclusions request;
char **paths = NULL;
if (unlikely((copy_from_user(&request, arg,
sizeof(struct eset_rtp_set_exclusions))))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_SET_EXCLUSIONS failed: copy_from_user failed");
return -EFAULT;
}
if (unlikely(request.version != ERTP_PROTOCOL_VERSION)) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"ERTP_OP_SET_EXCLUSIONS failed: invalid version of protocol (%u), this version\
of kernel module requires version: %u",
request.version, ERTP_PROTOCOL_VERSION);
return -EINVAL;
}
if (request.size > (uint32_t)ERTP_EXCLUSIONS_MAX) {
ertp_pr_error("number of exclusions to set exceeded the allowed limit");
return -EINVAL;
}
if (request.size > 0) {
paths = kmalloc_array((size_t)request.size, sizeof(char *), GFP_KERNEL);
if (unlikely(!paths)) {
return -ENOMEM;
}
if (unlikely(copy_from_user(paths, request.paths,
(size_t)request.size * sizeof(char *)))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_SET_EXCLUSIONS failed: copy_from_user() of "
"eset_rtp_set_exclusions.paths failed");
err = -EFAULT;
goto end;
}
err = load_excluded_paths(paths, request.size);
if (unlikely(err)) {
goto end;
}
}
switch (request.type) {
case ERTP_EXCLUSIONS_TYPE_FILE_DEFAULT:
err = ertp_file_default_excludes_add(paths, request.size);
if (unlikely(err)) {
ertp_pr_error("cannot change default file exclusions");
}
break;
case ERTP_EXCLUSIONS_TYPE_PROCESS_DEFAULT:
err = ertp_proc_default_excludes_add(paths, request.size);
if (unlikely(err)) {
ertp_pr_error("cannot change default process exclusions");
}
break;
case ERTP_EXCLUSIONS_TYPE_FILE_USER:
err = ertp_file_user_excludes_add(paths, request.size);
if (unlikely(err)) {
ertp_pr_error("cannot change user configured file exclusions");
}
break;
case ERTP_EXCLUSIONS_TYPE_PROCESS_USER:
err = ertp_proc_user_excludes_add(paths, request.size);
if (unlikely(err)) {
ertp_pr_error("cannot change user configured process exclusions");
}
break;
default:
err = -EINVAL;
ertp_pr_error("cannot change exclusions: invalid exclusions type");
break;
}
if (unlikely(err)) {
free_excluded_paths(paths, request.size);
}
end:
kfree(paths);
return err;
}
static long ertp_dev_handle_do_action(struct eset_rtp_do_action __user *arg) {
struct eset_rtp_do_action request;
if (unlikely(
copy_from_user(&request, arg, sizeof(struct eset_rtp_do_action)))) {
ertp_pr_log(ERTP_LOG_ERRORS,
"ERTP_OP_DO_ACTION failed: copy_from_user failed");
return -EFAULT;
}
if (unlikely(request.version != ERTP_PROTOCOL_VERSION)) {
ertp_pr_log(
ERTP_LOG_ERRORS,
"ERTP_OP_DO_ACTION failed: invalid version of protocol (%u), this version\
of kernel module requires version: %u",
request.version, ERTP_PROTOCOL_VERSION);
return -EINVAL;
}
switch (request.action) {
case ERTP_ACTION_FLUSH_CACHE:
ertp_cache_clear();
return 0;
default:
break;
}
return -EINVAL;
}
static long ertp_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg) {
switch (cmd) {
case ERTP_OP_GET_EVENT:
return ertp_dev_handle_get_event((struct eset_rtp_get_event __user *)arg);
case ERTP_OP_SET_AV_STATUS:
return ertp_dev_handle_set_av_status(
(struct eset_rtp_set_av_status __user *)arg);
case ERTP_OP_REMOVE_EVENT_FINISHED:
return ertp_dev_handle_set_event_response(
(struct eset_rtp_remove_event_finished __user *)arg);
case ERTP_OP_SET_EXCLUSIONS:
return ertp_dev_handle_set_exclusions(
(struct eset_rtp_set_exclusions __user *)arg);
case ERTP_OP_DO_ACTION:
return ertp_dev_handle_do_action((struct eset_rtp_do_action __user *)arg);
default:
break;
}
return -EINVAL;
}
static unsigned int ertp_dev_poll(struct file *file, poll_table *wait) {
return ertp_event_queue_wait(file, wait);
}
static struct class *ertp_class;
static struct device *ertp_device;
static dev_t ertp_dev;
static struct file_operations ertp_fops = {.owner = THIS_MODULE,
.open = ertp_dev_open,
.release = ertp_dev_release,
.unlocked_ioctl = ertp_dev_ioctl,
.poll = ertp_dev_poll};
int ertp_dev_init(void) {
int major;
ertp_excludes_init();
major = register_chrdev(0, ESET_RTP, &ertp_fops);
if (major < 0) return major;
ertp_dev = MKDEV(major, 0);
ertp_class = ertp_class_create(ESET_RTP);
if (IS_ERR(ertp_class)) {
unregister_chrdev(major, ESET_RTP);
return PTR_ERR(ertp_class);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
ertp_device = device_create(ertp_class, NULL, ertp_dev, ESET_RTP);
#else
ertp_device = device_create(ertp_class, NULL, ertp_dev, NULL, ESET_RTP);
#endif
if (IS_ERR(ertp_device)) {
class_destroy(ertp_class);
unregister_chrdev(major, ESET_RTP);
return PTR_ERR(ertp_device);
}
return 0;
}
void ertp_dev_deinit(void) {
device_destroy(ertp_class, ertp_dev);
class_destroy(ertp_class);
unregister_chrdev(MAJOR(ertp_dev), ESET_RTP);
ertp_dev_release(NULL, NULL);
ertp_excludes_deinit();
}