// SPDX-License-Identifier: GPL-2.0
/*
 * Linux Security Module infrastructure tests
 * Tests for the lsm_list_modules system call
 *
 * Copyright © 2022 Casey Schaufler <casey@schaufler-ca.com>
 */

#define _GNU_SOURCE
#include <linux/lsm.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "../kselftest_harness.h"
#include "common.h"

TEST(size_null_lsm_list_modules)
{
	const long page_size = sysconf(_SC_PAGESIZE);
	__u64 *syscall_lsms = calloc(page_size, 1);

	ASSERT_NE(NULL, syscall_lsms);
	errno = 0;
	ASSERT_EQ(-1, lsm_list_modules(syscall_lsms, NULL, 0));
	ASSERT_EQ(EFAULT, errno);

	free(syscall_lsms);
}

TEST(ids_null_lsm_list_modules)
{
	const long page_size = sysconf(_SC_PAGESIZE);
	__u32 size = page_size;

	errno = 0;
	ASSERT_EQ(-1, lsm_list_modules(NULL, &size, 0));
	ASSERT_EQ(EFAULT, errno);
	ASSERT_NE(1, size);
}

TEST(size_too_small_lsm_list_modules)
{
	const long page_size = sysconf(_SC_PAGESIZE);
	__u64 *syscall_lsms = calloc(page_size, 1);
	__u32 size = 1;

	ASSERT_NE(NULL, syscall_lsms);
	errno = 0;
	ASSERT_EQ(-1, lsm_list_modules(syscall_lsms, &size, 0));
	ASSERT_EQ(E2BIG, errno);
	ASSERT_NE(1, size);

	free(syscall_lsms);
}

TEST(flags_set_lsm_list_modules)
{
	const long page_size = sysconf(_SC_PAGESIZE);
	__u64 *syscall_lsms = calloc(page_size, 1);
	__u32 size = page_size;

	ASSERT_NE(NULL, syscall_lsms);
	errno = 0;
	ASSERT_EQ(-1, lsm_list_modules(syscall_lsms, &size, 7));
	ASSERT_EQ(EINVAL, errno);
	ASSERT_EQ(page_size, size);

	free(syscall_lsms);
}

TEST(correct_lsm_list_modules)
{
	const long page_size = sysconf(_SC_PAGESIZE);
	__u32 size = page_size;
	__u64 *syscall_lsms = calloc(page_size, 1);
	char *sysfs_lsms = calloc(page_size, 1);
	char *name;
	char *cp;
	int count;
	int i;

	ASSERT_NE(NULL, sysfs_lsms);
	ASSERT_NE(NULL, syscall_lsms);
	ASSERT_EQ(0, read_sysfs_lsms(sysfs_lsms, page_size));

	count = lsm_list_modules(syscall_lsms, &size, 0);
	ASSERT_LE(1, count);
	cp = sysfs_lsms;
	for (i = 0; i < count; i++) {
		switch (syscall_lsms[i]) {
		case LSM_ID_CAPABILITY:
			name = "capability";
			break;
		case LSM_ID_SELINUX:
			name = "selinux";
			break;
		case LSM_ID_SMACK:
			name = "smack";
			break;
		case LSM_ID_TOMOYO:
			name = "tomoyo";
			break;
		case LSM_ID_APPARMOR:
			name = "apparmor";
			break;
		case LSM_ID_YAMA:
			name = "yama";
			break;
		case LSM_ID_LOADPIN:
			name = "loadpin";
			break;
		case LSM_ID_SAFESETID:
			name = "safesetid";
			break;
		case LSM_ID_LOCKDOWN:
			name = "lockdown";
			break;
		case LSM_ID_BPF:
			name = "bpf";
			break;
		case LSM_ID_LANDLOCK:
			name = "landlock";
			break;
		default:
			name = "INVALID";
			break;
		}
		ASSERT_EQ(0, strncmp(cp, name, strlen(name)));
		cp += strlen(name) + 1;
	}

	free(sysfs_lsms);
	free(syscall_lsms);
}

TEST_HARNESS_MAIN
