diff options
Diffstat (limited to 'security/landlock/domain.c')
-rw-r--r-- | security/landlock/domain.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/security/landlock/domain.c b/security/landlock/domain.c index e6367933303c..3c1715e4b1c9 100644 --- a/security/landlock/domain.c +++ b/security/landlock/domain.c @@ -7,21 +7,122 @@ * Copyright © 2024-2025 Microsoft Corporation */ +#include <linux/cred.h> +#include <linux/file.h> +#include <linux/mm.h> +#include <linux/path.h> +#include <linux/pid.h> +#include <linux/sched.h> +#include <linux/uidgid.h> + #include "domain.h" #include "id.h" #ifdef CONFIG_AUDIT /** + * get_current_exe - Get the current's executable path, if any + * + * @exe_str: Returned pointer to a path string with a lifetime tied to the + * returned buffer, if any. + * @exe_size: Returned size of @exe_str (including the trailing null + * character), if any. + * + * Returns: A pointer to an allocated buffer where @exe_str point to, %NULL if + * there is no executable path, or an error otherwise. + */ +static const void *get_current_exe(const char **const exe_str, + size_t *const exe_size) +{ + const size_t buffer_size = LANDLOCK_PATH_MAX_SIZE; + struct mm_struct *mm = current->mm; + struct file *file __free(fput) = NULL; + char *buffer __free(kfree) = NULL; + const char *exe; + ssize_t size; + + if (!mm) + return NULL; + + file = get_mm_exe_file(mm); + if (!file) + return NULL; + + buffer = kmalloc(buffer_size, GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + exe = d_path(&file->f_path, buffer, buffer_size); + if (WARN_ON_ONCE(IS_ERR(exe))) + /* Should never happen according to LANDLOCK_PATH_MAX_SIZE. */ + return ERR_CAST(exe); + + size = buffer + buffer_size - exe; + if (WARN_ON_ONCE(size <= 0)) + return ERR_PTR(-ENAMETOOLONG); + + *exe_size = size; + *exe_str = exe; + return no_free_ptr(buffer); +} + +/* + * Returns: A newly allocated object describing a domain, or an error + * otherwise. + */ +static struct landlock_details *get_current_details(void) +{ + /* Cf. audit_log_d_path_exe() */ + static const char null_path[] = "(null)"; + const char *path_str = null_path; + size_t path_size = sizeof(null_path); + const void *buffer __free(kfree) = NULL; + struct landlock_details *details; + + buffer = get_current_exe(&path_str, &path_size); + if (IS_ERR(buffer)) + return ERR_CAST(buffer); + + /* + * Create the new details according to the path's length. Do not + * allocate with GFP_KERNEL_ACCOUNT because it is independent from the + * caller. + */ + details = + kzalloc(struct_size(details, exe_path, path_size), GFP_KERNEL); + if (!details) + return ERR_PTR(-ENOMEM); + + memcpy(details->exe_path, path_str, path_size); + WARN_ON_ONCE(current_cred() != current_real_cred()); + details->pid = get_pid(task_pid(current)); + details->uid = from_kuid(&init_user_ns, current_uid()); + get_task_comm(details->comm, current); + return details; +} + +/** * landlock_init_hierarchy_log - Partially initialize landlock_hierarchy * * @hierarchy: The hierarchy to initialize. * + * The current task is referenced as the domain that is enforcing the + * restriction. The subjective credentials must not be in an overridden state. + * * @hierarchy->parent and @hierarchy->usage should already be set. */ int landlock_init_hierarchy_log(struct landlock_hierarchy *const hierarchy) { + struct landlock_details *details; + + details = get_current_details(); + if (IS_ERR(details)) + return PTR_ERR(details); + + hierarchy->details = details; hierarchy->id = landlock_get_id_range(1); + hierarchy->log_status = LANDLOCK_LOG_PENDING; + atomic64_set(&hierarchy->num_denials, 0); return 0; } |