Commit b64fcae7 authored by Kees Cook's avatar Kees Cook Committed by Greg Kroah-Hartman
Browse files

LSM: Introduce kernel_post_load_data() hook



There are a few places in the kernel where LSMs would like to have
visibility into the contents of a kernel buffer that has been loaded or
read. While security_kernel_post_read_file() (which includes the
buffer) exists as a pairing for security_kernel_read_file(), no such
hook exists to pair with security_kernel_load_data().

Earlier proposals for just using security_kernel_post_read_file() with a
NULL file argument were rejected (i.e. "file" should always be valid for
the security_..._file hooks, but it appears at least one case was
left in the kernel during earlier refactoring. (This will be fixed in
a subsequent patch.)

Since not all cases of security_kernel_load_data() can have a single
contiguous buffer made available to the LSM hook (e.g. kexec image
segments are separately loaded), there needs to be a way for the LSM to
reason about its expectations of the hook coverage. In order to handle
this, add a "contents" argument to the "kernel_load_data" hook that
indicates if the newly added "kernel_post_load_data" hook will be called
with the full contents once loaded. That way, LSMs requiring full contents
can choose to unilaterally reject "kernel_load_data" with contents=false
(which is effectively the existing hook coverage), but when contents=true
they can allow it and later evaluate the "kernel_post_load_data" hook
once the buffer is loaded.

With this change, LSMs can gain coverage over non-file-backed data loads
(e.g. init_module(2) and firmware userspace helper), which will happen
in subsequent patches.

Additionally prepare IMA to start processing these cases.

Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Reviewed-by: default avatarKP Singh <kpsingh@google.com>
Link: https://lore.kernel.org/r/20201002173828.2099543-9-keescook@chromium.org


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 88535288
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -613,7 +613,7 @@ static bool fw_run_sysfs_fallback(u32 opt_flags)
		return false;

	/* Also permit LSMs and IMA to fail firmware sysfs fallback */
	ret = security_kernel_load_data(LOADING_FIRMWARE);
	ret = security_kernel_load_data(LOADING_FIRMWARE, false);
	if (ret < 0)
		return false;

+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
	if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM))
		return -ENOENT;

	rc = security_kernel_load_data(LOADING_FIRMWARE);
	rc = security_kernel_load_data(LOADING_FIRMWARE, false);
	if (rc)
		return rc;

+11 −2
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ extern void ima_post_create_tmpfile(struct inode *inode);
extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot);
extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot);
extern int ima_load_data(enum kernel_load_data_id id);
extern int ima_load_data(enum kernel_load_data_id id, bool contents);
extern int ima_post_load_data(char *buf, loff_t size,
			      enum kernel_load_data_id id, char *description);
extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
			      enum kernel_read_file_id id);
@@ -78,7 +80,14 @@ static inline int ima_file_mprotect(struct vm_area_struct *vma,
	return 0;
}

static inline int ima_load_data(enum kernel_load_data_id id)
static inline int ima_load_data(enum kernel_load_data_id id, bool contents)
{
	return 0;
}

static inline int ima_post_load_data(char *buf, loff_t size,
				     enum kernel_load_data_id id,
				     char *description)
{
	return 0;
}
+3 −1
Original line number Diff line number Diff line
@@ -184,7 +184,9 @@ LSM_HOOK(void, LSM_RET_VOID, cred_getsecid, const struct cred *c, u32 *secid)
LSM_HOOK(int, 0, kernel_act_as, struct cred *new, u32 secid)
LSM_HOOK(int, 0, kernel_create_files_as, struct cred *new, struct inode *inode)
LSM_HOOK(int, 0, kernel_module_request, char *kmod_name)
LSM_HOOK(int, 0, kernel_load_data, enum kernel_load_data_id id)
LSM_HOOK(int, 0, kernel_load_data, enum kernel_load_data_id id, bool contents)
LSM_HOOK(int, 0, kernel_post_load_data, char *buf, loff_t size,
	 enum kernel_read_file_id id, char *description)
LSM_HOOK(int, 0, kernel_read_file, struct file *file,
	 enum kernel_read_file_id id)
LSM_HOOK(int, 0, kernel_post_read_file, struct file *file, char *buf,
+10 −0
Original line number Diff line number Diff line
@@ -635,7 +635,17 @@
 * @kernel_load_data:
 *	Load data provided by userspace.
 *	@id kernel load data identifier
 *	@contents if a subsequent @kernel_post_load_data will be called.
 *	Return 0 if permission is granted.
 * @kernel_post_load_data:
 *	Load data provided by a non-file source (usually userspace buffer).
 *	@buf pointer to buffer containing the data contents.
 *	@size length of the data contents.
 *	@id kernel load data identifier
 *	@description a text description of what was loaded, @id-specific
 *	Return 0 if permission is granted.
 *	This must be paired with a prior @kernel_load_data call that had
 *	@contents set to true.
 * @kernel_read_file:
 *	Read a file specified by userspace.
 *	@file contains the file structure pointing to the file being read
Loading