[PATCH 4/4] module: Support to disable validity enforcement in runtime
In order to disable the module validity enforcement, writing a PKCS#7 signature corresponding the signed content '0' is required. Given a simple way to archive this: $ echo -n 0 > data $ openssl smime -sign -nocerts -noattr -binary -in data \ -inkey -signer -outform der \ -out data.sig Note that the signing key must be a trust key located in system trusted keyring. So even the root privilige cannot simply disable the enforcement. Signed-off-by: Jia Zhang--- kernel/module.c | 118 ++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 6b032577..16be198 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include "module-internal.h" @@ -288,6 +289,11 @@ bool is_module_sig_enforced(void) } EXPORT_SYMBOL(is_module_sig_enforced); +static void set_module_sig_enforce(bool enforce) +{ + sig_enforce = enforce; +} + /* Block module loading/unloading? */ int modules_disabled = 0; core_param(nomodule, modules_disabled, bint, 0); @@ -2796,6 +2802,61 @@ static int module_sig_check(struct load_info *info, int flags) } #ifdef CONFIG_SECURITYFS +/* + * Check the intention of setting the enforcement policy. + * + * Return 1 if enabling the policy, or return 0 if disabling + * the policy. Note that the root privilege cannot simply + * disable the policy without the authentication given by a + * trusted key. + */ +static int check_enforce(char *buf, size_t count) +{ + u8 *p; + + if (buf[0] == '1') { + if (count == 1 || (count == 2 && buf[1] == '\n')) + return 1; + + return -EINVAL; + } + + /* +* In order to disable the enforcement policy, a PKCS#7 signature +* is supplied. +* +* Assuming ASN.1 encoding supplied, the minimal length would be +* 4-byte header plus at least 256-byte payload. +*/ + if (count < 260) + return -EINVAL; + + p = (u8 *)buf; + + /* The primitive type must be a sequnce */ + if (p[0] != 0x30 || p[1] != 0x82) + return -EINVAL; + + /* Match up the length of the supplied buffer */ + if (be16_to_cpup((__be16 *)(p + 2)) != count - 4) + return -EINVAL; + + return 0; +} + +/* + * Disable the enforceme and verify the supplied PKCS#7 signature. + * The signed content is simply the charactoror '0'. + */ +static int disable_enforce(void *pkcs7, size_t pkcs7_len) +{ + char data = '0'; + + return verify_pkcs7_signature(, sizeof(data), pkcs7, pkcs7_len, + NULL, VERIFYING_UNSPECIFIED_SIGNATURE, + NULL, NULL); +} + static ssize_t modsign_enforce_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp) { @@ -2806,7 +2867,50 @@ static ssize_t modsign_enforce_read(struct file *filp, char __user *ubuf, return simple_read_from_buffer(ubuf, count, offp, buf, 1); } -static const struct file_operations modsign_enforce_ops = { +static ssize_t modsign_enforce_write(struct file *filp, +const char __user *ubuf, +size_t count, loff_t *offp) +{ + char *buf; + ssize_t ret; + size_t max_buf_size = 1 << MAX_ORDER; + + if (*offp > 1) + return -EFBIG; + + if (count > max_buf_size) + return -EFBIG; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, offp, ubuf, count); + if (ret <= 0) { + kfree(buf); + return ret; + } + + ret = check_enforce(buf, count); + if (is_module_sig_enforced() && !ret) { + ret = disable_enforce(buf, count); + if (!ret) { + set_module_sig_enforce(false); + pr_notice("Kernel module validity enforcement disabled\n"); + ret = count; + } + } else if (!is_module_sig_enforced() && ret == 1) { + set_module_sig_enforce(true); + pr_notice("Kernel module validity enforcement enabled\n"); + ret = count; + } + + kfree(buf); + + return ret; +} + +static struct file_operations modsign_enforce_ops = { .read = modsign_enforce_read, .llseek = generic_file_llseek, }; @@ -2815,14 +2919,20 @@ static int __init securityfs_init(void) { struct dentry *modsign_dir; struct dentry *enforce; + umode_t mode; modsign_dir = securityfs_create_dir("modsign", NULL); if (IS_ERR(modsign_dir)) return -1; - enforce =
[PATCH 4/4] module: Support to disable validity enforcement in runtime
In order to disable the module validity enforcement, writing a PKCS#7 signature corresponding the signed content '0' is required. Given a simple way to archive this: $ echo -n 0 > data $ openssl smime -sign -nocerts -noattr -binary -in data \ -inkey -signer -outform der \ -out data.sig Note that the signing key must be a trust key located in system trusted keyring. So even the root privilige cannot simply disable the enforcement. Signed-off-by: Jia Zhang --- kernel/module.c | 118 ++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/kernel/module.c b/kernel/module.c index 6b032577..16be198 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -64,6 +64,7 @@ #include #include #include +#include #include #include "module-internal.h" @@ -288,6 +289,11 @@ bool is_module_sig_enforced(void) } EXPORT_SYMBOL(is_module_sig_enforced); +static void set_module_sig_enforce(bool enforce) +{ + sig_enforce = enforce; +} + /* Block module loading/unloading? */ int modules_disabled = 0; core_param(nomodule, modules_disabled, bint, 0); @@ -2796,6 +2802,61 @@ static int module_sig_check(struct load_info *info, int flags) } #ifdef CONFIG_SECURITYFS +/* + * Check the intention of setting the enforcement policy. + * + * Return 1 if enabling the policy, or return 0 if disabling + * the policy. Note that the root privilege cannot simply + * disable the policy without the authentication given by a + * trusted key. + */ +static int check_enforce(char *buf, size_t count) +{ + u8 *p; + + if (buf[0] == '1') { + if (count == 1 || (count == 2 && buf[1] == '\n')) + return 1; + + return -EINVAL; + } + + /* +* In order to disable the enforcement policy, a PKCS#7 signature +* is supplied. +* +* Assuming ASN.1 encoding supplied, the minimal length would be +* 4-byte header plus at least 256-byte payload. +*/ + if (count < 260) + return -EINVAL; + + p = (u8 *)buf; + + /* The primitive type must be a sequnce */ + if (p[0] != 0x30 || p[1] != 0x82) + return -EINVAL; + + /* Match up the length of the supplied buffer */ + if (be16_to_cpup((__be16 *)(p + 2)) != count - 4) + return -EINVAL; + + return 0; +} + +/* + * Disable the enforceme and verify the supplied PKCS#7 signature. + * The signed content is simply the charactoror '0'. + */ +static int disable_enforce(void *pkcs7, size_t pkcs7_len) +{ + char data = '0'; + + return verify_pkcs7_signature(, sizeof(data), pkcs7, pkcs7_len, + NULL, VERIFYING_UNSPECIFIED_SIGNATURE, + NULL, NULL); +} + static ssize_t modsign_enforce_read(struct file *filp, char __user *ubuf, size_t count, loff_t *offp) { @@ -2806,7 +2867,50 @@ static ssize_t modsign_enforce_read(struct file *filp, char __user *ubuf, return simple_read_from_buffer(ubuf, count, offp, buf, 1); } -static const struct file_operations modsign_enforce_ops = { +static ssize_t modsign_enforce_write(struct file *filp, +const char __user *ubuf, +size_t count, loff_t *offp) +{ + char *buf; + ssize_t ret; + size_t max_buf_size = 1 << MAX_ORDER; + + if (*offp > 1) + return -EFBIG; + + if (count > max_buf_size) + return -EFBIG; + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, offp, ubuf, count); + if (ret <= 0) { + kfree(buf); + return ret; + } + + ret = check_enforce(buf, count); + if (is_module_sig_enforced() && !ret) { + ret = disable_enforce(buf, count); + if (!ret) { + set_module_sig_enforce(false); + pr_notice("Kernel module validity enforcement disabled\n"); + ret = count; + } + } else if (!is_module_sig_enforced() && ret == 1) { + set_module_sig_enforce(true); + pr_notice("Kernel module validity enforcement enabled\n"); + ret = count; + } + + kfree(buf); + + return ret; +} + +static struct file_operations modsign_enforce_ops = { .read = modsign_enforce_read, .llseek = generic_file_llseek, }; @@ -2815,14 +2919,20 @@ static int __init securityfs_init(void) { struct dentry *modsign_dir; struct dentry *enforce; + umode_t mode; modsign_dir = securityfs_create_dir("modsign", NULL); if (IS_ERR(modsign_dir)) return -1; - enforce = securityfs_create_file("enforce", -