diff options
author | Ramji Jiyani <ramjiyani@google.com> | 2022-09-21 14:26:52 -0700 |
---|---|---|
committer | Ramji Jiyani <ramjiyani@google.com> | 2022-09-30 17:41:39 +0000 |
commit | e7451150cb701de01d6b836c496e1bd012d885cd (patch) | |
tree | 2f6ec7300a836ac90295d1b4d9963ac934e0e195 | |
parent | ea705b4ac32fd227e2becb3d0fd84843c16f8386 (diff) |
ANDROID: GKI: Add module load time symbol protection
Add CONFIG_MODULE_SIG_PROTECT to enable lookup for the unprotected
symbols from the build time generated list of symbols.
Module loading behavior will change as follows:
- Allows Android GKI Modules signed using MODULE_SIG_ALL during build.
- Allows other modules to load if they don't violate the access to
Android GKI protected symbols. Loading will fail and return
-EACCES (Permission denied) if these modules access the symbol which
is not allowlisted via symbol list or exported by a GKI module.
Bug: 232430739
Test: TH
Signed-off-by: Ramji Jiyani <ramjiyani@google.com>
Change-Id: I751b1951241b45712c20ac0e3878abd2152dd002
-rw-r--r-- | arch/arm64/configs/gki_defconfig | 2 | ||||
-rw-r--r-- | arch/x86/configs/gki_defconfig | 2 | ||||
-rw-r--r-- | init/Kconfig | 12 | ||||
-rw-r--r-- | kernel/Makefile | 12 | ||||
-rw-r--r-- | kernel/gki_module.c | 35 | ||||
-rw-r--r-- | kernel/module-internal.h | 9 | ||||
-rw-r--r-- | kernel/module.c | 39 | ||||
-rwxr-xr-x | scripts/gen_gki_modules_headers.sh | 94 |
8 files changed, 201 insertions, 4 deletions
diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index 03e12a4f175b..38178c5340f8 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -98,6 +98,8 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SCMVERSION=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_PROTECT=y CONFIG_BLK_DEV_ZONED=y CONFIG_BLK_CGROUP_IOCOST=y CONFIG_BLK_INLINE_ENCRYPTION=y diff --git a/arch/x86/configs/gki_defconfig b/arch/x86/configs/gki_defconfig index 3da32df3869e..526f5458b7e2 100644 --- a/arch/x86/configs/gki_defconfig +++ b/arch/x86/configs/gki_defconfig @@ -79,6 +79,8 @@ CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SCMVERSION=y +CONFIG_MODULE_SIG=y +CONFIG_MODULE_SIG_PROTECT=y CONFIG_BLK_DEV_ZONED=y CONFIG_BLK_CGROUP_IOCOST=y CONFIG_BLK_INLINE_ENCRYPTION=y diff --git a/init/Kconfig b/init/Kconfig index e10bd5748a83..ed153ba4d2fe 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -2205,6 +2205,18 @@ config MODULE_SIG_FORCE Reject unsigned modules or signed modules for which we don't have a key. Without this, such modules will simply taint the kernel. +config MODULE_SIG_PROTECT + bool "Android GKI module protection" + depends on MODULE_SIG && !MODULE_SIG_FORCE + help + Enables Android GKI symbol protection support. + + This modifies the behavior of the MODULE_SIG_FORCE as follows: + - Allows Android GKI Modules signed using MODULE_SIG_ALL during build. + - Allows other modules to load if they don't violate the access to + Android GKI protected symbols. Loading will fail and return + -EACCES (Permission denied) if symbol access contidions are not met. + config MODULE_SIG_ALL bool "Automatically sign all modules" default y diff --git a/kernel/Makefile b/kernel/Makefile index 0e119c52a2cd..d50fd85b9abb 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SIG) += module_signing.o obj-$(CONFIG_MODULE_SIG_FORMAT) += module_signature.o +obj-$(CONFIG_MODULE_SIG_PROTECT) += gki_module.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_CRASH_CORE) += crash_core.o @@ -160,3 +161,14 @@ $(obj)/kheaders_data.tar.xz: FORCE $(call cmd,genikh) clean-files := kheaders_data.tar.xz kheaders.md5 + +# +# ANDROID: GKI: Generate headerfile required for gki_module.o +# +# Dependencies on generated files need to be listed explicitly +$(obj)/gki_module.o: $(obj)/gki_module_unprotected.h + +$(obj)/gki_module_unprotected.h: $(srctree)/scripts/gen_gki_modules_headers.sh \ + $(if $(wildcard ${OUT_DIR}/abi_symbollist.raw), ${OUT_DIR}/abi_symbollist.raw) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/gen_gki_modules_headers.sh $@ \ + "$(srctree)" diff --git a/kernel/gki_module.c b/kernel/gki_module.c new file mode 100644 index 000000000000..04989540b7b4 --- /dev/null +++ b/kernel/gki_module.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2022 Google LLC + * Author: ramjiyani@google.com (Ramji Jiyani) + */ + +#include <linux/bsearch.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/string.h> + +/* + * Build time generated header files + * + * gki_module_unprotected.h -- Symbols allowed to _access_ by unsigned modules + */ +#include "gki_module_unprotected.h" + +/* bsearch() comparision callback */ +static int cmp_name(const void *sym, const void *protected_sym) +{ + return strncmp(sym, protected_sym, MAX_UNPROTECTED_NAME_LEN); +} + +/** + * gki_is_module_unprotected_symbol - Is a symbol unprotected for unsigned module? + * + * @name: Symbol being checked in list of unprotected symbols + */ +bool gki_is_module_unprotected_symbol(const char *name) +{ + return bsearch(name, gki_unprotected_symbols, NO_OF_UNPROTECTED_SYMBOLS, + MAX_UNPROTECTED_NAME_LEN, cmp_name) != NULL; +} diff --git a/kernel/module-internal.h b/kernel/module-internal.h index 33783abc377b..2a8e3a2062e8 100644 --- a/kernel/module-internal.h +++ b/kernel/module-internal.h @@ -29,3 +29,12 @@ struct load_info { }; extern int mod_verify_sig(const void *mod, struct load_info *info); + +#ifdef CONFIG_MODULE_SIG_PROTECT +extern bool gki_is_module_unprotected_symbol(const char *name); +#else +static inline bool gki_is_module_unprotected_symbol(const char *name) +{ + return 1; +} +#endif /* CONFIG_MODULE_SIG_PROTECT */ diff --git a/kernel/module.c b/kernel/module.c index f5c6e0a3a877..1a08745b84c0 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -271,7 +271,7 @@ static void module_assert_mutex_or_preempt(void) #endif } -#ifdef CONFIG_MODULE_SIG +#if defined(CONFIG_MODULE_SIG) && !defined(CONFIG_MODULE_SIG_PROTECT) static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE); module_param(sig_enforce, bool_enable_only, 0644); @@ -1440,6 +1440,21 @@ static const struct kernel_symbol *resolve_symbol(struct module *mod, goto getname; } + /* + * ANDROID: GKI: + * In case of an unsigned module symbol resolves only if: + * 1. Symbol is in the list of unprotected symbol list OR + * 2. If symbol owner is not NULL i.e. owner is another module; + * it has to be an unsigned module and not signed GKI module + * to protect symbols exported by signed GKI modules. + */ + if (!mod->sig_ok && + !gki_is_module_unprotected_symbol(name) && + fsa.owner && fsa.owner->sig_ok) { + fsa.sym = ERR_PTR(-EACCES); + goto getname; + } + err = ref_module(mod, fsa.owner); if (err) { fsa.sym = ERR_PTR(err); @@ -2347,9 +2362,15 @@ static int simplify_symbols(struct module *mod, const struct load_info *info) ignore_undef_symbol(info->hdr->e_machine, name))) break; - ret = PTR_ERR(ksym) ?: -ENOENT; - pr_warn("%s: Unknown symbol %s (err %d)\n", - mod->name, name, ret); + if (PTR_ERR(ksym) == -EACCES) { + ret = -EACCES; + pr_warn("%s: Protected symbol: %s (err %d)\n", + mod->name, name, ret); + } else { + ret = PTR_ERR(ksym) ?: -ENOENT; + pr_warn("%s: Unknown symbol %s (err %d)\n", + mod->name, name, ret); + } break; default: @@ -2945,7 +2966,15 @@ static int module_sig_check(struct load_info *info, int flags) return -EKEYREJECTED; } +/* + * ANDROID: GKI: Do not prevent loading of unsigned modules; + * as all modules except GKI modules are not signed. + */ +#ifndef CONFIG_MODULE_SIG_PROTECT return security_locked_down(LOCKDOWN_MODULE_SIGNATURE); +#else + return 0; +#endif } #else /* !CONFIG_MODULE_SIG */ static int module_sig_check(struct load_info *info, int flags) @@ -4045,6 +4074,8 @@ static int load_module(struct load_info *info, const char __user *uargs, "kernel\n", mod->name); add_taint_module(mod, TAINT_UNSIGNED_MODULE, LOCKDEP_STILL_OK); } +#else + mod->sig_ok = 0; #endif /* To avoid stressing percpu allocator, do this once we're unique. */ diff --git a/scripts/gen_gki_modules_headers.sh b/scripts/gen_gki_modules_headers.sh new file mode 100755 index 000000000000..b9ab5dcc4c75 --- /dev/null +++ b/scripts/gen_gki_modules_headers.sh @@ -0,0 +1,94 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright 2022 Google LLC +# Author: ramjiyani@google.com (Ramji Jiyani) +# + +# +# Generates hearder file with list of unprotected symbols +# +# Called By: KERNEL_SRC/kernel/Makefile if CONFIG_MODULE_SIG_PROTECT=y +# +# gki_module_unprotected.h: Symbols allowed to _access_ by unsigned modules +# +# If valid symbol file doesn't exists then still generates valid C header files for +# compilation to proceed with no symbols to protect +# + +# Collect arguments from Makefile +TARGET=$1 +SRCTREE=$2 + +set -e + +# +# Common Definitions +# +# Use "make V=1" to debug this script. +case "$KBUILD_VERBOSE" in +*1*) + set -x + ;; +esac + +# +# generate_header(): +# Args: $1 = Name of the header file +# $2 = Input symbol list +# $3 = Symbol type (protected/exported) +# +generate_header() { + local header_file=$1 + local symbol_file=$2 + local symbol_type=$3 + + if [ -f "${header_file}" ]; then + rm -f -- "${header_file}" + fi + + # Find Maximum symbol name length if valid symbol_file exist + if [ -s "${symbol_file}" ]; then + # Skip 1st line (symbol header), Trim white spaces & +1 for null termination + local max_name_len=$(awk ' + { + $1=$1; + if ( length > L && NR > 1) { + L=length + } + } END { print ++L }' "${symbol_file}") + else + # Set to 1 to generate valid C header file + local max_name_len=1 + fi + + # Header generation + cat > "${header_file}" <<- EOT + /* + * DO NOT EDIT + * + * Build generated header file with unprotected symbols/exports + */ + + #define NO_OF_$(printf ${symbol_type} | tr [:lower:] [:upper:])_SYMBOLS \\ + $(printf '\t')(sizeof(gki_${symbol_type}_symbols) / sizeof(gki_${symbol_type}_symbols[0])) + #define MAX_$(printf ${symbol_type} | tr [:lower:] [:upper:])_NAME_LEN (${max_name_len}) + + static const char gki_${symbol_type}_symbols[][MAX_$(printf ${symbol_type} | + tr [:lower:] [:upper:])_NAME_LEN] = { + EOT + + # If a valid symbol_file present add symbols in an array except the 1st line + if [ -s "${symbol_file}" ]; then + sed -e 's/^[ \t]*/\t"/;s/[ \t]*$/",/' "${symbol_file}" >> "${header_file}" + fi + + # Terminate the file + echo "};" >> "${header_file}" +} + +# Sorted list of vendor symbols +GKI_VENDOR_SYMBOLS="${OUT_DIR}/abi_symbollist.raw" + +generate_header "${TARGET}" "${GKI_VENDOR_SYMBOLS}" "unprotected" + |