Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features Download
Marketing
Archives
FAQ
Blog
 
Gmane
From: Milan Broz <mbroz-H+wXaHxf7aLQT0dZR+AlfA <at> public.gmane.org>
Subject: [RFC PATCH] kmod: add whitelist option
Newsgroups: gmane.linux.kernel.modules
Date: Tuesday 6th November 2012 12:03:04 UTC (over 5 years ago)
The whitelist option allows setup of hardened systems, only
modules on whitelist are allowed to load.

This patch adds "whitelist" option and definition to libkmod
allowing to work with whitelist (similar to blakclist, except
whitelist, once defined, is unconditional).

Without defined whitelist, there is no change.

If at least one whitelist command is specified, module loading
is limited to module matching loaded whitelist.

For more context see bug
https://bugzilla.redhat.com/show_bug.cgi?id=560084
---
 Makefile.am                                        |    4 +-
 libkmod/docs/libkmod-sections.txt                  |    1 +
 libkmod/libkmod-config.c                           |   72 ++++++++++++-
 libkmod/libkmod-module.c                           |   43 +++++++-
 libkmod/libkmod-private.h                          |    3 +-
 libkmod/libkmod.h                                  |    3 +
 libkmod/libkmod.sym                                |    5 +
 man/modprobe.d.xml                                 |   27 +++++
 .../test-whitelist/etc/modprobe.d/modprobe.conf    |    2 +
 testsuite/test-whitelist.c                         |  114
++++++++++++++++++++
 tools/modprobe.c                                   |   12 ++-
 11 files changed, 275 insertions(+), 11 deletions(-)
 create mode 100644
testsuite/rootfs-pristine/test-whitelist/etc/modprobe.d/modprobe.conf
 create mode 100644 testsuite/test-whitelist.c

diff --git a/Makefile.am b/Makefile.am
index e4c1a83..03aa421 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -176,7 +176,7 @@ testsuite_libtestsuite_la_LIBADD = -lrt
 
 TESTSUITE = testsuite/test-init testsuite/test-testsuite
testsuite/test-loaded \
 	    testsuite/test-modinfo testsuite/test-alias testsuite/test-new-module
\
-	    testsuite/test-modprobe testsuite/test-blacklist \
+	    testsuite/test-modprobe testsuite/test-blacklist
testsuite/test-whitelist \
 	    testsuite/test-dependencies testsuite/test-depmod
 
 check_PROGRAMS = $(TESTSUITE)
@@ -198,6 +198,8 @@ testsuite_test_modprobe_LDADD = $(TESTSUITE_LDADD)
 testsuite_test_modprobe_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
 testsuite_test_blacklist_LDADD = $(TESTSUITE_LDADD)
 testsuite_test_blacklist_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
+testsuite_test_whitelist_LDADD = $(TESTSUITE_LDADD)
+testsuite_test_whitelist_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
 testsuite_test_dependencies_LDADD = $(TESTSUITE_LDADD)
 testsuite_test_dependencies_CPPFLAGS = $(TESTSUITE_CPPFLAGS)
 testsuite_test_depmod_LDADD = $(TESTSUITE_LDADD)
diff --git a/libkmod/docs/libkmod-sections.txt
b/libkmod/docs/libkmod-sections.txt
index e59ab7a..bf7c0d9 100644
--- a/libkmod/docs/libkmod-sections.txt
+++ b/libkmod/docs/libkmod-sections.txt
@@ -31,6 +31,7 @@ kmod_list_prev
 libkmod-config
 kmod_config_iter
 kmod_config_get_blacklists
+kmod_config_get_whitelists
 kmod_config_get_install_commands
 kmod_config_get_remove_commands
 kmod_config_get_aliases
diff --git a/libkmod/libkmod-config.c b/libkmod/libkmod-config.c
index 398468e..f1f1181 100644
--- a/libkmod/libkmod-config.c
+++ b/libkmod/libkmod-config.c
@@ -56,7 +56,7 @@ struct kmod_softdep {
 	unsigned int n_post;
 };
 
-const char *kmod_blacklist_get_modname(const struct kmod_list *l)
+const char *kmod_list_get_modname(const struct kmod_list *l)
 {
 	return l->data;
 }
@@ -303,6 +303,38 @@ static void kmod_config_free_blacklist(struct
kmod_config *config,
 	config->blacklists = kmod_list_remove(l);
 }
 
+static int kmod_config_add_whitelist(struct kmod_config *config,
+							const char *modname)
+{
+	char *p;
+	struct kmod_list *list;
+
+	DBG(config->ctx, "modname=%s\n", modname);
+
+	p = strdup(modname);
+	if (!p)
+		goto oom_error_init;
+
+	list = kmod_list_append(config->whitelists, p);
+	if (!list)
+		goto oom_error;
+	config->whitelists = list;
+	return 0;
+
+oom_error:
+	free(p);
+oom_error_init:
+	ERR(config->ctx, "out-of-memory modname=%s\n", modname);
+	return -ENOMEM;
+}
+
+static void kmod_config_free_whitelist(struct kmod_config *config,
+							struct kmod_list *l)
+{
+	free(l->data);
+	config->whitelists = kmod_list_remove(l);
+}
+
 static int kmod_config_add_softdep(struct kmod_config *config,
 							const char *modname,
 							const char *line)
@@ -634,6 +666,14 @@ static int kmod_config_parse(struct kmod_config
*config, int fd,
 
 			kmod_config_add_blacklist(config,
 						underscores(ctx, modname));
+		} else if (streq(cmd, "whitelist")) {
+			char *modname = strtok_r(NULL, "\t ", &saveptr);
+
+			if (modname == NULL)
+				goto syntax_error;
+
+			kmod_config_add_whitelist(config,
+						underscores(ctx, modname));
 		} else if (streq(cmd, "options")) {
 			char *modname = strtok_r(NULL, "\t ", &saveptr);
 			char *options = strtok_r(NULL, "\0", &saveptr);
@@ -703,6 +743,9 @@ void kmod_config_free(struct kmod_config *config)
 	while (config->blacklists)
 		kmod_config_free_blacklist(config, config->blacklists);
 
+	while (config->whitelists)
+		kmod_config_free_whitelist(config, config->whitelists);
+
 	while (config->options)
 		kmod_config_free_options(config, config->options);
 
@@ -955,6 +998,7 @@ enum config_type {
 	CONFIG_TYPE_ALIAS,
 	CONFIG_TYPE_OPTION,
 	CONFIG_TYPE_SOFTDEP,
+	CONFIG_TYPE_WHITELIST,
 };
 
 struct kmod_config_iter {
@@ -987,7 +1031,11 @@ static struct kmod_config_iter
*kmod_config_iter_new(const struct kmod_ctx* ctx,
 	switch (type) {
 	case CONFIG_TYPE_BLACKLIST:
 		iter->list = config->blacklists;
-		iter->get_key = kmod_blacklist_get_modname;
+		iter->get_key = kmod_list_get_modname;
+		break;
+	case CONFIG_TYPE_WHITELIST:
+		iter->list = config->whitelists;
+		iter->get_key = kmod_list_get_modname;
 		break;
 	case CONFIG_TYPE_INSTALL:
 		iter->list = config->install_commands;
@@ -1046,6 +1094,26 @@ KMOD_EXPORT struct kmod_config_iter
*kmod_config_get_blacklists(const struct kmo
 }
 
 /**
+ * kmod_config_get_whitelists:
+ * @ctx: kmod library context
+ *
+ * Retrieve an iterator to deal with the whitelist maintained inside the
+ * library. See kmod_config_iter_get_key(), kmod_config_iter_get_value()
and
+ * kmod_config_iter_next(). At least one call to kmod_config_iter_next()
must
+ * be made to initialize the iterator and check if it's valid.
+ *
+ * Returns: a new iterator over the whitelists or NULL on failure. Free it
+ * with kmod_config_iter_free_iter().
+ */
+KMOD_EXPORT struct kmod_config_iter *kmod_config_get_whitelists(const
struct kmod_ctx *ctx)
+{
+	if (ctx == NULL)
+		return NULL;
+
+	return kmod_config_iter_new(ctx, CONFIG_TYPE_WHITELIST);
+}
+
+/**
  * kmod_config_get_install_commands:
  * @ctx: kmod library context
  *
diff --git a/libkmod/libkmod-module.c b/libkmod/libkmod-module.c
index 0d87ce1..cf68fae 100644
--- a/libkmod/libkmod-module.c
+++ b/libkmod/libkmod-module.c
@@ -850,7 +850,7 @@ static bool module_is_blacklisted(struct kmod_module
*mod)
 	const struct kmod_list *l;
 
 	kmod_list_foreach(l, bl) {
-		const char *modname = kmod_blacklist_get_modname(l);
+		const char *modname = kmod_list_get_modname(l);
 
 		if (streq(modname, mod->name))
 			return true;
@@ -859,6 +859,26 @@ static bool module_is_blacklisted(struct kmod_module
*mod)
 	return false;
 }
 
+static bool module_is_not_whitelisted(struct kmod_module *mod)
+{
+	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;
+
+	if (!wl)
+		return false;
+
+	kmod_list_foreach(l, wl) {
+		const char *modname = kmod_list_get_modname(l);
+
+		if (streq(modname, mod->name))
+			return false;
+	}
+
+	return true;
+}
+
 /**
  * kmod_module_apply_filter
  * @ctx: kmod library context
@@ -897,6 +917,10 @@ KMOD_EXPORT int kmod_module_apply_filter(const struct
kmod_ctx *ctx,
 		if ((filter_type & KMOD_FILTER_BUILTIN) && mod->builtin)
 			continue;
 
+		if ((filter_type & KMOD_FILTER_WHITELIST) &&
+				module_is_not_whitelisted(mod))
+			continue;
+
 		node = kmod_list_append(*output, mod);
 		if (node == NULL)
 			goto fail;
@@ -1143,7 +1167,7 @@ static int kmod_module_get_probe_list(struct
kmod_module *mod,
  * output or in dry-run mode.
  *
  * Insert a module in Linux kernel resolving dependencies, soft
dependencies,
- * install commands and applying blacklist.
+ * install commands and applying blacklist and whitelist.
  *
  * If @run_install is NULL, this function will fork and exec by calling
  * system(3). Don't pass a NULL argument in @run_install if your binary is
@@ -1163,6 +1187,7 @@ KMOD_EXPORT int
kmod_module_probe_insert_module(struct kmod_module *mod,
 						const char *options))
 {
 	struct kmod_list *list = NULL, *l;
+	struct kmod_list *filtered = NULL;
 	struct probe_insert_cb cb;
 	int err;
 
@@ -1195,8 +1220,20 @@ KMOD_EXPORT int
kmod_module_probe_insert_module(struct kmod_module *mod,
 	if (err < 0)
 		return err;
 
+	/* Removes modules NOT in whitelist */
+	err = kmod_module_apply_filter(mod->ctx,
+			KMOD_FILTER_WHITELIST, list, &filtered);
+	if (err < 0)
+		return err;
+
+	kmod_module_unref_list(list);
+	if (filtered == NULL)
+		return KMOD_PROBE_APPLY_WHITELIST;
+
+	list = filtered;
+	filtered = NULL;
+
 	if (flags & KMOD_PROBE_APPLY_BLACKLIST_ALL) {
-		struct kmod_list *filtered = NULL;
 
 		err = kmod_module_apply_filter(mod->ctx,
 				KMOD_FILTER_BLACKLIST, list, &filtered);
diff --git a/libkmod/libkmod-private.h b/libkmod/libkmod-private.h
index 4760733..0651d52 100644
--- a/libkmod/libkmod-private.h
+++ b/libkmod/libkmod-private.h
@@ -102,6 +102,7 @@ struct kmod_config {
 	struct kmod_ctx *ctx;
 	struct kmod_list *aliases;
 	struct kmod_list *blacklists;
+	struct kmod_list *whitelists;
 	struct kmod_list *options;
 	struct kmod_list *remove_commands;
 	struct kmod_list *install_commands;
@@ -112,7 +113,7 @@ struct kmod_config {
 
 int kmod_config_new(struct kmod_ctx *ctx, struct kmod_config **config,
const char * const *config_paths) __attribute__((nonnull(1, 2,3)));
 void kmod_config_free(struct kmod_config *config)
__attribute__((nonnull(1)));
-const char *kmod_blacklist_get_modname(const struct kmod_list *l)
__attribute__((nonnull(1)));
+const char *kmod_list_get_modname(const struct kmod_list *l)
__attribute__((nonnull(1)));
 const char *kmod_alias_get_name(const struct kmod_list *l)
__attribute__((nonnull(1)));
 const char *kmod_alias_get_modname(const struct kmod_list *l)
__attribute__((nonnull(1)));
 const char *kmod_option_get_options(const struct kmod_list *l)
__attribute__((nonnull(1)));
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index d03ab19..18721f3 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -106,6 +106,7 @@ struct kmod_list *kmod_list_last(const struct kmod_list
*list);
  */
 struct kmod_config_iter;
 struct kmod_config_iter *kmod_config_get_blacklists(const struct kmod_ctx
*ctx);
+struct kmod_config_iter *kmod_config_get_whitelists(const struct kmod_ctx
*ctx);
 struct kmod_config_iter *kmod_config_get_install_commands(const struct
kmod_ctx *ctx);
 struct kmod_config_iter *kmod_config_get_remove_commands(const struct
kmod_ctx *ctx);
 struct kmod_config_iter *kmod_config_get_aliases(const struct kmod_ctx
*ctx);
@@ -162,12 +163,14 @@ enum kmod_probe {
 	KMOD_PROBE_APPLY_BLACKLIST_ALL =	0x10000,
 	KMOD_PROBE_APPLY_BLACKLIST =		0x20000,
 	KMOD_PROBE_APPLY_BLACKLIST_ALIAS_ONLY =	0x40000,
+	KMOD_PROBE_APPLY_WHITELIST =		0x80000,
 };
 
 /* Flags to kmod_module_apply_filter() */
 enum kmod_filter {
 	KMOD_FILTER_BLACKLIST = 0x00001,
 	KMOD_FILTER_BUILTIN = 0x00002,
+	KMOD_FILTER_WHITELIST = 0x00004,
 };
 
 int kmod_module_remove_module(struct kmod_module *mod, unsigned int
flags);
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
index 854d257..28c8462 100644
--- a/libkmod/libkmod.sym
+++ b/libkmod/libkmod.sym
@@ -86,3 +86,8 @@ LIBKMOD_6 {
 global:
 	kmod_module_apply_filter;
 } LIBKMOD_5;
+
+LIBKMOD_11 {
+global:
+	kmod_config_get_whitelists;
+} LIBKMOD_6;
diff --git a/man/modprobe.d.xml b/man/modprobe.d.xml
index dc19b23..2140c9b 100644
--- a/man/modprobe.d.xml
+++ b/man/modprobe.d.xml
@@ -112,6 +112,33 @@
         
       
       
+        whitelist modulename
+        
+        
+         
+           If at least one whitelist command is
specified,
+           module loading and scripts specified
+           using install are limited to module names
+           matching a glob pattern specified in
one
+           of the whitelist commands.
+         
+         
+           You can prepare whitelist of currently loaded modules e.g. by
this
+           command:
+         
+         
+           lsmod | tail -n +2 | cut -d ' ' -f 1 | sed 's/^/whitelist /'
+         
+         
+           Note that the whitelist command is not a
direct
+           opposite of the blacklist command.
+           The blacklist command affects the selection
+           of a module to be loaded or install script
to
+           be performed.
+         
+        
+      
+      
         install modulename
command...
         
         
diff --git
a/testsuite/rootfs-pristine/test-whitelist/etc/modprobe.d/modprobe.conf
b/testsuite/rootfs-pristine/test-whitelist/etc/modprobe.d/modprobe.conf
new file mode 100644
index 0000000..9dc0ced
--- /dev/null
+++ b/testsuite/rootfs-pristine/test-whitelist/etc/modprobe.d/modprobe.conf
@@ -0,0 +1,2 @@
+whitelist floppy
+whitelist pcspkr
diff --git a/testsuite/test-whitelist.c b/testsuite/test-whitelist.c
new file mode 100644
index 0000000..49a3082
--- /dev/null
+++ b/testsuite/test-whitelist.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011-2012  ProFUSION embedded systems
+ * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 
USA
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/* good luck bulding a kmod_list outside of the library... makes this
blacklist
+ * function rather pointless */
+#include 
+
+/* FIXME: hack, change name so we don't clash */
+#undef ERR
+#include "testsuite.h"
+
+static int whitelist_1(const struct test *t)
+{
+	struct kmod_ctx *ctx;
+	struct kmod_list *list = NULL, *l, *filtered;
+	struct kmod_module *mod;
+	int err;
+	size_t len = 0;
+
+	const char *names[] = { "pcspkr", "pcspkr2", "floppy", "ext4", NULL };
+	const char **name;
+
+	ctx = kmod_new(NULL, NULL);
+	if (ctx == NULL)
+		exit(EXIT_FAILURE);
+
+	for(name = names; *name; name++) {
+		err = kmod_module_new_from_name(ctx, *name, &mod);
+		if (err < 0)
+			goto fail_lookup;
+		list = kmod_list_append(list, mod);
+	}
+
+	err = kmod_module_apply_filter(ctx, KMOD_FILTER_WHITELIST, list,
+								&filtered);
+	if (err < 0) {
+		ERR("Could not filter: %s\n", strerror(-err));
+		goto fail;
+	}
+	if (filtered == NULL) {
+		ERR("All modules were filtered out!\n");
+		goto fail;
+	}
+
+	kmod_list_foreach(l, filtered) {
+		const char *modname;
+		mod = kmod_module_get_module(l);
+		modname = kmod_module_get_name(mod);
+		/* These are not on whitelist, must be rejected */
+		if (strcmp("pcspkr2", modname) == 0 || strcmp("ext4", modname) == 0)
+			goto fail;
+		/* These are on whitelist, must be in list */
+		if (strcmp("pcspkr", modname) && strcmp("floppy", modname))
+			goto fail;
+		len++;
+		kmod_module_unref(mod);
+	}
+
+	if (len != 2)
+		goto fail;
+
+	kmod_module_unref_list(filtered);
+	kmod_module_unref_list(list);
+	kmod_unref(ctx);
+
+	return EXIT_SUCCESS;
+
+fail:
+	kmod_module_unref_list(list);
+fail_lookup:
+	kmod_unref(ctx);
+	return EXIT_FAILURE;
+}
+static const struct test swhitelist_1 = {
+	.name = "whitelist_1",
+	.description = "check if modules are correctly whitelisted",
+	.func = whitelist_1,
+	.config = {
+		[TC_ROOTFS] = TESTSUITE_ROOTFS "test-whitelist/",
+	},
+	.need_spawn = true,
+};
+
+static const struct test *tests[] = {
+	&swhitelist_1,
+	NULL,
+};
+
+TESTSUITE_MAIN(tests);
diff --git a/tools/modprobe.c b/tools/modprobe.c
index 58f6df9..81b8442 100644
--- a/tools/modprobe.c
+++ b/tools/modprobe.c
@@ -638,10 +638,14 @@ static int insmod(struct kmod_ctx *ctx, const char
*alias,
 					extra_options, NULL, NULL, show);
 		}
 
-		if (err >= 0)
-			/* ignore flag return values such as a mod being blacklisted */
-			err = 0;
-		else {
+		if (err >= 0) {
+			if (err & KMOD_PROBE_APPLY_WHITELIST)
+				ERR("could not insert '%s': Module not on whitelist\n",
+							kmod_module_get_name(mod));
+			else
+				/* ignore flag return values such as a mod being blacklisted */
+				err = 0;
+		} else {
 			switch (err) {
 			case -EEXIST:
 				ERR("could not insert '%s': Module already in kernel\n",
-- 
1.7.10.4
 
CD: 4ms