shell bypass 403
/** @file @brief 'fork' event @details Copyright (c) 2017-2018 Acronis International GmbH @author Mikhail Krivtsov ([email protected]) @since $Id: $ */ #include "fork_event.h" #include "debug.h" #include "message.h" #include "path_tools.h" #include "si_templates.h" #include "si_writer_common.h" #include "task_info_map.h" #include "task_tools.h" #include <linux/fs_struct.h> // other is referring to 'child' in this case #define COMMON_FORK_FIELDS \ SI_COMMON_FIELDS, \ SI_COMMON_CRED_FIELDS, \ FP_SI_PI_OTHER_PROCESS_ID, \ FP_SI_PI_OTHER_THREAD_ID, \ FP_SI_PI_OTHER_PROCESS_ID_VERSION, \ FP_SI_PI_OTHER_PROCESS_UID, \ FP_SI_PI_OTHER_ARTIFICIAL_PROCESS_START_TIMESTAMP, \ FP_SI_PI_OTHER_PROCESS_START_TIMESTAMP, \ FP_SI_PI_PARENT_PROCESS_ID, \ FP_SI_PI_PARENT_THREAD_ID, \ FP_SI_PI_PARENT_PROCESS_ID_VERSION, \ FP_SI_PI_PARENT_PROCESS_UID, \ FP_SI_PI_PARENT_ARTIFICIAL_PROCESS_START_TIMESTAMP, \ FP_SI_PI_PARENT_PROCESS_START_TIMESTAMP, \ FP_SI_PI_PROCESS_START_TIMESTAMP, \ FP_SI_PI_ARTIFICIAL_PROCESS_START_TIMESTAMP, \ FP_SI_PI_CURRENT_WORKING_DIRECTORY static const uint8_t k_fork_fields_without_exe_fields[] = { COMMON_FORK_FIELDS, SI_COMMON_IMAGE_NAME_FIELDS, }; static const uint8_t k_fork_fields[] = { COMMON_FORK_FIELDS, SI_COMMON_EXE_FILE_FIELDS, }; // fork in Linux may refer to either process creation or thread creation // In case of process creation, we are interesting in image file and other process fields. static void fork_event_process_creation_with_alloc_flags(struct task_struct* curr, struct task_struct* child, bool nowait) { msg_t* msg; uint32_t event_size; bool has_parent = false; pid_t parent_tgid = 0; pid_t parent_pid = 0; uint64_t parent_upid = 0; SiTimeMicroseconds parent_start_time = {0}; uint64_t curr_unique_pid = make_unique_pid(curr); file_handle_info_t handle_info = (file_handle_info_t){0}; path_info_t path_info = (path_info_t){0}; char comm[TASK_COMM_LEN]; struct path* exe; path_info_t cwd_path_info = (path_info_t){0}; // mm allowed to be accessed without locks because child has not started to execute yet and it has a separate mm // Notice that some children do not have mm. That is true for kernel tasks, for example. struct mm_struct *mm = child->mm; struct file* exe_file = mm ? mm->exe_file : NULL; exe = exe_file ? &exe_file->f_path : NULL; if (exe && !path_is_usable(exe)) { exe = NULL; } // For FORK events, BE will want to have EXEC-like fields for child and curr process if (exe) { file_handle_info_make_with_alloc_flags(&handle_info, exe, nowait); path_info_make_with_alloc_flags(&path_info, exe, false /*not dir*/, nowait); event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fork_fields) + SI_ESTIMATE_SIZE_PATH_INFO(path_info) + SI_ESTIMATE_SIZE_FILE_HANDLE_INFO(handle_info); } else { get_task_comm(comm, child); event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fork_fields_without_exe_fields) + TASK_COMM_LEN; } { struct task_struct* parent; rcu_read_lock(); parent = rcu_dereference(curr->real_parent); parent_tgid = parent ? parent->tgid : 0; has_parent = 0 != parent_tgid; if (has_parent) { parent_pid = parent->pid; parent_upid = make_unique_pid(parent); parent_start_time = make_process_start_time(parent); } rcu_read_unlock(); } if (!nowait && child->fs) { // 'path_put' may sleep so have to do this only in '!nowait' mode struct path cwd = (struct path){}; get_fs_pwd(child->fs, &cwd); if (path_is_valid(&cwd)) { path_info_make_from_valid(&cwd_path_info, &cwd); } path_put(&cwd); event_size += SI_ESTIMATE_SIZE_PATH_INFO(cwd_path_info); } event_size += SI_CONTAINER_NAME_LIMIT; msg = msg_new_with_alloc_flags(FP_SI_OT_NOTIFY_PROCESS_FORK, 0, SI_CT_POST_CALLBACK, curr_unique_pid, event_size, nowait); if (msg) { si_property_writer_t writer; task_info_t* curr_task_info = task_info_map_get_with_alloc_flags(curr, exe, nowait); task_info_t* child_task_info = task_info_map_get_with_alloc_flags(child, exe, nowait); task_info_t* parent_task_info = has_parent ? task_info_map_add_with_alloc_flags(parent_tgid, parent_upid, NULL /*no exe update*/, parent_start_time.microseconds, nowait, false /*no force advance*/) : NULL; uint64_t child_unique_pid = make_unique_pid(child); uint64_t child_pid_version = child_task_info ? READ_ONCE(child_task_info->pid_version) : 0; uint64_t parent_pid_version = parent_task_info ? READ_ONCE(parent_task_info->pid_version) : 0; uint64_t event_uid = transport_global_sequence_next(); si_event_writer_init(&writer, &msg->event, event_size); si_property_writer_write_common(&writer, event_uid, curr->pid, curr->tgid, curr_task_info); si_property_writer_write_process_start_timestamp(&writer, make_process_start_time(curr)); si_property_writer_write_other_process_id(&writer, child->tgid); si_property_writer_write_other_thread_id(&writer, child->pid); si_property_writer_write_other_process_uid(&writer, child_unique_pid); si_property_writer_write_other_process_id_version(&writer, child_pid_version); si_property_writer_write_other_process_start_timestamp(&writer, make_process_start_time(child)); if (has_parent) { si_property_writer_write_parent_process_id(&writer, parent_tgid); si_property_writer_write_parent_thread_id(&writer, parent_pid); si_property_writer_write_parent_process_uid(&writer, parent_upid); si_property_writer_write_parent_process_id_version(&writer, parent_pid_version); si_property_writer_write_parent_process_start_timestamp(&writer, parent_start_time); } if (exe) si_property_writer_write_exe_file(&writer, &path_info, exe, &handle_info); else si_property_writer_write_exe_comm(&writer, comm); if (curr_task_info) { SiTimeMicroseconds t; t.microseconds = READ_ONCE(curr_task_info->artificial_start_time_us); si_property_writer_write_artificial_process_start_timestamp(&writer, t); task_info_put(curr_task_info); } if (child_task_info) { SiTimeMicroseconds t; t.microseconds = READ_ONCE(child_task_info->artificial_start_time_us); si_property_writer_write_other_artificial_process_start_timestamp(&writer, t); } if (parent_task_info) { SiTimeMicroseconds t; t.microseconds = READ_ONCE(parent_task_info->artificial_start_time_us); si_property_writer_write_parent_artificial_process_start_timestamp(&writer, t); task_info_put(parent_task_info); } si_property_writer_write_task_creds(&writer, child); si_property_writer_write_cgroup(&writer, child, SI_CONTAINER_NAME_LIMIT); if (cwd_path_info.buf) si_property_writer_write_current_working_directory(&writer, cwd_path_info.str); si_event_writer_finalize(&msg->event, &writer); msg->task_info = child_task_info; msg->fork.pid_version = child_pid_version; } file_handle_info_free(&handle_info); path_info_free(&path_info); path_info_free(&cwd_path_info); if (msg) { send_msg_async(msg); msg_unref(msg); } } static const uint8_t k_fork_fields_thread_creation_fields[] = { SI_COMMON_FIELDS, FP_SI_PI_OTHER_PROCESS_ID, FP_SI_PI_OTHER_THREAD_ID }; static void fork_event_thread_creation_with_alloc_flags(struct task_struct* curr, struct task_struct* child, bool nowait) { pid_t curr_tgid = curr->tgid; pid_t curr_pid = curr->pid; pid_t child_tgid = child->tgid; pid_t child_pid = child->pid; uint64_t curr_unique_pid = make_unique_pid(curr); const uint32_t event_size = SI_ESTIMATE_SIZE_CONST_PARAMS(k_fork_fields_thread_creation_fields); msg_t* msg = msg_new_with_alloc_flags(FP_SI_OT_NOTIFY_PROCESS_FORK, 0, SI_CT_POST_CALLBACK, curr_unique_pid, event_size, nowait); if (msg) { si_property_writer_t writer; task_info_t* curr_task_info = task_info_map_get_with_alloc_flags(curr, NULL /*no task refresh*/, nowait); uint64_t event_uid = transport_global_sequence_next(); si_event_writer_init(&writer, &msg->event, event_size); si_property_writer_write_common(&writer, event_uid, curr_pid, curr_tgid, curr_task_info); si_property_writer_write_other_process_id(&writer, child_tgid); si_property_writer_write_other_thread_id(&writer, child_pid); si_event_writer_finalize(&msg->event, &writer); // !!! Do not set 'curr_task_info' in msg because it may trigger refresh login in 'should_send' if (curr_task_info) { task_info_put(curr_task_info); } } if (msg) { send_msg_async(msg); msg_unref(msg); } } static void fork_event_with_alloc_flags(struct task_struct* curr, struct task_struct* child, bool nowait) { if (curr->tgid != child->tgid) { fork_event_process_creation_with_alloc_flags(curr, child, nowait); } else { fork_event_thread_creation_with_alloc_flags(curr, child, nowait); } } void fork_event_nowait(struct task_struct* parent, struct task_struct* child) { return fork_event_with_alloc_flags(parent, child, true /*nowait*/); } void fork_event(struct task_struct* child) { return fork_event_with_alloc_flags(current, child, false /*nowait*/); }