1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/security.h>
15#include <linux/cred.h>
16
17#include "lsm.h"
18
19static struct dentry *safesetid_policy_dir;
20
21struct safesetid_file_entry {
22 const char *name;
23 enum safesetid_whitelist_file_write_type type;
24 struct dentry *dentry;
25};
26
27static struct safesetid_file_entry safesetid_files[] = {
28 {.name = "add_whitelist_policy",
29 .type = SAFESETID_WHITELIST_ADD},
30 {.name = "flush_whitelist_policies",
31 .type = SAFESETID_WHITELIST_FLUSH},
32};
33
34
35
36
37
38
39static int parse_safesetid_whitelist_policy(const char __user *buf,
40 size_t len,
41 kuid_t *parent,
42 kuid_t *child)
43{
44 char *kern_buf;
45 char *parent_buf;
46 char *child_buf;
47 const char separator[] = ":";
48 int ret;
49 size_t first_substring_length;
50 long parsed_parent;
51 long parsed_child;
52
53
54 kern_buf = memdup_user_nul(buf, len);
55 if (IS_ERR(kern_buf))
56 return PTR_ERR(kern_buf);
57
58
59
60
61
62 first_substring_length = strcspn(kern_buf, separator);
63 if (first_substring_length == 0 || first_substring_length == len) {
64 ret = -EINVAL;
65 goto free_kern;
66 }
67
68 parent_buf = kmemdup_nul(kern_buf, first_substring_length, GFP_KERNEL);
69 if (!parent_buf) {
70 ret = -ENOMEM;
71 goto free_kern;
72 }
73
74 ret = kstrtol(parent_buf, 0, &parsed_parent);
75 if (ret)
76 goto free_both;
77
78 child_buf = kern_buf + first_substring_length + 1;
79 ret = kstrtol(child_buf, 0, &parsed_child);
80 if (ret)
81 goto free_both;
82
83 *parent = make_kuid(current_user_ns(), parsed_parent);
84 if (!uid_valid(*parent)) {
85 ret = -EINVAL;
86 goto free_both;
87 }
88
89 *child = make_kuid(current_user_ns(), parsed_child);
90 if (!uid_valid(*child)) {
91 ret = -EINVAL;
92 goto free_both;
93 }
94
95free_both:
96 kfree(parent_buf);
97free_kern:
98 kfree(kern_buf);
99 return ret;
100}
101
102static ssize_t safesetid_file_write(struct file *file,
103 const char __user *buf,
104 size_t len,
105 loff_t *ppos)
106{
107 struct safesetid_file_entry *file_entry =
108 file->f_inode->i_private;
109 kuid_t parent;
110 kuid_t child;
111 int ret;
112
113 if (!ns_capable(current_user_ns(), CAP_MAC_ADMIN))
114 return -EPERM;
115
116 if (*ppos != 0)
117 return -EINVAL;
118
119 switch (file_entry->type) {
120 case SAFESETID_WHITELIST_FLUSH:
121 flush_safesetid_whitelist_entries();
122 break;
123 case SAFESETID_WHITELIST_ADD:
124 ret = parse_safesetid_whitelist_policy(buf, len, &parent,
125 &child);
126 if (ret)
127 return ret;
128
129 ret = add_safesetid_whitelist_entry(parent, child);
130 if (ret)
131 return ret;
132 break;
133 default:
134 pr_warn("Unknown securityfs file %d\n", file_entry->type);
135 break;
136 }
137
138
139 return len;
140}
141
142static const struct file_operations safesetid_file_fops = {
143 .write = safesetid_file_write,
144};
145
146static void safesetid_shutdown_securityfs(void)
147{
148 int i;
149
150 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
151 struct safesetid_file_entry *entry =
152 &safesetid_files[i];
153 securityfs_remove(entry->dentry);
154 entry->dentry = NULL;
155 }
156
157 securityfs_remove(safesetid_policy_dir);
158 safesetid_policy_dir = NULL;
159}
160
161static int __init safesetid_init_securityfs(void)
162{
163 int i;
164 int ret;
165
166 if (!safesetid_initialized)
167 return 0;
168
169 safesetid_policy_dir = securityfs_create_dir("safesetid", NULL);
170 if (IS_ERR(safesetid_policy_dir)) {
171 ret = PTR_ERR(safesetid_policy_dir);
172 goto error;
173 }
174
175 for (i = 0; i < ARRAY_SIZE(safesetid_files); ++i) {
176 struct safesetid_file_entry *entry =
177 &safesetid_files[i];
178 entry->dentry = securityfs_create_file(
179 entry->name, 0200, safesetid_policy_dir,
180 entry, &safesetid_file_fops);
181 if (IS_ERR(entry->dentry)) {
182 ret = PTR_ERR(entry->dentry);
183 goto error;
184 }
185 }
186
187 return 0;
188
189error:
190 safesetid_shutdown_securityfs();
191 return ret;
192}
193fs_initcall(safesetid_init_securityfs);
194