aboutsummaryrefslogtreecommitdiffstats
path: root/security/securelevel.c
blob: 7128430d4a0f560cc3fe9b1ed3eb7e6d7e838f08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 *  securelevel.c - support for generic kernel lockdown
 *
 *  Copyright Nebula, Inc <matthew.garrett@nebula.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 */

#include <linux/fs.h>
#include <linux/init.h>
#include <linux/security.h>
#include <linux/uaccess.h>

static int securelevel;

static DEFINE_SPINLOCK(securelevel_lock);

#define MAX_SECURELEVEL 1

int get_securelevel(void)
{
	return securelevel;
}
EXPORT_SYMBOL(get_securelevel);

int set_securelevel(int new_securelevel)
{
	int ret = 0;

	spin_lock(&securelevel_lock);

	if ((securelevel == -1) || (new_securelevel < securelevel) ||
	    (new_securelevel > MAX_SECURELEVEL)) {
		ret = -EINVAL;
		goto out;
	}

	securelevel = new_securelevel;
out:
	spin_unlock(&securelevel_lock);
	return ret;
}
EXPORT_SYMBOL(set_securelevel);

static ssize_t securelevel_read(struct file *filp, char __user *buf,
				size_t count, loff_t *ppos)
{
	char tmpbuf[12];
	ssize_t length;

	length = scnprintf(tmpbuf, sizeof(tmpbuf), "%d", securelevel);
	return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}

static ssize_t securelevel_write(struct file *file, const char __user *buf,
				 size_t count, loff_t *ppos)
{
	char *page = NULL;
	ssize_t length;
	int new_securelevel;

	length = -ENOMEM;
	if (count >= PAGE_SIZE)
		goto out;

	length = -EINVAL;
	if (*ppos != 0)
		goto out;

	length = -ENOMEM;
	page = (char *)get_zeroed_page(GFP_KERNEL);
	if (!page)
		goto out;

	length = -EFAULT;
	if (copy_from_user(page, buf, count))
		goto out;

	length = -EINVAL;
	if (sscanf(page, "%d", &new_securelevel) != 1)
		goto out;

	length = set_securelevel(new_securelevel);
	if (length)
		goto out;

	length = count;
out:
	free_page((unsigned long) page);
	return length;
}

static const struct file_operations securelevel_fops = {
	.read 	= securelevel_read,
	.write 	= securelevel_write,
	.llseek	= generic_file_llseek,
};

static __init int setup_securelevel(void)
{
	struct dentry *securelevel_file;

	securelevel_file = securityfs_create_file("securelevel",
						  S_IWUSR | S_IRUGO,
						  NULL, NULL,
						  &securelevel_fops);

	if (IS_ERR(securelevel_file))
		return PTR_ERR(securelevel_file);

	return 0;
}
late_initcall(setup_securelevel);