Message ID | 20191115114429.18566-1-richard_c_haines@btinternet.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [V2] selinux-testsuite: Add kernel module tests | expand |
A couple comments below... On Fri, Nov 15, 2019 at 12:44 PM Richard Haines <richard_c_haines@btinternet.com> wrote: > Test kernel module loading permissions. > > Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> > --- > V2 Change: > Check permission denial module_load versus module_request by using a > test kernel module for each. > Note: Rawhide (with secnext kernel) adds built-in.a and built-in.a.cmd when > building modules, therefore added to Makefile and .gitignore. > > policy/Makefile | 4 + > policy/test_module_load.te | 118 +++++++++++++++++++++ > tests/Makefile | 4 + > tests/module_load/.gitignore | 11 ++ > tests/module_load/Makefile | 12 +++ > tests/module_load/finit_load.c | 94 +++++++++++++++++ > tests/module_load/init_load.c | 121 ++++++++++++++++++++++ > tests/module_load/setest_module_load.c | 18 ++++ > tests/module_load/setest_module_request.c | 22 ++++ > tests/module_load/test | 62 +++++++++++ > 10 files changed, 466 insertions(+) > create mode 100644 policy/test_module_load.te > create mode 100644 tests/module_load/.gitignore > create mode 100644 tests/module_load/Makefile > create mode 100644 tests/module_load/finit_load.c > create mode 100644 tests/module_load/init_load.c > create mode 100644 tests/module_load/setest_module_load.c > create mode 100644 tests/module_load/setest_module_request.c > create mode 100755 tests/module_load/test > > diff --git a/policy/Makefile b/policy/Makefile > index ff65153..545f3b5 100644 > --- a/policy/Makefile > +++ b/policy/Makefile > @@ -90,6 +90,10 @@ ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms. > TARGETS+=test_notify.te > endif > > +ifeq ($(shell grep -q module_load $(POLDEV)/include/support/all_perms.spt && echo true),true) > +TARGETS+=test_module_load.te > +endif > + > ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) > TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS)) > endif > diff --git a/policy/test_module_load.te b/policy/test_module_load.te > new file mode 100644 > index 0000000..566ddf7 > --- /dev/null > +++ b/policy/test_module_load.te > @@ -0,0 +1,118 @@ > +# > +############################## Define Macro ################################ > +# > +# Replace domain_type() macro as it hides some relevant denials in audit.log > +# > +gen_require(` > + type setrans_var_run_t, syslogd_t; > +') > + > +define(`module_domain_type',` > + allow $1 proc_t:dir { search }; > + allow $1 proc_t:lnk_file { read }; > + allow $1 self:dir { search }; > + allow $1 self:file { open read write }; > + dontaudit init_t syslogd_t:fd use; > + dontaudit $1 security_t:filesystem getattr; > + dontaudit $1 self:file getattr; > + dontaudit $1 setrans_var_run_t:dir search; > + dontaudit unconfined_t $1:process { noatsecure rlimitinh siginh }; > +') > + > +# > +############# Test kernel modules with finitmod_module(2) ################### > +# > +attribute finitmoddomain; > + > +type test_finitmod_t; > +module_domain_type(test_finitmod_t) > +unconfined_runs_test(test_finitmod_t) > +typeattribute test_finitmod_t testdomain; > +typeattribute test_finitmod_t finitmoddomain; > + > +allow test_finitmod_t self:capability { sys_module }; > +allow test_finitmod_t test_file_t:system { module_load }; > +allow test_finitmod_t kernel_t:system { module_request }; > + > +############### Deny cap sys_module ###################### > +type test_finitmod_deny_sys_module_t; > +module_domain_type(test_finitmod_deny_sys_module_t) > +unconfined_runs_test(test_finitmod_deny_sys_module_t) > +typeattribute test_finitmod_deny_sys_module_t testdomain; > +typeattribute test_finitmod_deny_sys_module_t finitmoddomain; > + > +neverallow test_finitmod_deny_sys_module_t self:capability { sys_module }; > + > +############### Deny sys module_load ###################### > +type test_finitmod_deny_module_load_t; > +module_domain_type(test_finitmod_deny_module_load_t) > +unconfined_runs_test(test_finitmod_deny_module_load_t) > +typeattribute test_finitmod_deny_module_load_t testdomain; > +typeattribute test_finitmod_deny_module_load_t finitmoddomain; > + > +allow test_finitmod_deny_module_load_t self:capability { sys_module }; > +neverallow test_finitmod_deny_module_load_t test_file_t:system { module_load }; > + > +############### Deny sys module_request ###################### > +type test_finitmod_deny_module_request_t; > +module_domain_type(test_finitmod_deny_module_request_t) > +unconfined_runs_test(test_finitmod_deny_module_request_t) > +typeattribute test_finitmod_deny_module_request_t testdomain; > +typeattribute test_finitmod_deny_module_request_t finitmoddomain; > + > +allow test_finitmod_deny_module_request_t self:capability { sys_module }; > +allow test_finitmod_deny_module_request_t test_file_t:system { module_load }; > +neverallow test_finitmod_deny_module_request_t kernel_t:system { module_request }; > + > +# > +############# Test kernel modules with initmod_module(2) ################### > +# > +attribute initmoddomain; > + > +type test_initmod_t; > +module_domain_type(test_initmod_t) > +unconfined_runs_test(test_initmod_t) > +typeattribute test_initmod_t testdomain; > +typeattribute test_initmod_t initmoddomain; > + > +allow test_initmod_t self:capability { sys_module }; > +allow test_initmod_t self:system { module_load }; > +allow test_initmod_t kernel_t:system { module_request }; > + > +############### Deny cap sys_module ###################### > +type test_initmod_deny_sys_module_t; > +module_domain_type(test_initmod_deny_sys_module_t) > +unconfined_runs_test(test_initmod_deny_sys_module_t) > +typeattribute test_initmod_deny_sys_module_t testdomain; > +typeattribute test_initmod_deny_sys_module_t initmoddomain; > + > +neverallow test_initmod_deny_sys_module_t self:capability { sys_module }; > + > +############### Deny sys module_load ###################### > +type test_initmod_deny_module_load_t; > +module_domain_type(test_initmod_deny_module_load_t) > +unconfined_runs_test(test_initmod_deny_module_load_t) > +typeattribute test_initmod_deny_module_load_t testdomain; > +typeattribute test_initmod_deny_module_load_t initmoddomain; > + > +allow test_initmod_deny_module_load_t self:capability { sys_module }; > +neverallow test_initmod_deny_module_load_t self:system { module_load }; > + > +############### Deny sys module_request ###################### > +type test_initmod_deny_module_request_t; > +module_domain_type(test_initmod_deny_module_request_t) > +unconfined_runs_test(test_initmod_deny_module_request_t) > +typeattribute test_initmod_deny_module_request_t testdomain; > +typeattribute test_initmod_deny_module_request_t initmoddomain; > + > +allow test_initmod_deny_module_request_t self:capability { sys_module }; > +allow test_initmod_deny_module_request_t self:system { module_load }; > +neverallow test_initmod_deny_module_request_t kernel_t:system { module_request }; > + > +# > +########### Allow these domains to be entered from sysadm domain ############ > +# > +miscfiles_domain_entry_test_files(finitmoddomain) > +userdom_sysadm_entry_spec_domtrans_to(finitmoddomain) > +miscfiles_domain_entry_test_files(initmoddomain) > +userdom_sysadm_entry_spec_domtrans_to(initmoddomain) It seems that the finitmoddomain and initmoddomain type sets are exactly the same except for names - can they be merged into just one set of types? The AVC denials should be still easily distinguishable by the comm= field if that's the intended purpose of the separation. [...] > diff --git a/tests/module_load/Makefile b/tests/module_load/Makefile > new file mode 100644 > index 0000000..c561685 > --- /dev/null > +++ b/tests/module_load/Makefile > @@ -0,0 +1,12 @@ > +obj-m = setest_module_load.o setest_module_request.o > + > +TARGETS = finit_load init_load > +LDLIBS += -lselinux > +KDIR = /usr/src/kernels/$(shell uname -r) I think you should rather use /lib/modules/$(shell uname -r)/build here, which seems to be more portable (it works at least on Fedora and Ubuntu, while /usr/src/kernels/... doesn't work at least on Ubuntu). > + > +all: $(TARGETS) > + $(MAKE) -C $(KDIR) M=$(PWD) > + > +clean: > + rm -f $(TARGETS) > + rm -f *.a *.o *.ko *.cmd *.mod *.mod.c .*.cmd Module.symvers modules.order > diff --git a/tests/module_load/finit_load.c b/tests/module_load/finit_load.c > new file mode 100644 > index 0000000..029c698 > --- /dev/null > +++ b/tests/module_load/finit_load.c > @@ -0,0 +1,94 @@ > +#define _GNU_SOURCE 1 > + > +#include <stdio.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <stdbool.h> > +#include <fcntl.h> > +#include <limits.h> > +#include <sys/syscall.h> > +#include <selinux/selinux.h> > + > +static void print_usage(char *progfile_name) > +{ > + fprintf(stderr, > + "usage: %s [-v] path name\n" > + "Where:\n\t" > + "-v Print information.\n\t" > + "path Kernel module build path.\n\t" > + "name Name of kernel module to load.\n", progfile_name); > + exit(-1); > +} > + > +int main(int argc, char *argv[]) > +{ > + char *context, file_name[PATH_MAX]; > + int opt, result, fd; > + bool verbose = false; > + > + while ((opt = getopt(argc, argv, "v")) != -1) { > + switch (opt) { > + case 'v': > + verbose = true; > + break; > + default: > + print_usage(argv[0]); > + } > + } > + > + if (optind >= argc) > + print_usage(argv[0]); > + > + result = sprintf(file_name, "%s/%s.ko", argv[optind], > + argv[optind + 1]); > + if (result < 0) { > + fprintf(stderr, "Failed sprintf\n"); > + exit(-1); > + } > + > + fd = open(file_name, O_RDONLY); > + if (!fd) { > + fprintf(stderr, "Failed to open %s: %s\n", > + file_name, strerror(errno)); > + exit(-1); > + } > + > + result = getcon(&context); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + close(fd); > + exit(-1); > + } > + > + if (verbose) > + printf("Process context:\n\t%s\n", context); > + > + free(context); Why not wrap also the getcon() & free() calls under the 'if (verbose) { ... }'? The context is not used in the non-verbose case. > + > + result = syscall(__NR_finit_module, fd, "", 0); > + if (result < 0) { > + fprintf(stderr, "Failed to load '%s' module: %s\n", > + file_name, strerror(errno)); > + close(fd); > + /* Denying: sys_module=EPERM, module_load=EACCES */ > + exit(errno); > + } > + close(fd); > + > + if (verbose) > + printf("Loaded kernel module: %s\n", file_name); > + > + result = syscall(__NR_delete_module, argv[optind + 1], 0); > + if (result < 0) { > + fprintf(stderr, "Failed to delete '%s' module: %s\n", > + argv[optind + 1], strerror(errno)); > + exit(-1); > + } > + > + if (verbose) > + printf("Deleted kernel module: %s\n", argv[optind + 1]); > + > + return 0; > +} > diff --git a/tests/module_load/init_load.c b/tests/module_load/init_load.c > new file mode 100644 > index 0000000..5f9997b > --- /dev/null > +++ b/tests/module_load/init_load.c > @@ -0,0 +1,121 @@ > +#define _GNU_SOURCE 1 > + > +#include <stdio.h> > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <stdbool.h> > +#include <fcntl.h> > +#include <limits.h> > +#include <sys/stat.h> > +#include <sys/syscall.h> > +#include <selinux/selinux.h> > + > +static void print_usage(char *progfile_name) > +{ > + fprintf(stderr, > + "usage: %s [-v] path name\n" > + "Where:\n\t" > + "-v Print information.\n\t" > + "path Kernel module build path.\n\t" > + "name Name of kernel module to load.\n", progfile_name); > + exit(-1); > +} > + > +int main(int argc, char *argv[]) > +{ > + char *context, file_name[PATH_MAX]; > + int opt, result, fd; > + bool verbose = false; > + void *elf_image; > + struct stat st; > + > + while ((opt = getopt(argc, argv, "v")) != -1) { > + switch (opt) { > + case 'v': > + verbose = true; > + break; > + default: > + print_usage(argv[0]); > + } > + } > + > + if (optind >= argc) > + print_usage(argv[0]); > + > + result = sprintf(file_name, "%s/%s.ko", argv[optind], > + argv[optind + 1]); > + if (result < 0) { > + fprintf(stderr, "Failed sprintf\n"); > + exit(-1); > + } > + > + fd = open(file_name, O_RDONLY); > + if (!fd) { > + fprintf(stderr, "Failed to open %s: %s\n", > + file_name, strerror(errno)); > + exit(-1); > + } > + > + result = getcon(&context); > + if (result < 0) { > + fprintf(stderr, "Failed to obtain process context\n"); > + close(fd); > + exit(-1); > + } > + > + if (verbose) > + printf("Process context:\n\t%s\n", context); > + > + free(context); Ditto here. > + > + result = fstat(fd, &st); > + if (result < 0) { > + fprintf(stderr, "Failed fstat on %s: %s\n", > + file_name, strerror(errno)); > + close(fd); > + exit(-1); > + } > + > + elf_image = malloc(st.st_size); > + if (!elf_image) { > + fprintf(stderr, "Failed malloc on %s: %s\n", > + file_name, strerror(errno)); > + close(fd); > + exit(-1); > + } > + > + result = read(fd, elf_image, st.st_size); > + if (result < 0) { > + fprintf(stderr, "Failed read on %s: %s\n", > + file_name, strerror(errno)); > + close(fd); You should free 'elf_image' here. > + exit(-1); > + } > + close(fd); > + > + result = syscall(__NR_init_module, elf_image, st.st_size, ""); > + if (result < 0) { > + fprintf(stderr, "Failed to load '%s' module: %s\n", > + file_name, strerror(errno)); ...and here as well. (In this case you can just move the 'free(elf_image);' below to before the syscall return value check.)1 > + /* Denying: sys_module=EPERM, module_load & request=EACCES */ > + exit(errno); > + } > + free(elf_image); > + > + if (verbose) > + printf("Loaded kernel module: %s\n", file_name); > + > + result = syscall(__NR_delete_module, argv[optind + 1], 0); > + if (result < 0) { > + fprintf(stderr, "Failed to delete '%s' module: %s\n", > + argv[optind + 1], strerror(errno)); > + exit(-1); > + } > + > + if (verbose) > + printf("Deleted kernel module: %s\n", argv[optind + 1]); > + > + return 0; > +} [...] -- Ondrej Mosnacek <omosnace at redhat dot com> Software Engineer, Security Technologies Red Hat, Inc.
On 11/18/19 4:10 AM, Ondrej Mosnacek wrote: > A couple comments below... > > On Fri, Nov 15, 2019 at 12:44 PM Richard Haines > <richard_c_haines@btinternet.com> wrote: <snip> >> +########### Allow these domains to be entered from sysadm domain ############ >> +# >> +miscfiles_domain_entry_test_files(finitmoddomain) >> +userdom_sysadm_entry_spec_domtrans_to(finitmoddomain) >> +miscfiles_domain_entry_test_files(initmoddomain) >> +userdom_sysadm_entry_spec_domtrans_to(initmoddomain) > > It seems that the finitmoddomain and initmoddomain type sets are > exactly the same except for names - can they be merged into just one > set of types? The AVC denials should be still easily distinguishable > by the comm= field if that's the intended purpose of the separation. Do you just mean coalesce the type attributes together or coalesce the individual types to which they refer? If the former, then the denials will still be distinguishable based on individual types; the attribute names are only used in policy not denials. Coalescing the attributes makes sense to me too. If the latter, the individual types differ in that test_finitmod_t is only allowed module_load to a specific file type (test_file_t), i.e. it can only load modules from files with that type via the finit_module(2), whereas test_initmod_t is allowed module_load to self as the fallback when using init_module(2) and hence can load a module content at all. So coalescing those would detract from testing.
On Mon, Nov 18, 2019 at 4:51 PM Stephen Smalley <sds@tycho.nsa.gov> wrote: > On 11/18/19 4:10 AM, Ondrej Mosnacek wrote: > > A couple comments below... > > > > On Fri, Nov 15, 2019 at 12:44 PM Richard Haines > > <richard_c_haines@btinternet.com> wrote: > <snip> > >> +########### Allow these domains to be entered from sysadm domain ############ > >> +# > >> +miscfiles_domain_entry_test_files(finitmoddomain) > >> +userdom_sysadm_entry_spec_domtrans_to(finitmoddomain) > >> +miscfiles_domain_entry_test_files(initmoddomain) > >> +userdom_sysadm_entry_spec_domtrans_to(initmoddomain) > > > > It seems that the finitmoddomain and initmoddomain type sets are > > exactly the same except for names - can they be merged into just one > > set of types? The AVC denials should be still easily distinguishable > > by the comm= field if that's the intended purpose of the separation. > > Do you just mean coalesce the type attributes together or coalesce the > individual types to which they refer? I meant the latter. > > If the former, then the denials will still be distinguishable based on > individual types; the attribute names are only used in policy not > denials. Coalescing the attributes makes sense to me too. > > If the latter, the individual types differ in that test_finitmod_t is > only allowed module_load to a specific file type (test_file_t), i.e. it > can only load modules from files with that type via the finit_module(2), > whereas test_initmod_t is allowed module_load to self as the fallback > when using init_module(2) and hence can load a module content at all. > So coalescing those would detract from testing. Ah, so there was a difference between them that I was missing :) In that case please disregard my comment. I don't have an opinion on whether to coalesce the attributes. It can stay as it is as far as I'm concerned.
diff --git a/policy/Makefile b/policy/Makefile index ff65153..545f3b5 100644 --- a/policy/Makefile +++ b/policy/Makefile @@ -90,6 +90,10 @@ ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms. TARGETS+=test_notify.te endif +ifeq ($(shell grep -q module_load $(POLDEV)/include/support/all_perms.spt && echo true),true) +TARGETS+=test_module_load.te +endif + ifeq (x$(DISTRO),$(filter x$(DISTRO),xRHEL4 xRHEL5 xRHEL6)) TARGETS:=$(filter-out test_overlayfs.te test_mqueue.te test_ibpkey.te, $(TARGETS)) endif diff --git a/policy/test_module_load.te b/policy/test_module_load.te new file mode 100644 index 0000000..566ddf7 --- /dev/null +++ b/policy/test_module_load.te @@ -0,0 +1,118 @@ +# +############################## Define Macro ################################ +# +# Replace domain_type() macro as it hides some relevant denials in audit.log +# +gen_require(` + type setrans_var_run_t, syslogd_t; +') + +define(`module_domain_type',` + allow $1 proc_t:dir { search }; + allow $1 proc_t:lnk_file { read }; + allow $1 self:dir { search }; + allow $1 self:file { open read write }; + dontaudit init_t syslogd_t:fd use; + dontaudit $1 security_t:filesystem getattr; + dontaudit $1 self:file getattr; + dontaudit $1 setrans_var_run_t:dir search; + dontaudit unconfined_t $1:process { noatsecure rlimitinh siginh }; +') + +# +############# Test kernel modules with finitmod_module(2) ################### +# +attribute finitmoddomain; + +type test_finitmod_t; +module_domain_type(test_finitmod_t) +unconfined_runs_test(test_finitmod_t) +typeattribute test_finitmod_t testdomain; +typeattribute test_finitmod_t finitmoddomain; + +allow test_finitmod_t self:capability { sys_module }; +allow test_finitmod_t test_file_t:system { module_load }; +allow test_finitmod_t kernel_t:system { module_request }; + +############### Deny cap sys_module ###################### +type test_finitmod_deny_sys_module_t; +module_domain_type(test_finitmod_deny_sys_module_t) +unconfined_runs_test(test_finitmod_deny_sys_module_t) +typeattribute test_finitmod_deny_sys_module_t testdomain; +typeattribute test_finitmod_deny_sys_module_t finitmoddomain; + +neverallow test_finitmod_deny_sys_module_t self:capability { sys_module }; + +############### Deny sys module_load ###################### +type test_finitmod_deny_module_load_t; +module_domain_type(test_finitmod_deny_module_load_t) +unconfined_runs_test(test_finitmod_deny_module_load_t) +typeattribute test_finitmod_deny_module_load_t testdomain; +typeattribute test_finitmod_deny_module_load_t finitmoddomain; + +allow test_finitmod_deny_module_load_t self:capability { sys_module }; +neverallow test_finitmod_deny_module_load_t test_file_t:system { module_load }; + +############### Deny sys module_request ###################### +type test_finitmod_deny_module_request_t; +module_domain_type(test_finitmod_deny_module_request_t) +unconfined_runs_test(test_finitmod_deny_module_request_t) +typeattribute test_finitmod_deny_module_request_t testdomain; +typeattribute test_finitmod_deny_module_request_t finitmoddomain; + +allow test_finitmod_deny_module_request_t self:capability { sys_module }; +allow test_finitmod_deny_module_request_t test_file_t:system { module_load }; +neverallow test_finitmod_deny_module_request_t kernel_t:system { module_request }; + +# +############# Test kernel modules with initmod_module(2) ################### +# +attribute initmoddomain; + +type test_initmod_t; +module_domain_type(test_initmod_t) +unconfined_runs_test(test_initmod_t) +typeattribute test_initmod_t testdomain; +typeattribute test_initmod_t initmoddomain; + +allow test_initmod_t self:capability { sys_module }; +allow test_initmod_t self:system { module_load }; +allow test_initmod_t kernel_t:system { module_request }; + +############### Deny cap sys_module ###################### +type test_initmod_deny_sys_module_t; +module_domain_type(test_initmod_deny_sys_module_t) +unconfined_runs_test(test_initmod_deny_sys_module_t) +typeattribute test_initmod_deny_sys_module_t testdomain; +typeattribute test_initmod_deny_sys_module_t initmoddomain; + +neverallow test_initmod_deny_sys_module_t self:capability { sys_module }; + +############### Deny sys module_load ###################### +type test_initmod_deny_module_load_t; +module_domain_type(test_initmod_deny_module_load_t) +unconfined_runs_test(test_initmod_deny_module_load_t) +typeattribute test_initmod_deny_module_load_t testdomain; +typeattribute test_initmod_deny_module_load_t initmoddomain; + +allow test_initmod_deny_module_load_t self:capability { sys_module }; +neverallow test_initmod_deny_module_load_t self:system { module_load }; + +############### Deny sys module_request ###################### +type test_initmod_deny_module_request_t; +module_domain_type(test_initmod_deny_module_request_t) +unconfined_runs_test(test_initmod_deny_module_request_t) +typeattribute test_initmod_deny_module_request_t testdomain; +typeattribute test_initmod_deny_module_request_t initmoddomain; + +allow test_initmod_deny_module_request_t self:capability { sys_module }; +allow test_initmod_deny_module_request_t self:system { module_load }; +neverallow test_initmod_deny_module_request_t kernel_t:system { module_request }; + +# +########### Allow these domains to be entered from sysadm domain ############ +# +miscfiles_domain_entry_test_files(finitmoddomain) +userdom_sysadm_entry_spec_domtrans_to(finitmoddomain) +miscfiles_domain_entry_test_files(initmoddomain) +userdom_sysadm_entry_spec_domtrans_to(initmoddomain) diff --git a/tests/Makefile b/tests/Makefile index 0021590..4731d8c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -68,6 +68,10 @@ ifeq ($(shell grep -q all_file_perms.*watch $(POLDEV)/include/support/all_perms. SUBDIRS+=notify endif +ifeq ($(shell grep -q module_load $(POLDEV)/include/support/all_perms.spt && echo true),true) +SUBDIRS+=module_load +endif + ifeq ($(DISTRO),RHEL4) SUBDIRS:=$(filter-out bounds dyntrace dyntrans inet_socket mmap nnp_nosuid overlay unix_socket, $(SUBDIRS)) endif diff --git a/tests/module_load/.gitignore b/tests/module_load/.gitignore new file mode 100644 index 0000000..7fa5772 --- /dev/null +++ b/tests/module_load/.gitignore @@ -0,0 +1,11 @@ +finit_load +init_load +modules.order +Module.symvers +*.a +*.o +*.ko +*.cmd +*.mod +*.mod.c +.*.cmd diff --git a/tests/module_load/Makefile b/tests/module_load/Makefile new file mode 100644 index 0000000..c561685 --- /dev/null +++ b/tests/module_load/Makefile @@ -0,0 +1,12 @@ +obj-m = setest_module_load.o setest_module_request.o + +TARGETS = finit_load init_load +LDLIBS += -lselinux +KDIR = /usr/src/kernels/$(shell uname -r) + +all: $(TARGETS) + $(MAKE) -C $(KDIR) M=$(PWD) + +clean: + rm -f $(TARGETS) + rm -f *.a *.o *.ko *.cmd *.mod *.mod.c .*.cmd Module.symvers modules.order diff --git a/tests/module_load/finit_load.c b/tests/module_load/finit_load.c new file mode 100644 index 0000000..029c698 --- /dev/null +++ b/tests/module_load/finit_load.c @@ -0,0 +1,94 @@ +#define _GNU_SOURCE 1 + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdbool.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/syscall.h> +#include <selinux/selinux.h> + +static void print_usage(char *progfile_name) +{ + fprintf(stderr, + "usage: %s [-v] path name\n" + "Where:\n\t" + "-v Print information.\n\t" + "path Kernel module build path.\n\t" + "name Name of kernel module to load.\n", progfile_name); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + char *context, file_name[PATH_MAX]; + int opt, result, fd; + bool verbose = false; + + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + verbose = true; + break; + default: + print_usage(argv[0]); + } + } + + if (optind >= argc) + print_usage(argv[0]); + + result = sprintf(file_name, "%s/%s.ko", argv[optind], + argv[optind + 1]); + if (result < 0) { + fprintf(stderr, "Failed sprintf\n"); + exit(-1); + } + + fd = open(file_name, O_RDONLY); + if (!fd) { + fprintf(stderr, "Failed to open %s: %s\n", + file_name, strerror(errno)); + exit(-1); + } + + result = getcon(&context); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + close(fd); + exit(-1); + } + + if (verbose) + printf("Process context:\n\t%s\n", context); + + free(context); + + result = syscall(__NR_finit_module, fd, "", 0); + if (result < 0) { + fprintf(stderr, "Failed to load '%s' module: %s\n", + file_name, strerror(errno)); + close(fd); + /* Denying: sys_module=EPERM, module_load=EACCES */ + exit(errno); + } + close(fd); + + if (verbose) + printf("Loaded kernel module: %s\n", file_name); + + result = syscall(__NR_delete_module, argv[optind + 1], 0); + if (result < 0) { + fprintf(stderr, "Failed to delete '%s' module: %s\n", + argv[optind + 1], strerror(errno)); + exit(-1); + } + + if (verbose) + printf("Deleted kernel module: %s\n", argv[optind + 1]); + + return 0; +} diff --git a/tests/module_load/init_load.c b/tests/module_load/init_load.c new file mode 100644 index 0000000..5f9997b --- /dev/null +++ b/tests/module_load/init_load.c @@ -0,0 +1,121 @@ +#define _GNU_SOURCE 1 + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdbool.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <selinux/selinux.h> + +static void print_usage(char *progfile_name) +{ + fprintf(stderr, + "usage: %s [-v] path name\n" + "Where:\n\t" + "-v Print information.\n\t" + "path Kernel module build path.\n\t" + "name Name of kernel module to load.\n", progfile_name); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + char *context, file_name[PATH_MAX]; + int opt, result, fd; + bool verbose = false; + void *elf_image; + struct stat st; + + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + verbose = true; + break; + default: + print_usage(argv[0]); + } + } + + if (optind >= argc) + print_usage(argv[0]); + + result = sprintf(file_name, "%s/%s.ko", argv[optind], + argv[optind + 1]); + if (result < 0) { + fprintf(stderr, "Failed sprintf\n"); + exit(-1); + } + + fd = open(file_name, O_RDONLY); + if (!fd) { + fprintf(stderr, "Failed to open %s: %s\n", + file_name, strerror(errno)); + exit(-1); + } + + result = getcon(&context); + if (result < 0) { + fprintf(stderr, "Failed to obtain process context\n"); + close(fd); + exit(-1); + } + + if (verbose) + printf("Process context:\n\t%s\n", context); + + free(context); + + result = fstat(fd, &st); + if (result < 0) { + fprintf(stderr, "Failed fstat on %s: %s\n", + file_name, strerror(errno)); + close(fd); + exit(-1); + } + + elf_image = malloc(st.st_size); + if (!elf_image) { + fprintf(stderr, "Failed malloc on %s: %s\n", + file_name, strerror(errno)); + close(fd); + exit(-1); + } + + result = read(fd, elf_image, st.st_size); + if (result < 0) { + fprintf(stderr, "Failed read on %s: %s\n", + file_name, strerror(errno)); + close(fd); + exit(-1); + } + close(fd); + + result = syscall(__NR_init_module, elf_image, st.st_size, ""); + if (result < 0) { + fprintf(stderr, "Failed to load '%s' module: %s\n", + file_name, strerror(errno)); + /* Denying: sys_module=EPERM, module_load & request=EACCES */ + exit(errno); + } + free(elf_image); + + if (verbose) + printf("Loaded kernel module: %s\n", file_name); + + result = syscall(__NR_delete_module, argv[optind + 1], 0); + if (result < 0) { + fprintf(stderr, "Failed to delete '%s' module: %s\n", + argv[optind + 1], strerror(errno)); + exit(-1); + } + + if (verbose) + printf("Deleted kernel module: %s\n", argv[optind + 1]); + + return 0; +} diff --git a/tests/module_load/setest_module_load.c b/tests/module_load/setest_module_load.c new file mode 100644 index 0000000..0be7a26 --- /dev/null +++ b/tests/module_load/setest_module_load.c @@ -0,0 +1,18 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> + +static int __init setest_module_load_init(void) +{ + pr_info("INIT - setest_module_load\n"); + return 0; +} + +static void __exit setest_module_load_exit(void) +{ + pr_info("EXIT - setest_module_load\n"); +} + +module_init(setest_module_load_init); +module_exit(setest_module_load_exit); +MODULE_LICENSE("GPL"); diff --git a/tests/module_load/setest_module_request.c b/tests/module_load/setest_module_request.c new file mode 100644 index 0000000..f79d4ef --- /dev/null +++ b/tests/module_load/setest_module_request.c @@ -0,0 +1,22 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> + +static int __init setest_module_request_init(void) +{ + int result; + + pr_info("INIT - setest_module_request\n"); + result = request_module_nowait("dummy-module"); + pr_info("request_module() returned: %d\n", result); + return result; +} + +static void __exit setest_module_request_exit(void) +{ + pr_info("EXIT - setest_module_request\n"); +} + +module_init(setest_module_request_init); +module_exit(setest_module_request_exit); +MODULE_LICENSE("GPL"); diff --git a/tests/module_load/test b/tests/module_load/test new file mode 100755 index 0000000..c3242fc --- /dev/null +++ b/tests/module_load/test @@ -0,0 +1,62 @@ +#!/usr/bin/perl +use Test::More; + +BEGIN { + $basedir = $0; + $basedir =~ s|(.*)/[^/]*|$1|; + + # allow info to be shown during tests + $v = $ARGV[0]; + if ($v) { + if ( $v ne "-v" ) { + plan skip_all => "Invalid option (use -v)"; + } + } + else { + $v = " "; + } + + plan tests => 8; +} + +print "Test finit_module(2)\n"; +$result = system +"runcon -t test_finitmod_t $basedir/finit_load $v $basedir setest_module_request"; +ok( $result eq 0 ); + +# Deny capability { sys_module } - EPERM +$result = system +"runcon -t test_finitmod_deny_sys_module_t $basedir/finit_load $v $basedir setest_module_load 2>&1"; +ok( $result >> 8 eq 1 ); + +# Deny system { module_load } - EACCES +$result = system +"runcon -t test_finitmod_deny_module_load_t $basedir/finit_load $v $basedir setest_module_load 2>&1"; +ok( $result >> 8 eq 13 ); + +# Deny system { module_request } - EACCES +$result = system +"runcon -t test_finitmod_deny_module_request_t $basedir/finit_load $v $basedir setest_module_request 2>&1"; +ok( $result >> 8 eq 13 ); + +print "Test init_module(2)\n"; +$result = system +"runcon -t test_initmod_t $basedir/init_load $v $basedir setest_module_request"; +ok( $result eq 0 ); + +# Deny capability { sys_module } - EPERM +$result = system +"runcon -t test_initmod_deny_sys_module_t $basedir/init_load $v $basedir setest_module_load 2>&1"; +ok( $result >> 8 eq 1 ); + +# Deny system { module_load } - EACCES +$result = system +"runcon -t test_initmod_deny_module_load_t $basedir/init_load $v $basedir setest_module_load 2>&1"; +ok( $result >> 8 eq 13 ); + +# Deny system { module_request } - EACCES +$result = system +"runcon -t test_initmod_deny_module_request_t $basedir/init_load $v $basedir setest_module_request 2>&1"; +ok( $result >> 8 eq 13 ); + +exit;
Test kernel module loading permissions. Signed-off-by: Richard Haines <richard_c_haines@btinternet.com> --- V2 Change: Check permission denial module_load versus module_request by using a test kernel module for each. Note: Rawhide (with secnext kernel) adds built-in.a and built-in.a.cmd when building modules, therefore added to Makefile and .gitignore. policy/Makefile | 4 + policy/test_module_load.te | 118 +++++++++++++++++++++ tests/Makefile | 4 + tests/module_load/.gitignore | 11 ++ tests/module_load/Makefile | 12 +++ tests/module_load/finit_load.c | 94 +++++++++++++++++ tests/module_load/init_load.c | 121 ++++++++++++++++++++++ tests/module_load/setest_module_load.c | 18 ++++ tests/module_load/setest_module_request.c | 22 ++++ tests/module_load/test | 62 +++++++++++ 10 files changed, 466 insertions(+) create mode 100644 policy/test_module_load.te create mode 100644 tests/module_load/.gitignore create mode 100644 tests/module_load/Makefile create mode 100644 tests/module_load/finit_load.c create mode 100644 tests/module_load/init_load.c create mode 100644 tests/module_load/setest_module_load.c create mode 100644 tests/module_load/setest_module_request.c create mode 100755 tests/module_load/test