| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Ramji Jiyani <ramjiyani@google.com> |
| Date: Thu, 25 Nov 2021 00:51:55 +0000 |
| Subject: ANDROID: GKI: Add script to generate symbol protection headers |
| |
| Called By: KERNEL_SRC/kernel/Makefile if CONFIG_MODULE_SIG_PROTECT=y |
| |
| Generates headers required by gki_modules.c from symbol lists: |
| |
| gki_module_protected.h: from android/abi_gki_modules_protected |
| gki_module_exported.h: from android/abi_gki_modules_exports |
| |
| Bug: 200082547 |
| Test: Treehugger |
| Signed-off-by: Ramji Jiyani <ramjiyani@google.com> |
| Change-Id: Ibcc6e6fe0ad6c7850d48f7c0a283c7f9b06e4456 |
| (cherry picked from commit 23cd26aab14d813fd73eced18988bae06d5b9334) |
| (cherry picked from commit 31d5735baf0b20dd6a90bd8fcd1fa73a6531a62b) |
| Signed-off-by: Lee Jones <joneslee@google.com> |
| --- |
| android/abi_gki_protected_exports | 0 |
| android/gki_protected_modules | 47 ++++++++++++++ |
| kernel/.gitignore | 2 + |
| kernel/module/Kconfig | 13 ++++ |
| kernel/module/Makefile | 18 +++++ |
| kernel/module/gki_module.c | 68 +++++++++++++++++++ |
| kernel/module/internal.h | 14 ++++ |
| kernel/module/main.c | 37 ++++++++++- |
| kernel/module/signing.c | 25 +++++-- |
| scripts/gen_gki_modules_headers.sh | 101 +++++++++++++++++++++++++++++ |
| 10 files changed, 317 insertions(+), 8 deletions(-) |
| create mode 100644 android/abi_gki_protected_exports |
| create mode 100644 android/gki_protected_modules |
| create mode 100644 kernel/module/gki_module.c |
| create mode 100755 scripts/gen_gki_modules_headers.sh |
| |
| diff --git a/android/abi_gki_protected_exports b/android/abi_gki_protected_exports |
| new file mode 100644 |
| diff --git a/android/gki_protected_modules b/android/gki_protected_modules |
| new file mode 100644 |
| --- /dev/null |
| +++ b/android/gki_protected_modules |
| @@ -0,0 +1,47 @@ |
| +drivers/bluetooth/btbcm.ko |
| +drivers/bluetooth/btqca.ko |
| +drivers/bluetooth/btsdio.ko |
| +drivers/bluetooth/hci_uart.ko |
| +drivers/net/can/dev/can-dev.ko |
| +drivers/net/can/slcan/slcan.ko |
| +drivers/net/can/vcan.ko |
| +drivers/net/ppp/bsd_comp.ko |
| +drivers/net/ppp/ppp_deflate.ko |
| +drivers/net/ppp/ppp_generic.ko |
| +drivers/net/ppp/ppp_mppe.ko |
| +drivers/net/ppp/pppox.ko |
| +drivers/net/ppp/pptp.ko |
| +drivers/net/slip/slhc.ko |
| +drivers/usb/class/cdc-acm.ko |
| +drivers/usb/serial/ftdi_sio.ko |
| +drivers/usb/serial/usbserial.ko |
| +lib/crypto/libarc4.ko |
| +net/6lowpan/6lowpan.ko |
| +net/6lowpan/nhc_dest.ko |
| +net/6lowpan/nhc_fragment.ko |
| +net/6lowpan/nhc_hop.ko |
| +net/6lowpan/nhc_ipv6.ko |
| +net/6lowpan/nhc_mobility.ko |
| +net/6lowpan/nhc_routing.ko |
| +net/6lowpan/nhc_udp.ko |
| +net/8021q/8021q.ko |
| +net/bluetooth/bluetooth.ko |
| +net/bluetooth/hidp/hidp.ko |
| +net/bluetooth/rfcomm/rfcomm.ko |
| +net/can/can.ko |
| +net/can/can-bcm.ko |
| +net/can/can-gw.ko |
| +net/can/can-raw.ko |
| +net/ieee802154/6lowpan/ieee802154_6lowpan.ko |
| +net/ieee802154/ieee802154.ko |
| +net/ieee802154/ieee802154_socket.ko |
| +net/l2tp/l2tp_core.ko |
| +net/l2tp/l2tp_ppp.ko |
| +net/mac80211/mac80211.ko |
| +net/mac802154/mac802154.ko |
| +net/nfc/nfc.ko |
| +net/rfkill/rfkill.ko |
| +net/tipc/diag.ko |
| +net/tipc/tipc.ko |
| +net/wireless/cfg80211.ko |
| + |
| diff --git a/kernel/.gitignore b/kernel/.gitignore |
| --- a/kernel/.gitignore |
| +++ b/kernel/.gitignore |
| @@ -1,3 +1,5 @@ |
| # SPDX-License-Identifier: GPL-2.0-only |
| /config_data |
| /kheaders.md5 |
| +/gki_module_exported.h |
| +/gki_module_protected.h |
| diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig |
| --- a/kernel/module/Kconfig |
| +++ b/kernel/module/Kconfig |
| @@ -117,6 +117,19 @@ 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 and export 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 and do not export the symbols already |
| + exported by the Android GKI modules. Loading will fail and return |
| + -EACCES (Permission denied) if symbol access conditions are not met. |
| + |
| config MODULE_SIG_ALL |
| bool "Automatically sign all modules" |
| default y |
| diff --git a/kernel/module/Makefile b/kernel/module/Makefile |
| --- a/kernel/module/Makefile |
| +++ b/kernel/module/Makefile |
| @@ -10,6 +10,7 @@ KCOV_INSTRUMENT_module.o := n |
| obj-y += main.o strict_rwx.o |
| obj-$(CONFIG_MODULE_DECOMPRESS) += decompress.o |
| obj-$(CONFIG_MODULE_SIG) += signing.o |
| +obj-$(CONFIG_MODULE_SIG_PROTECT) += gki_module.o |
| obj-$(CONFIG_LIVEPATCH) += livepatch.o |
| obj-$(CONFIG_MODULES_TREE_LOOKUP) += tree_lookup.o |
| obj-$(CONFIG_DEBUG_KMEMLEAK) += debug_kmemleak.o |
| @@ -19,3 +20,20 @@ obj-$(CONFIG_SYSFS) += sysfs.o |
| obj-$(CONFIG_KGDB_KDB) += kdb.o |
| obj-$(CONFIG_MODVERSIONS) += version.o |
| obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o |
| + |
| +# |
| +# 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_protected_exports.h \ |
| + $(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)" |
| + |
| +$(obj)/gki_module_protected_exports.h: $(srctree)/android/abi_gki_protected_exports \ |
| + $(srctree)/scripts/gen_gki_modules_headers.sh |
| + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/gen_gki_modules_headers.sh $@ \ |
| + "$(srctree)" |
| diff --git a/kernel/module/gki_module.c b/kernel/module/gki_module.c |
| new file mode 100644 |
| --- /dev/null |
| +++ b/kernel/module/gki_module.c |
| @@ -0,0 +1,68 @@ |
| +// 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_protected_exports.h -- Symbols protected from _export_ by unsigned modules |
| + * gki_module_unprotected.h -- Symbols allowed to _access_ by unsigned modules |
| + */ |
| +#include "gki_module_protected_exports.h" |
| +#include "gki_module_unprotected.h" |
| + |
| +#define MAX_STRCMP_LEN (max(MAX_UNPROTECTED_NAME_LEN, MAX_PROTECTED_EXPORTS_NAME_LEN)) |
| + |
| +/* bsearch() comparision callback */ |
| +static int cmp_name(const void *sym, const void *protected_sym) |
| +{ |
| + return strncmp(sym, protected_sym, MAX_STRCMP_LEN); |
| +} |
| + |
| +/** |
| + * gki_is_module_protected_export - Is a symbol exported from a protected GKI module? |
| + * |
| + * @name: Symbol being checked against exported symbols from protected GKI modules |
| + */ |
| +bool gki_is_module_protected_export(const char *name) |
| +{ |
| + if (NR_UNPROTECTED_SYMBOLS) { |
| + return bsearch(name, gki_protected_exports_symbols, NR_PROTECTED_EXPORTS_SYMBOLS, |
| + MAX_PROTECTED_EXPORTS_NAME_LEN, cmp_name) != NULL; |
| + } else { |
| + /* |
| + * If there are no symbols in unprotected list; We don't need to |
| + * protect exports as there is no KMI enforcement. |
| + * Treat everything exportable in this case. |
| + */ |
| + return false; |
| + } |
| +} |
| + |
| +/** |
| + * 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) |
| +{ |
| + if (NR_UNPROTECTED_SYMBOLS) { |
| + return bsearch(name, gki_unprotected_symbols, NR_UNPROTECTED_SYMBOLS, |
| + MAX_UNPROTECTED_NAME_LEN, cmp_name) != NULL; |
| + } else { |
| + /* |
| + * If there are no symbols in unprotected list; |
| + * there isn't a KMI enforcement for the kernel. |
| + * Treat everything accessible in this case. |
| + */ |
| + return true; |
| + } |
| +} |
| diff --git a/kernel/module/internal.h b/kernel/module/internal.h |
| --- a/kernel/module/internal.h |
| +++ b/kernel/module/internal.h |
| @@ -303,3 +303,17 @@ static inline int same_magic(const char *amagic, const char *bmagic, bool has_cr |
| return strcmp(amagic, bmagic) == 0; |
| } |
| #endif /* CONFIG_MODVERSIONS */ |
| + |
| +#ifdef CONFIG_MODULE_SIG_PROTECT |
| +extern bool gki_is_module_unprotected_symbol(const char *name); |
| +extern bool gki_is_module_protected_export(const char *name); |
| +#else |
| +static inline bool gki_is_module_unprotected_symbol(const char *name) |
| +{ |
| + return true; |
| +} |
| +static inline bool gki_is_module_protected_export(const char *name) |
| +{ |
| + return false; |
| +} |
| +#endif /* CONFIG_MODULE_SIG_PROTECT */ |
| diff --git a/kernel/module/main.c b/kernel/module/main.c |
| --- a/kernel/module/main.c |
| +++ b/kernel/module/main.c |
| @@ -1095,6 +1095,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); |
| @@ -1247,6 +1262,14 @@ static int verify_exported_symbols(struct module *mod) |
| .name = kernel_symbol_name(s), |
| .gplok = true, |
| }; |
| + |
| + if (!mod->sig_ok && gki_is_module_protected_export( |
| + kernel_symbol_name(s))) { |
| + pr_err("%s: exports protected symbol %s\n", |
| + mod->name, kernel_symbol_name(s)); |
| + return -EACCES; |
| + } |
| + |
| if (find_symbol(&fsa)) { |
| pr_err("%s: exports duplicate symbol %s" |
| " (owned by %s)\n", |
| @@ -1327,9 +1350,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: |
| @@ -2749,6 +2778,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/kernel/module/signing.c b/kernel/module/signing.c |
| --- a/kernel/module/signing.c |
| +++ b/kernel/module/signing.c |
| @@ -19,8 +19,20 @@ |
| #undef MODULE_PARAM_PREFIX |
| #define MODULE_PARAM_PREFIX "module." |
| |
| +/* |
| + * ANDROID: GKI: |
| + * Only enforce signature if SIG_PROTECT is not set |
| + */ |
| +#ifndef CONFIG_MODULE_SIG_PROTECT |
| static bool sig_enforce = IS_ENABLED(CONFIG_MODULE_SIG_FORCE); |
| module_param(sig_enforce, bool_enable_only, 0644); |
| +void set_module_sig_enforced(void) |
| +{ |
| + sig_enforce = true; |
| +} |
| +#else |
| +#define sig_enforce false |
| +#endif |
| |
| /* |
| * Export sig_enforce kernel cmdline parameter to allow other subsystems rely |
| @@ -32,11 +44,6 @@ bool is_module_sig_enforced(void) |
| } |
| EXPORT_SYMBOL(is_module_sig_enforced); |
| |
| -void set_module_sig_enforced(void) |
| -{ |
| - sig_enforce = true; |
| -} |
| - |
| /* |
| * Verify the signature on a module. |
| */ |
| @@ -121,5 +128,13 @@ 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 |
| } |
| diff --git a/scripts/gen_gki_modules_headers.sh b/scripts/gen_gki_modules_headers.sh |
| new file mode 100755 |
| --- /dev/null |
| +++ b/scripts/gen_gki_modules_headers.sh |
| @@ -0,0 +1,101 @@ |
| +#!/bin/bash |
| +# SPDX-License-Identifier: GPL-2.0-only |
| +# |
| +# Copyright 2022 Google LLC |
| +# Author: ramjiyani@google.com (Ramji Jiyani) |
| +# |
| + |
| +# |
| +# Generates header 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 ("unprotected") |
| +# |
| +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 |
| + # Trim white spaces & +1 for null termination |
| + local max_name_len=$(awk ' |
| + { |
| + $1=$1; |
| + if ( length > L ) { |
| + 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 ${symbol_type} |
| + */ |
| + |
| + #define NR_$(printf ${symbol_type} | tr [:lower:] [:upper:])_SYMBOLS \\ |
| + $(printf '\t')(ARRAY_SIZE(gki_${symbol_type}_symbols)) |
| + #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}" |
| +} |
| + |
| +if [ "$(basename "${TARGET}")" = "gki_module_unprotected.h" ]; then |
| + # Sorted list of vendor symbols |
| + GKI_VENDOR_SYMBOLS="${OUT_DIR}/abi_symbollist.raw" |
| + |
| + generate_header "${TARGET}" "${GKI_VENDOR_SYMBOLS}" "unprotected" |
| +else |
| + # Sorted list of exported symbols |
| + GKI_EXPORTED_SYMBOLS="${SRCTREE}/android/abi_gki_protected_exports" |
| + |
| + generate_header "${TARGET}" "${GKI_EXPORTED_SYMBOLS}" "protected_exports" |
| +fi |
| + |