diff mbox series

[3/3] Add lockdown checks to init_module* and finit_module* tests

Message ID 20210720103941.9767-3-mdoucha@suse.cz
State Accepted
Headers show
Series [1/3] Add skip_in_lockdown flag to struct tst_test | expand

Commit Message

Martin Doucha July 20, 2021, 10:39 a.m. UTC
Signed-off-by: Martin Doucha <mdoucha@suse.cz>
---
 .../syscalls/finit_module/finit_module01.c    |  2 ++
 .../syscalls/finit_module/finit_module02.c    | 23 +++++++++++++--
 .../syscalls/init_module/init_module01.c      |  2 ++
 .../syscalls/init_module/init_module02.c      | 29 +++++++++++++------
 4 files changed, 44 insertions(+), 12 deletions(-)

Comments

Cyril Hrubis July 20, 2021, 12:02 p.m. UTC | #1
Hi!
> +static void lockdown_setup(struct tcase *tc)
> +{
> +	if (kernel_lockdown)
> +		tc->exp_errno = EPERM;
> +}
> +
>  static struct tcase tcases[] = {
>  	{"invalid-fd", &fd_invalid, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, bad_fd_setup},
>  	{"zero-fd", &fd_zero, "", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
> -	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT, NULL},
> -	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
> +	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT,
> +		lockdown_setup},
> +	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0,
> +		EINVAL, lockdown_setup},
>  	{"invalid-flags", &fd, "", O_RDONLY | O_CLOEXEC, -1, 0, EINVAL, NULL},
>  	{"no-perm", &fd, "", O_RDONLY | O_CLOEXEC, 0, 1, EPERM, NULL},
>  	{"module-exists", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EEXIST, NULL},

I'm slightly afraid that the order of checks may change over the time
and we will get EPERM in all these cases, but maybe I'm just overly
cautious. Other than this the code looks good.
Martin Doucha July 20, 2021, 12:36 p.m. UTC | #2
On 20. 07. 21 14:02, Cyril Hrubis wrote:
> Hi!
>> +static void lockdown_setup(struct tcase *tc)
>> +{
>> +	if (kernel_lockdown)
>> +		tc->exp_errno = EPERM;
>> +}
>> +
>>  static struct tcase tcases[] = {
>>  	{"invalid-fd", &fd_invalid, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, bad_fd_setup},
>>  	{"zero-fd", &fd_zero, "", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
>> -	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT, NULL},
>> -	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
>> +	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT,
>> +		lockdown_setup},
>> +	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0,
>> +		EINVAL, lockdown_setup},
>>  	{"invalid-flags", &fd, "", O_RDONLY | O_CLOEXEC, -1, 0, EINVAL, NULL},
>>  	{"no-perm", &fd, "", O_RDONLY | O_CLOEXEC, 0, 1, EPERM, NULL},
>>  	{"module-exists", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EEXIST, NULL},
> 
> I'm slightly afraid that the order of checks may change over the time
> and we will get EPERM in all these cases, but maybe I'm just overly
> cautious. Other than this the code looks good.

I don't think we need to worry about that. With root privileges, the
EPERM error is returned when a kernel module does not have a valid
signature. How would something that is not even a valid kernel module in
the first place fail that check?

The only subtests that actually try to load a valid kernel module are
null-param, invalid-param and module-exists. All three of them now
handle lockdown correctly.
Cyril Hrubis July 20, 2021, 12:45 p.m. UTC | #3
Hi!
> > I'm slightly afraid that the order of checks may change over the time
> > and we will get EPERM in all these cases, but maybe I'm just overly
> > cautious. Other than this the code looks good.
> 
> I don't think we need to worry about that. With root privileges, the
> EPERM error is returned when a kernel module does not have a valid
> signature. How would something that is not even a valid kernel module in
> the first place fail that check?
>
> The only subtests that actually try to load a valid kernel module are
> null-param, invalid-param and module-exists. All three of them now
> handle lockdown correctly.

Right, we have to be able to read the signature in order to produce
EPERM and the same for the init_module() there has to be a pointer to
a module data that kernel can check the signature from.

Patch pushed, thanks.
Petr Vorel July 26, 2021, 6:05 a.m. UTC | #4
Hi Martin,

thanks a lot for fixing this.

Kind regards,
Petr
diff mbox series

Patch

diff --git a/testcases/kernel/syscalls/finit_module/finit_module01.c b/testcases/kernel/syscalls/finit_module/finit_module01.c
index 9c34282e1..21c35f101 100644
--- a/testcases/kernel/syscalls/finit_module/finit_module01.c
+++ b/testcases/kernel/syscalls/finit_module/finit_module01.c
@@ -51,4 +51,6 @@  static struct tst_test test = {
 	.setup = setup,
 	.cleanup = cleanup,
 	.needs_root = 1,
+	/* lockdown requires signed modules */
+	.skip_in_lockdown = 1,
 };
diff --git a/testcases/kernel/syscalls/finit_module/finit_module02.c b/testcases/kernel/syscalls/finit_module/finit_module02.c
index 9d9255c6d..503c8e994 100644
--- a/testcases/kernel/syscalls/finit_module/finit_module02.c
+++ b/testcases/kernel/syscalls/finit_module/finit_module02.c
@@ -25,6 +25,7 @@ 
 static char *mod_path;
 
 static int fd, fd_zero, fd_invalid = -1, fd_dir;
+static int kernel_lockdown;
 
 static struct tst_cap cap_req = TST_CAP(TST_CAP_REQ, CAP_SYS_MODULE);
 static struct tst_cap cap_drop = TST_CAP(TST_CAP_DROP, CAP_SYS_MODULE);
@@ -64,11 +65,19 @@  static void dir_setup(struct tcase *tc)
 		tc->exp_errno = EINVAL;
 }
 
+static void lockdown_setup(struct tcase *tc)
+{
+	if (kernel_lockdown)
+		tc->exp_errno = EPERM;
+}
+
 static struct tcase tcases[] = {
 	{"invalid-fd", &fd_invalid, "", O_RDONLY | O_CLOEXEC, 0, 0, 0, bad_fd_setup},
 	{"zero-fd", &fd_zero, "", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
-	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT, NULL},
-	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0, EINVAL, NULL},
+	{"null-param", &fd, NULL, O_RDONLY | O_CLOEXEC, 0, 0, EFAULT,
+		lockdown_setup},
+	{"invalid-param", &fd, "status=invalid", O_RDONLY | O_CLOEXEC, 0, 0,
+		EINVAL, lockdown_setup},
 	{"invalid-flags", &fd, "", O_RDONLY | O_CLOEXEC, -1, 0, EINVAL, NULL},
 	{"no-perm", &fd, "", O_RDONLY | O_CLOEXEC, 0, 1, EPERM, NULL},
 	{"module-exists", &fd, "", O_RDONLY | O_CLOEXEC, 0, 0, EEXIST, NULL},
@@ -84,6 +93,7 @@  static void setup(void)
 
 	tst_module_exists(MODULE_NAME, &mod_path);
 
+	kernel_lockdown = tst_lockdown_enabled();
 	SAFE_MKDIR(TEST_DIR, 0700);
 	fd_dir = SAFE_OPEN(TEST_DIR, O_DIRECTORY);
 
@@ -108,8 +118,15 @@  static void run(unsigned int n)
 		tst_cap_action(&cap_drop);
 
 	/* Insert module twice */
-	if (tc->exp_errno == EEXIST)
+	if (tc->exp_errno == EEXIST) {
+		if (kernel_lockdown) {
+			tst_res(TCONF, "Kernel is locked down, skipping %s",
+				tc->name);
+			return;
+		}
+
 		tst_module_load(MODULE_NAME, NULL);
+	}
 
 	TST_EXP_FAIL(finit_module(*tc->fd, tc->param, tc->flags), tc->exp_errno,
 		     "TestName: %s", tc->name);
diff --git a/testcases/kernel/syscalls/init_module/init_module01.c b/testcases/kernel/syscalls/init_module/init_module01.c
index 2f47eed32..79e567cd6 100644
--- a/testcases/kernel/syscalls/init_module/init_module01.c
+++ b/testcases/kernel/syscalls/init_module/init_module01.c
@@ -53,4 +53,6 @@  static struct tst_test test = {
 	.setup = setup,
 	.cleanup = cleanup,
 	.needs_root = 1,
+	/* lockdown requires signed modules */
+	.skip_in_lockdown = 1,
 };
diff --git a/testcases/kernel/syscalls/init_module/init_module02.c b/testcases/kernel/syscalls/init_module/init_module02.c
index 3953f4f61..dac99a4da 100644
--- a/testcases/kernel/syscalls/init_module/init_module02.c
+++ b/testcases/kernel/syscalls/init_module/init_module02.c
@@ -22,6 +22,7 @@ 
 #define MODULE_NAME	"init_module.ko"
 
 static unsigned long size, zero_size;
+static int kernel_lockdown;
 static void *buf, *faulty_buf, *null_buf;
 
 static struct tst_cap cap_req = TST_CAP(TST_CAP_REQ, CAP_SYS_MODULE);
@@ -34,14 +35,15 @@  static struct tcase {
 	const char *param;
 	int cap;
 	int exp_errno;
+	int lockdown_errno;
 } tcases[] = {
-	{"NULL-buffer", &null_buf, &size, "", 0, EFAULT},
-	{"faulty-buffer", &faulty_buf, &size, "", 0, EFAULT},
-	{"null-param", &buf, &size, NULL, 0, EFAULT},
-	{"zero-size", &buf, &zero_size, "", 0, ENOEXEC},
-	{"invalid_param", &buf, &size, "status=invalid", 0, EINVAL},
-	{"no-perm", &buf, &size, "", 1, EPERM},
-	{"module-exists", &buf, &size, "", 0, EEXIST},
+	{"NULL-buffer", &null_buf, &size, "", 0, EFAULT, EFAULT},
+	{"faulty-buffer", &faulty_buf, &size, "", 0, EFAULT, EFAULT},
+	{"null-param", &buf, &size, NULL, 0, EFAULT, EPERM},
+	{"zero-size", &buf, &zero_size, "", 0, ENOEXEC, ENOEXEC},
+	{"invalid_param", &buf, &size, "status=invalid", 0, EINVAL, EPERM},
+	{"no-perm", &buf, &size, "", 1, EPERM, EPERM},
+	{"module-exists", &buf, &size, "", 0, EEXIST, EPERM},
 };
 
 static void setup(void)
@@ -51,6 +53,7 @@  static void setup(void)
 
 	tst_module_exists(MODULE_NAME, NULL);
 
+	kernel_lockdown = tst_lockdown_enabled();
 	fd = SAFE_OPEN(MODULE_NAME, O_RDONLY|O_CLOEXEC);
 	SAFE_FSTAT(fd, &sb);
 	size = sb.st_size;
@@ -68,10 +71,18 @@  static void run(unsigned int n)
 		tst_cap_action(&cap_drop);
 
 	/* Insert module twice */
-	if (tc->exp_errno == EEXIST)
+	if (tc->exp_errno == EEXIST) {
+		if (kernel_lockdown) {
+			tst_res(TCONF, "Kernel is locked down, skipping %s",
+				tc->name);
+			return;
+		}
+
 		tst_module_load(MODULE_NAME, NULL);
+	}
 
-	TST_EXP_FAIL(init_module(*tc->buf, *tc->size, tc->param), tc->exp_errno,
+	TST_EXP_FAIL(init_module(*tc->buf, *tc->size, tc->param),
+		     kernel_lockdown ? tc->lockdown_errno : tc->exp_errno,
 		     "TestName: %s", tc->name);
 
 	if (tc->exp_errno == EEXIST)