-
Notifications
You must be signed in to change notification settings - Fork 68
Add whitelist feature #449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0ae1b1b
1ce367b
c72acf5
7b7c466
088f321
af4894a
ce79c27
6e4f36c
f367201
9decce7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -736,6 +736,23 @@ static bool module_is_blacklisted(const struct kmod_module *mod) | |
| return false; | ||
| } | ||
|
|
||
| static bool module_is_whitelisted(const struct kmod_module *mod) | ||
| { | ||
| const struct kmod_ctx *ctx = mod->ctx; | ||
| const struct kmod_config *config = kmod_get_config(ctx); | ||
| const struct kmod_list *wl = config->whitelists; | ||
| const struct kmod_list *l; | ||
|
|
||
| kmod_list_foreach(l, wl) { | ||
| const char *modname = kmod_whitelist_get_modname(l); | ||
|
|
||
| if (streq(modname, mod->name)) | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| KMOD_EXPORT int kmod_module_apply_filter(const struct kmod_ctx *ctx, | ||
| enum kmod_filter filter_type, | ||
| const struct kmod_list *input, | ||
|
|
@@ -1001,6 +1018,24 @@ static int kmod_module_get_probe_list(struct kmod_module *mod, bool ignorecmd, | |
| return err; | ||
| } | ||
|
|
||
| static bool module_whitelist_check(struct kmod_module *mod) | ||
| { | ||
| const struct kmod_config *config = kmod_get_config(mod->ctx); | ||
|
|
||
| if (!config->whitelist_active || module_is_whitelisted(mod)) | ||
| return true; | ||
|
|
||
| if (config->whitelist_test_mode) { | ||
| NOTICE(mod->ctx, | ||
| "module '%s' would be denied (test mode active, load permitted)\n", | ||
| mod->name); | ||
| return true; | ||
| } | ||
|
|
||
| NOTICE(mod->ctx, "module '%s' not in whitelist, denied\n", mod->name); | ||
| return false; | ||
| } | ||
|
|
||
| KMOD_EXPORT int kmod_module_probe_insert_module( | ||
| struct kmod_module *mod, unsigned int flags, const char *extra_options, | ||
| int (*run_install)(struct kmod_module *m, const char *cmd, void *data), | ||
|
|
@@ -1032,6 +1067,9 @@ KMOD_EXPORT int kmod_module_probe_insert_module( | |
| return KMOD_PROBE_APPLY_BLACKLIST; | ||
| } | ||
|
|
||
| if (!module_whitelist_check(mod)) | ||
| return -EPERM; | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Curious about the train of thought here - do we want the check prior to any of the others - already_loaded, blacklist - or why not? |
||
| err = kmod_module_get_probe_list(mod, !!(flags & KMOD_PROBE_IGNORE_COMMAND), | ||
| &list); | ||
| if (err < 0) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -156,6 +156,27 @@ weakdep _modulename_ _modules_ | |
| required/desired at runtime. When c is loaded and is being probed, it | ||
| may issue calls to request_module() causing a or b to also be loaded. | ||
|
|
||
| whitelist _modulename_ | ||
| Adds _modulename_ to the whitelist of modules that are permitted to | ||
| load. This command only has an effect once *whitelist-enable* is | ||
| also given (in this or any other configuration file); by itself, a | ||
| *whitelist* entry is parsed but has no effect. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly related to this PR: Ideally we would have a way to validate the modulename(s) listed in the config. Think typos, people accidentally adding multiple on the same line (space, comma, other-separated), etc. None of this is a blocker for this feature, but if you feel like sending separate PR that would be really appreciated. |
||
|
|
||
| whitelist-enable | ||
| Enables whitelist enforcement: once this directive appears in any | ||
| configuration file, only modules listed via *whitelist* commands | ||
| (anywhere in the configuration) are permitted to load, and all other | ||
| modules are denied. If *whitelist-enable* is given but no | ||
| *whitelist* entries exist, every module is denied. Without | ||
| *whitelist-enable*, no whitelist is enforced regardless of any | ||
| *whitelist* entries present, and all modules may load as usual. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's shorten this up a bit - my somewhat ADHD brain stopped reading past "configuration file" the first time. |
||
|
|
||
| whitelist-test-mode | ||
| Used together with *whitelist-enable*. Modules that the whitelist | ||
| would otherwise deny are still permitted to load; a notice is logged | ||
| instead, reporting which module would have been denied. This allows | ||
| auditing the effect of a whitelist before enforcing it. | ||
|
|
||
| # COMPATIBILITY | ||
|
|
||
| A future version of kmod will come with a strong warning to avoid use of the | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| whitelist-enable | ||
| whitelist mod-simple |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| kernel/mod-simple.ko: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Device nodes to trigger on-demand module loading. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Soft dependencies extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases for symbols, used by symbol_request(). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| whitelist-enable |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| kernel/mod-simple.ko: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Device nodes to trigger on-demand module loading. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Soft dependencies extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases for symbols, used by symbol_request(). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| kernel/mod-simple.ko: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Device nodes to trigger on-demand module loading. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Soft dependencies extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases for symbols, used by symbol_request(). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| whitelist-enable | ||
| whitelist-test-mode |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| kernel/mod-simple.ko: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Device nodes to trigger on-demand module loading. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Soft dependencies extracted from modules themselves. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # Aliases for symbols, used by symbol_request(). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| // SPDX-License-Identifier: LGPL-2.1-or-later | ||
| /* | ||
| * Copyright © 2026 Donald Buczek | ||
| */ | ||
|
|
||
| #include <stdlib.h> | ||
|
|
||
| #include "testsuite.h" | ||
|
|
||
| static int whitelist_inactive(void) | ||
| { | ||
| return EXEC_TOOL(modprobe, "mod-simple"); | ||
| } | ||
| DEFINE_TEST(whitelist_inactive, | ||
| .description = "check that modules load normally when no whitelist directive is configured", | ||
| .config = { | ||
| [TC_UNAME_R] = "3.3.3", | ||
| [TC_ROOTFS] = TESTSUITE_ROOTFS "test-whitelist/no-directives", | ||
| [TC_INIT_MODULE_RETCODES] = "", | ||
| }, | ||
| .modules_loaded = "mod-simple", | ||
| ); | ||
|
|
||
| static int whitelist_deny_all(void) | ||
| { | ||
| return EXEC_TOOL(modprobe, "mod-simple"); | ||
| } | ||
| DEFINE_TEST(whitelist_deny_all, | ||
| .description = "check that modules are denied when whitelist-enable is set with no entries", | ||
| .config = { | ||
| [TC_UNAME_R] = "3.3.3", | ||
| [TC_ROOTFS] = TESTSUITE_ROOTFS "test-whitelist/deny-all", | ||
| [TC_INIT_MODULE_RETCODES] = "", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the empty TC_INIT_MODULE_RETCODES here? |
||
| }, | ||
| .expected_fail = true, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not related to your work: we should get a exit-status field, since expected_fail effectively masks any failure - crash, sanitizers, etc. |
||
| ); | ||
|
|
||
| static int whitelist_allowed(void) | ||
| { | ||
| return EXEC_TOOL(modprobe, "mod-simple"); | ||
| } | ||
| DEFINE_TEST(whitelist_allowed, | ||
| .description = "check that a listed module loads, also verifying hyphen/underscore normalisation", | ||
| .config = { | ||
| [TC_UNAME_R] = "3.3.3", | ||
| [TC_ROOTFS] = TESTSUITE_ROOTFS "test-whitelist/allowed", | ||
| [TC_INIT_MODULE_RETCODES] = "", | ||
| }, | ||
| .modules_loaded = "mod-simple", | ||
| ); | ||
|
|
||
| static int whitelist_test_mode(void) | ||
| { | ||
| return EXEC_TOOL(modprobe, "mod-simple"); | ||
| } | ||
| DEFINE_TEST(whitelist_test_mode, | ||
| .description = "check that whitelist-test-mode permits loading a module that would otherwise be denied", | ||
| .config = { | ||
| [TC_UNAME_R] = "3.3.3", | ||
| [TC_ROOTFS] = TESTSUITE_ROOTFS "test-whitelist/test-mode", | ||
| [TC_INIT_MODULE_RETCODES] = "", | ||
| }, | ||
| .modules_loaded = "mod-simple", | ||
| ); | ||
|
|
||
| TESTSUITE_MAIN(); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have a clear/unique prefix like the original patch. Eg.
whitelist: module %s....Here and throughout.