Re: [PATCH V2] selinux-testsuite: Add kernel module tests

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A couple comments below...

On Fri, Nov 15, 2019 at 12:44 PM Richard Haines
<richard_c_haines@xxxxxxxxxxxxxx> wrote:
> Test kernel module loading permissions.
>
> Signed-off-by: Richard Haines <richard_c_haines@xxxxxxxxxxxxxx>
> ---
> 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.





[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux