2.1.1
@@ -0,0 +1,233 @@ | ||
1 | +argv[0] check functions for TOMOYO Linux. | |
2 | +If the executed program name and argv[0] is different, | |
3 | +TOMOYO Linux checks permission. | |
4 | + | |
5 | +Each permission can be automatically accumulated into | |
6 | +the policy of each domain using 'learning mode'. | |
7 | + | |
8 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
9 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
10 | + security/tomoyo/exec.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
11 | + 1 file changed, 218 insertions(+) | |
12 | + | |
13 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
14 | ++++ linux-2.6.23/security/tomoyo/exec.c 2007-11-09 17:16:26.000000000 +0900 | |
15 | +@@ -0,0 +1,218 @@ | |
16 | ++/* | |
17 | ++ * security/tomoyo/exec.c | |
18 | ++ * | |
19 | ++ * Argv0 access control functions for TOMOYO Linux. | |
20 | ++ */ | |
21 | ++ | |
22 | ++#include "tomoyo.h" | |
23 | ++#include "realpath.h" | |
24 | ++ | |
25 | ++/************************* AUDIT FUNCTIONS *************************/ | |
26 | ++ | |
27 | ++static int tmy_audit_argv0_log(const struct path_info *filename, | |
28 | ++ const char *argv0, | |
29 | ++ const bool is_granted, | |
30 | ++ const u8 profile, | |
31 | ++ const unsigned int mode) | |
32 | ++{ | |
33 | ++ char *buf; | |
34 | ++ int len; | |
35 | ++ | |
36 | ++ if (is_granted) { | |
37 | ++ if (!tmy_audit_grant()) | |
38 | ++ return 0; | |
39 | ++ } else { | |
40 | ++ if (!tmy_audit_reject()) | |
41 | ++ return 0; | |
42 | ++ } | |
43 | ++ | |
44 | ++ len = filename->total_len + strlen(argv0) + 8; | |
45 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
46 | ++ | |
47 | ++ if (!buf) | |
48 | ++ return -ENOMEM; | |
49 | ++ | |
50 | ++ snprintf(buf + strlen(buf), | |
51 | ++ len - strlen(buf) - 1, | |
52 | ++ TMY_ALLOW_ARGV0 "%s %s", | |
53 | ++ filename->name, | |
54 | ++ argv0); | |
55 | ++ | |
56 | ++ return tmy_write_audit_log(buf, is_granted); | |
57 | ++} | |
58 | ++ | |
59 | ++/************************* ARGV0 MISMATCH HANDLER *************************/ | |
60 | ++ | |
61 | ++static int tmy_add_argv0_entry(const char *filename, | |
62 | ++ const char *argv0, | |
63 | ++ struct domain_info *domain, | |
64 | ++ const struct condition_list *cond, | |
65 | ++ const bool is_delete) | |
66 | ++{ | |
67 | ++ struct acl_info *ptr; | |
68 | ++ struct argv0_acl *acl; | |
69 | ++ const struct path_info *saved_filename; | |
70 | ++ const struct path_info *saved_argv0; | |
71 | ++ int error = -ENOMEM; | |
72 | ++ | |
73 | ++ if (!tmy_correct_path(filename, 1, 0, -1, __FUNCTION__) || | |
74 | ++ !tmy_correct_path(argv0, -1, 0, -1, __FUNCTION__) || | |
75 | ++ strchr(argv0, '/')) | |
76 | ++ return -EINVAL; | |
77 | ++ | |
78 | ++ saved_filename = tmy_save_name(filename); | |
79 | ++ saved_argv0 = tmy_save_name(argv0); | |
80 | ++ if (!saved_filename || !saved_argv0) | |
81 | ++ return -ENOMEM; | |
82 | ++ | |
83 | ++ mutex_lock(&domain_acl_lock); | |
84 | ++ | |
85 | ++ if (is_delete) | |
86 | ++ goto remove; | |
87 | ++ | |
88 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
89 | ++ acl = (struct argv0_acl *) ptr; | |
90 | ++ if (ptr->type == TMY_TYPE_ARGV0_ACL && ptr->cond == cond && | |
91 | ++ acl->filename == saved_filename && | |
92 | ++ acl->argv0 == saved_argv0) { | |
93 | ++ ptr->is_deleted = 0; | |
94 | ++ /* Found. Nothing to do. */ | |
95 | ++ error = 0; | |
96 | ++ goto ok; | |
97 | ++ } | |
98 | ++ } | |
99 | ++ | |
100 | ++ /* Not found. Append it to the tail. */ | |
101 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
102 | ++ if (!acl) | |
103 | ++ goto ok; | |
104 | ++ | |
105 | ++ acl->head.type = TMY_TYPE_ARGV0_ACL; | |
106 | ++ acl->head.cond = cond; | |
107 | ++ acl->filename = saved_filename; | |
108 | ++ acl->argv0 = saved_argv0; | |
109 | ++ error = tmy_add_acl(domain, | |
110 | ++ (struct acl_info *) acl); | |
111 | ++ goto ok; | |
112 | ++remove: ; | |
113 | ++ error = -ENOENT; | |
114 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
115 | ++ acl = (struct argv0_acl *) ptr; | |
116 | ++ if (ptr->type != TMY_TYPE_ARGV0_ACL || | |
117 | ++ ptr->cond != cond || ptr->is_deleted || | |
118 | ++ acl->filename != saved_filename || | |
119 | ++ acl->argv0 != saved_argv0) | |
120 | ++ continue; | |
121 | ++ | |
122 | ++ error = tmy_del_acl(ptr); | |
123 | ++ break; | |
124 | ++ } | |
125 | ++ok: ; | |
126 | ++ mutex_unlock(&domain_acl_lock); | |
127 | ++ | |
128 | ++ return error; | |
129 | ++} | |
130 | ++ | |
131 | ++static int tmy_argv0_acl(const struct path_info *filename, | |
132 | ++ const char *argv0_) | |
133 | ++{ | |
134 | ++ const struct domain_info *domain = TMY_SECURITY->domain; | |
135 | ++ int error = -EPERM; | |
136 | ++ struct acl_info *ptr; | |
137 | ++ struct path_info argv0; | |
138 | ++ | |
139 | ++ if (!tmy_flags(TMY_MAC_FOR_ARGV0)) | |
140 | ++ return 0; | |
141 | ++ | |
142 | ++ argv0.name = argv0_; | |
143 | ++ tmy_fill_path_info(&argv0); | |
144 | ++ | |
145 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
146 | ++ struct argv0_acl *acl = (struct argv0_acl *) ptr; | |
147 | ++ | |
148 | ++ if (ptr->type == TMY_TYPE_ARGV0_ACL && | |
149 | ++ ptr->is_deleted == 0 && | |
150 | ++ tmy_check_condition(ptr->cond, NULL) == 0 && | |
151 | ++ tmy_path_match(filename, acl->filename) && | |
152 | ++ tmy_path_match(&argv0, acl->argv0)) { | |
153 | ++ error = 0; | |
154 | ++ break; | |
155 | ++ } | |
156 | ++ } | |
157 | ++ | |
158 | ++ return error; | |
159 | ++} | |
160 | ++ | |
161 | ++/** | |
162 | ++ * tmy_argv0_perm - check for argv[0] permission. | |
163 | ++ * @filename: pointer to filename. | |
164 | ++ * @argv0: pointer to basename of argv[0]. | |
165 | ++ * | |
166 | ++ * Returns zero if permission granted. | |
167 | ++ * Returns nonzero if permission denied. | |
168 | ++ */ | |
169 | ++int tmy_argv0_perm(const struct path_info *filename, const char *argv0) | |
170 | ++{ | |
171 | ++ int error = 0; | |
172 | ++ const u8 profile = TMY_SECURITY->domain->profile; | |
173 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_ARGV0); | |
174 | ++ const bool is_enforce = (mode == 3); | |
175 | ++ | |
176 | ++ if (!mode) | |
177 | ++ return 0; | |
178 | ++ if (!filename || !argv0 || !*argv0) | |
179 | ++ return 0; | |
180 | ++ | |
181 | ++ error = tmy_argv0_acl(filename, argv0); | |
182 | ++ | |
183 | ++ tmy_audit_argv0_log(filename, argv0, !error, profile, mode); | |
184 | ++ | |
185 | ++ if (error) { | |
186 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
187 | ++ | |
188 | ++ if (tmy_flags(TMY_VERBOSE)) | |
189 | ++ tmy_audit("TOMOYO-%s: Run %s as %s denied for %s\n", | |
190 | ++ tmy_getmsg(is_enforce), filename->name, argv0, | |
191 | ++ tmy_lastname(domain)); | |
192 | ++ | |
193 | ++ if (is_enforce) | |
194 | ++ error = tmy_supervisor("%s\n" TMY_ALLOW_ARGV0 "%s %s\n", | |
195 | ++ domain->domainname->name, | |
196 | ++ filename->name, argv0); | |
197 | ++ | |
198 | ++ else if (mode == 1 && tmy_quota()) | |
199 | ++ tmy_add_argv0_entry(filename->name, argv0, domain, | |
200 | ++ NULL, 0); | |
201 | ++ | |
202 | ++ if (!is_enforce) | |
203 | ++ error = 0; | |
204 | ++ } | |
205 | ++ | |
206 | ++ return error; | |
207 | ++} | |
208 | ++ | |
209 | ++/** | |
210 | ++ * tmy_add_argv0_policy - add or delete argv[0] policy. | |
211 | ++ * @data: a line to parse. | |
212 | ++ * @domain: pointer to "struct domain_info". | |
213 | ++ * @cond: pointer to "struct condition_list". May be NULL. | |
214 | ++ * @is_delete: is this delete request? | |
215 | ++ * | |
216 | ++ * Returns zero on success. | |
217 | ++ * Returns nonzero on failure. | |
218 | ++ */ | |
219 | ++int tmy_add_argv0_policy(char *data, | |
220 | ++ struct domain_info *domain, | |
221 | ++ const struct condition_list *cond, | |
222 | ++ const bool is_delete) | |
223 | ++{ | |
224 | ++ char *argv0 = strchr(data, ' '); | |
225 | ++ | |
226 | ++ if (!argv0) | |
227 | ++ return -EINVAL; | |
228 | ++ | |
229 | ++ *argv0++ = '\0'; | |
230 | ++ | |
231 | ++ return tmy_add_argv0_entry(data, argv0, domain, cond, | |
232 | ++ is_delete); | |
233 | ++} |
@@ -0,0 +1,976 @@ | ||
1 | +Network access control functions for TOMOYO Linux. | |
2 | +TOMOYO Linux checks permission by the following four parameters. | |
3 | + * protocol type (TCP, UDP, RAW) | |
4 | + * access type (bind, listen, connect, accept) | |
5 | + * IP address (Both IPv4 and IPv6 are available) | |
6 | + * port number | |
7 | +In order to check 'TCP accept' and 'UDP connect', | |
8 | +LSM expansion patch ([TOMOYO /]) is needed. | |
9 | + | |
10 | +Each permission can be automatically accumulated into | |
11 | +the policy of each domain using 'learning mode'. | |
12 | + | |
13 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
14 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
15 | + security/tomoyo/net.c | 956 ++++++++++++++++++++++++++++++++++++++++++++++++++ | |
16 | + 1 file changed, 956 insertions(+) | |
17 | + | |
18 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
19 | ++++ linux-2.6.23/security/tomoyo/net.c 2007-11-21 09:31:12.812105824 +0900 | |
20 | +@@ -0,0 +1,956 @@ | |
21 | ++/* | |
22 | ++ * security/tomoyo/net.c | |
23 | ++ * | |
24 | ++ * Network access control functions for TOMOYO Linux. | |
25 | ++ */ | |
26 | ++ | |
27 | ++#include "tomoyo.h" | |
28 | ++#include "realpath.h" | |
29 | ++ | |
30 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | |
31 | ++#define NIP6_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" | |
32 | ++#endif | |
33 | ++ | |
34 | ++/************************* AUDIT FUNCTIONS *************************/ | |
35 | ++ | |
36 | ++static int tmy_audit_network_log(const bool is_ipv6, | |
37 | ++ const char *operation, | |
38 | ++ const u32 *address, | |
39 | ++ const u16 port, | |
40 | ++ const bool is_granted, | |
41 | ++ const u8 profile, | |
42 | ++ const unsigned int mode) | |
43 | ++{ | |
44 | ++ char *buf; | |
45 | ++ int len = 256; | |
46 | ++ | |
47 | ++ if (is_granted) { | |
48 | ++ if (!tmy_audit_grant()) | |
49 | ++ return 0; | |
50 | ++ } else { | |
51 | ++ if (!tmy_audit_reject()) | |
52 | ++ return 0; | |
53 | ++ } | |
54 | ++ | |
55 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
56 | ++ if (!buf) | |
57 | ++ return -ENOMEM; | |
58 | ++ | |
59 | ++ snprintf(buf + strlen(buf), len - strlen(buf) - 1, | |
60 | ++ TMY_ALLOW_NETWORK "%s ", operation); | |
61 | ++ | |
62 | ++ if (is_ipv6) | |
63 | ++ tmy_print_ipv6(buf + strlen(buf), len - strlen(buf), | |
64 | ++ (const u16 *) address); | |
65 | ++ else { | |
66 | ++ u32 ip = *address; | |
67 | ++ snprintf(buf + strlen(buf), len - strlen(buf) - 1, | |
68 | ++ NIPQUAD_FMT, NIPQUAD(ip)); | |
69 | ++ } | |
70 | ++ | |
71 | ++ snprintf(buf + strlen(buf), len - strlen(buf) - 1, " %u\n", port); | |
72 | ++ | |
73 | ++ return tmy_write_audit_log(buf, is_granted); | |
74 | ++} | |
75 | ++ | |
76 | ++/************************* ADDRESS GROUP HANDLER *************************/ | |
77 | ++ | |
78 | ++/* List of address group. */ | |
79 | ++static LIST_HEAD(address_group_list); | |
80 | ++ | |
81 | ++static int tmy_add_address_group_entry(const char *group_name, | |
82 | ++ const bool is_ipv6, | |
83 | ++ const u16 *min_address, | |
84 | ++ const u16 *max_address, | |
85 | ++ const bool is_delete) | |
86 | ++{ | |
87 | ++ static DEFINE_MUTEX(mutex); | |
88 | ++ struct address_group_entry *new_group; | |
89 | ++ struct address_group_entry *group; | |
90 | ++ struct address_group_member *new_member; | |
91 | ++ struct address_group_member *member; | |
92 | ++ const struct path_info *saved_group_name; | |
93 | ++ int error = -ENOMEM; | |
94 | ++ bool found = 0; | |
95 | ++ | |
96 | ++ if (!tmy_correct_path(group_name, 0, 0, 0, __FUNCTION__) || | |
97 | ++ !group_name[0]) | |
98 | ++ return -EINVAL; | |
99 | ++ | |
100 | ++ saved_group_name = tmy_save_name(group_name); | |
101 | ++ if (!saved_group_name) | |
102 | ++ return -ENOMEM; | |
103 | ++ | |
104 | ++ mutex_lock(&mutex); | |
105 | ++ | |
106 | ++ list_for_each_entry(group, &address_group_list, list) { | |
107 | ++ if (saved_group_name != group->group_name) | |
108 | ++ continue; | |
109 | ++ list_for_each_entry(member, &group->address_group_member_list, | |
110 | ++ list) { | |
111 | ++ if (member->is_ipv6 != is_ipv6) | |
112 | ++ continue; | |
113 | ++ if (is_ipv6) { | |
114 | ++ if (memcmp(member->min.ipv6, min_address, 16) || | |
115 | ++ memcmp(member->max.ipv6, max_address, 16)) | |
116 | ++ continue; | |
117 | ++ } else { | |
118 | ++ if (member->min.ipv4 != *(u32 *) min_address || | |
119 | ++ member->max.ipv4 != *(u32 *) max_address) | |
120 | ++ continue; | |
121 | ++ } | |
122 | ++ member->is_deleted = is_delete; | |
123 | ++ error = 0; | |
124 | ++ goto out; | |
125 | ++ } | |
126 | ++ found = 1; | |
127 | ++ break; | |
128 | ++ } | |
129 | ++ | |
130 | ++ if (is_delete) { | |
131 | ++ error = -ENOENT; | |
132 | ++ goto out; | |
133 | ++ } | |
134 | ++ | |
135 | ++ if (!found) { | |
136 | ++ new_group = tmy_alloc_element(sizeof(*new_group)); | |
137 | ++ if (!new_group) | |
138 | ++ goto out; | |
139 | ++ INIT_LIST_HEAD(&new_group->address_group_member_list); | |
140 | ++ new_group->group_name = saved_group_name; | |
141 | ++ list_add_tail_mb(&new_group->list, &address_group_list); | |
142 | ++ group = new_group; | |
143 | ++ } | |
144 | ++ | |
145 | ++ new_member = tmy_alloc_element(sizeof(*new_member)); | |
146 | ++ if (!new_member) | |
147 | ++ goto out; | |
148 | ++ | |
149 | ++ new_member->is_ipv6 = is_ipv6; | |
150 | ++ | |
151 | ++ if (is_ipv6) { | |
152 | ++ memmove(new_member->min.ipv6, min_address, 16); | |
153 | ++ memmove(new_member->max.ipv6, max_address, 16); | |
154 | ++ } else { | |
155 | ++ new_member->min.ipv4 = *(u32 *) min_address; | |
156 | ++ new_member->max.ipv4 = *(u32 *) max_address; | |
157 | ++ } | |
158 | ++ | |
159 | ++ list_add_tail_mb(&new_member->list, &group->address_group_member_list); | |
160 | ++ error = 0; | |
161 | ++out: ; | |
162 | ++ mutex_unlock(&mutex); | |
163 | ++ | |
164 | ++ return error; | |
165 | ++} | |
166 | ++ | |
167 | ++/** | |
168 | ++ * tmy_add_address_group_policy - add or delete address group policy. | |
169 | ++ * @data: a line to parse. | |
170 | ++ * @is_delete: is this delete request? | |
171 | ++ * | |
172 | ++ * Returns zero on success. | |
173 | ++ * Returns nonzero on failure. | |
174 | ++ */ | |
175 | ++int tmy_add_address_group_policy(char *data, const bool is_delete) | |
176 | ++{ | |
177 | ++ int count; | |
178 | ++ bool is_ipv6; | |
179 | ++ u16 min_address[8]; | |
180 | ++ u16 max_address[8]; | |
181 | ++ unsigned int min[8]; | |
182 | ++ unsigned int max[8]; | |
183 | ++ char *cp = strchr(data, ' '); | |
184 | ++ | |
185 | ++ if (!cp) | |
186 | ++ return -EINVAL; | |
187 | ++ | |
188 | ++ *cp++ = '\0'; | |
189 | ++ count = sscanf(cp, | |
190 | ++ NIP6_FMT "-" NIP6_FMT, | |
191 | ++ &min[0], &min[1], &min[2], &min[3], | |
192 | ++ &min[4], &min[5], &min[6], &min[7], | |
193 | ++ &max[0], &max[1], &max[2], &max[3], | |
194 | ++ &max[4], &max[5], &max[6], &max[7]); | |
195 | ++ | |
196 | ++ if (count == 8 || count == 16) { | |
197 | ++ | |
198 | ++ int i; | |
199 | ++ | |
200 | ++ for (i = 0; i < 8; i++) { | |
201 | ++ min_address[i] = htons((u16) min[i]); | |
202 | ++ max_address[i] = htons((u16) max[i]); | |
203 | ++ } | |
204 | ++ if (count == 8) | |
205 | ++ memmove(max_address, min_address, sizeof(min_address)); | |
206 | ++ is_ipv6 = 1; | |
207 | ++ | |
208 | ++ goto ok; | |
209 | ++ | |
210 | ++ } | |
211 | ++ | |
212 | ++ count = sscanf(cp, | |
213 | ++ NIPQUAD_FMT "-" NIPQUAD_FMT, | |
214 | ++ &min[0], &min[1], | |
215 | ++ &min[2], &min[3], | |
216 | ++ &max[0], &max[1], | |
217 | ++ &max[2], &max[3]); | |
218 | ++ | |
219 | ++ if (count == 4 || count == 8) { | |
220 | ++ u32 ip = ((((u8) min[0]) << 24) + | |
221 | ++ (((u8) min[1]) << 16) + | |
222 | ++ (((u8) min[2]) << 8) + | |
223 | ++ (u8) min[3]); | |
224 | ++ | |
225 | ++ *(u32 *) min_address = ip; | |
226 | ++ | |
227 | ++ if (count == 8) | |
228 | ++ ip = ((((u8) max[0]) << 24) + | |
229 | ++ (((u8) max[1]) << 16) + | |
230 | ++ (((u8) max[2]) << 8) + | |
231 | ++ (u8) max[3]); | |
232 | ++ | |
233 | ++ *(u32 *) max_address = ip; | |
234 | ++ is_ipv6 = 0; | |
235 | ++ | |
236 | ++ goto ok; | |
237 | ++ | |
238 | ++ } | |
239 | ++ | |
240 | ++ return -EINVAL; | |
241 | ++ | |
242 | ++ok: ; | |
243 | ++ return tmy_add_address_group_entry(data, is_ipv6, min_address, | |
244 | ++ max_address, is_delete); | |
245 | ++} | |
246 | ++ | |
247 | ++static struct address_group_entry *tmy_new_address_group(const char *name) | |
248 | ++{ | |
249 | ++ int i; | |
250 | ++ struct address_group_entry *group; | |
251 | ++ | |
252 | ++ for (i = 0; i <= 1; i++) { | |
253 | ++ list_for_each_entry(group, &address_group_list, list) { | |
254 | ++ if (strcmp(name, group->group_name->name) == 0) | |
255 | ++ return group; | |
256 | ++ } | |
257 | ++ | |
258 | ++ if (i == 0) { | |
259 | ++ /* | |
260 | ++ * Add a dummy entry to create new address group | |
261 | ++ * and delete that entry. | |
262 | ++ */ | |
263 | ++ const u16 dum[2] = { 0, 0 }; | |
264 | ++ tmy_add_address_group_entry(name, 0, dum, dum, 0); | |
265 | ++ tmy_add_address_group_entry(name, 0, dum, dum, 1); | |
266 | ++ } | |
267 | ++ } | |
268 | ++ | |
269 | ++ return NULL; | |
270 | ++} | |
271 | ++ | |
272 | ++static int tmy_address_match_group(const bool is_ipv6, | |
273 | ++ const u32 *address, | |
274 | ++ const struct address_group_entry *group) | |
275 | ++{ | |
276 | ++ struct address_group_member *member; | |
277 | ++ const u32 ip = ntohl(*address); | |
278 | ++ | |
279 | ++ list_for_each_entry(member, &group->address_group_member_list, list) { | |
280 | ++ if (member->is_deleted) | |
281 | ++ continue; | |
282 | ++ | |
283 | ++ if (member->is_ipv6) { | |
284 | ++ | |
285 | ++ if (is_ipv6 && | |
286 | ++ memcmp(member->min.ipv6, address, 16) <= 0 && | |
287 | ++ memcmp(address, member->max.ipv6, 16) <= 0) | |
288 | ++ return 1; | |
289 | ++ | |
290 | ++ } else { | |
291 | ++ | |
292 | ++ if (!is_ipv6 && | |
293 | ++ member->min.ipv4 <= ip && | |
294 | ++ ip <= member->max.ipv4) | |
295 | ++ return 1; | |
296 | ++ | |
297 | ++ } | |
298 | ++ } | |
299 | ++ | |
300 | ++ return 0; | |
301 | ++} | |
302 | ++ | |
303 | ++static int tmy_read_address_group(struct io_buffer *head, | |
304 | ++ struct address_group_entry *group, | |
305 | ++ struct address_group_member *member) | |
306 | ++{ | |
307 | ++ char buf[128]; | |
308 | ++ if (!member) | |
309 | ++ return 0; | |
310 | ++ | |
311 | ++ if (member->is_ipv6) { | |
312 | ++ | |
313 | ++ const u16 *min_addr = member->min.ipv6; | |
314 | ++ const u16 *max_addr = member->max.ipv6; | |
315 | ++ | |
316 | ++ tmy_print_ipv6(buf, sizeof(buf), min_addr); | |
317 | ++ | |
318 | ++ if (memcmp(min_addr, max_addr, 16)) { | |
319 | ++ char *cp = strchr(buf, '\0'); | |
320 | ++ int len = sizeof(buf) - strlen(buf) - 1; | |
321 | ++ | |
322 | ++ *cp++ = '-'; | |
323 | ++ tmy_print_ipv6(cp, len, max_addr); | |
324 | ++ } | |
325 | ++ | |
326 | ++ } else { | |
327 | ++ | |
328 | ++ const u32 min_addr = member->min.ipv4; | |
329 | ++ const u32 max_addr = member->max.ipv4; | |
330 | ++ | |
331 | ++ memset(buf, 0, sizeof(buf)); | |
332 | ++ snprintf(buf, sizeof(buf) - 1, | |
333 | ++ NIPQUAD_FMT, HIPQUAD(min_addr)); | |
334 | ++ | |
335 | ++ if (min_addr != max_addr) { | |
336 | ++ const int len = strlen(buf); | |
337 | ++ | |
338 | ++ snprintf(buf + len, sizeof(buf) - 1 - len, | |
339 | ++ "-" NIPQUAD_FMT, HIPQUAD(max_addr)); | |
340 | ++ } | |
341 | ++ | |
342 | ++ } | |
343 | ++ | |
344 | ++ return tmy_io_printf(head, TMY_ADDRESS_GROUP "%s %s\n", | |
345 | ++ group->group_name->name, buf); | |
346 | ++} | |
347 | ++ | |
348 | ++/** | |
349 | ++ * tmy_read_address_group_policy - read address group policy | |
350 | ++ * @head: pointer to "struct io_buffer". | |
351 | ++ * | |
352 | ++ * Returns nonzero if reading incomplete. | |
353 | ++ * Returns zero otherwise. | |
354 | ++ */ | |
355 | ++int tmy_read_address_group_policy(struct io_buffer *head) | |
356 | ++{ | |
357 | ++ struct list_head *gpos; | |
358 | ++ struct list_head *mpos; | |
359 | ++ list_for_each_cookie(gpos, head->read_var1, &address_group_list) { | |
360 | ++ struct address_group_entry *group; | |
361 | ++ group = list_entry(gpos, struct address_group_entry, list); | |
362 | ++ list_for_each_cookie(mpos, head->read_var2, | |
363 | ++ &group->address_group_member_list) { | |
364 | ++ struct address_group_member *member; | |
365 | ++ member = list_entry(mpos, struct address_group_member, | |
366 | ++ list); | |
367 | ++ if (member->is_deleted) | |
368 | ++ continue; | |
369 | ++ if (tmy_read_address_group(head, group, member)) | |
370 | ++ return -ENOMEM; | |
371 | ++ } | |
372 | ++ } | |
373 | ++ return 0; | |
374 | ++} | |
375 | ++ | |
376 | ++/*********************** NETWORK NETWORK ACL HANDLER ***********************/ | |
377 | ++ | |
378 | ++/** | |
379 | ++ * tmy_print_ipv6 - print ipv6 address | |
380 | ++ * @buffer: pointer to buffer to save the result. | |
381 | ++ * @buffer_len: sizeof @buffer . | |
382 | ++ * @ip: pointer to an IPv6 address in network byte order. | |
383 | ++ * | |
384 | ++ * Returns @buffer . | |
385 | ++ */ | |
386 | ++char *tmy_print_ipv6(char *buffer, const int buffer_len, const u16 *ip) | |
387 | ++{ | |
388 | ++ memset(buffer, 0, buffer_len); | |
389 | ++ snprintf(buffer, buffer_len - 1, NIP6_FMT, | |
390 | ++ ntohs(ip[0]), ntohs(ip[1]), ntohs(ip[2]), ntohs(ip[3]), | |
391 | ++ ntohs(ip[4]), ntohs(ip[5]), ntohs(ip[6]), ntohs(ip[7])); | |
392 | ++ return buffer; | |
393 | ++} | |
394 | ++ | |
395 | ++/** | |
396 | ++ * tmy_network2keyword - get keyword from access control index. | |
397 | ++ * @operation: index number. | |
398 | ++ * | |
399 | ++ * Returns keyword that corresponds with @operation . | |
400 | ++ */ | |
401 | ++const char *tmy_network2keyword(const unsigned int operation) | |
402 | ++{ | |
403 | ++ const char *keyword = "unknown"; | |
404 | ++ switch (operation) { | |
405 | ++ case TMY_NETWORK_ACL_UDP_BIND: | |
406 | ++ keyword = "UDP bind"; | |
407 | ++ break; | |
408 | ++ case TMY_NETWORK_ACL_UDP_CONNECT: | |
409 | ++ keyword = "UDP connect"; | |
410 | ++ break; | |
411 | ++ case TMY_NETWORK_ACL_TCP_BIND: | |
412 | ++ keyword = "TCP bind"; | |
413 | ++ break; | |
414 | ++ case TMY_NETWORK_ACL_TCP_LISTEN: | |
415 | ++ keyword = "TCP listen"; | |
416 | ++ break; | |
417 | ++ case TMY_NETWORK_ACL_TCP_CONNECT: | |
418 | ++ keyword = "TCP connect"; | |
419 | ++ break; | |
420 | ++ case TMY_NETWORK_ACL_TCP_ACCEPT: | |
421 | ++ keyword = "TCP accept"; | |
422 | ++ break; | |
423 | ++ case TMY_NETWORK_ACL_RAW_BIND: | |
424 | ++ keyword = "RAW bind"; | |
425 | ++ break; | |
426 | ++ case TMY_NETWORK_ACL_RAW_CONNECT: | |
427 | ++ keyword = "RAW connect"; | |
428 | ++ break; | |
429 | ++ } | |
430 | ++ return keyword; | |
431 | ++} | |
432 | ++ | |
433 | ++/* Compare IPv4/IPv6 address. */ | |
434 | ++static int tmy_cmp_network_entry(const u8 record_type, | |
435 | ++ struct net_acl *acl, | |
436 | ++ const struct address_group_entry *group, | |
437 | ++ const u32 min_ip, | |
438 | ++ const u32 max_ip, | |
439 | ++ const u32 *min_address, | |
440 | ++ const u32 *max_address) | |
441 | ++{ | |
442 | ++ int found = 0; | |
443 | ++ | |
444 | ++ switch (record_type) { | |
445 | ++ | |
446 | ++ case TMY_TYPE_ADDRESS_GROUP: | |
447 | ++ if (acl->u.group == group) | |
448 | ++ found = 1; | |
449 | ++ break; | |
450 | ++ | |
451 | ++ case TMY_TYPE_IPv4: | |
452 | ++ if (acl->u.ipv4.min == min_ip && | |
453 | ++ max_ip == acl->u.ipv4.max) | |
454 | ++ found = 1; | |
455 | ++ break; | |
456 | ++ | |
457 | ++ case TMY_TYPE_IPv6: | |
458 | ++ if (memcmp(acl->u.ipv6.min, min_address, 16) == 0 && | |
459 | ++ memcmp(max_address, acl->u.ipv6.max, 16) == 0) | |
460 | ++ found = 1; | |
461 | ++ break; | |
462 | ++ | |
463 | ++ } | |
464 | ++ | |
465 | ++ return found; | |
466 | ++} | |
467 | ++ | |
468 | ++static int tmy_add_network_entry(const u8 operation, | |
469 | ++ const u8 record_type, | |
470 | ++ const struct address_group_entry *group, | |
471 | ++ const u32 *min_address, | |
472 | ++ const u32 *max_address, | |
473 | ++ const u16 min_port, | |
474 | ++ const u16 max_port, | |
475 | ++ struct domain_info *domain, | |
476 | ++ const struct condition_list *cond, | |
477 | ++ const bool is_delete) | |
478 | ++{ | |
479 | ++ struct acl_info *ptr; | |
480 | ++ struct net_acl *acl; | |
481 | ++ int error = -ENOMEM; | |
482 | ++ /* using host byte order to allow u32 comparison than memcmp().*/ | |
483 | ++ const u32 min_ip = ntohl(*min_address); | |
484 | ++ const u32 max_ip = ntohl(*max_address); | |
485 | ++ | |
486 | ++ if (!domain) | |
487 | ++ return -EINVAL; | |
488 | ++ | |
489 | ++ mutex_lock(&domain_acl_lock); | |
490 | ++ | |
491 | ++ if (is_delete) | |
492 | ++ goto remove; | |
493 | ++ | |
494 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
495 | ++ acl = (struct net_acl *) ptr; | |
496 | ++ if (ptr->type == TMY_TYPE_IP_NETWORK_ACL && | |
497 | ++ ptr->cond == cond && | |
498 | ++ acl->operation_type == operation && | |
499 | ++ acl->record_type == record_type && | |
500 | ++ acl->min_port == min_port && | |
501 | ++ max_port == acl->max_port && | |
502 | ++ tmy_cmp_network_entry(record_type, acl, | |
503 | ++ group, min_ip, max_ip, | |
504 | ++ min_address, | |
505 | ++ max_address)) { | |
506 | ++ ptr->is_deleted = 0; | |
507 | ++ error = 0; | |
508 | ++ goto ok; | |
509 | ++ } | |
510 | ++ } | |
511 | ++ /* Not found. Append it to the tail. */ | |
512 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
513 | ++ if (!acl) | |
514 | ++ goto ok; | |
515 | ++ | |
516 | ++ acl->head.type = TMY_TYPE_IP_NETWORK_ACL; | |
517 | ++ acl->head.cond = cond; | |
518 | ++ acl->operation_type = operation; | |
519 | ++ acl->record_type = record_type; | |
520 | ++ | |
521 | ++ if (record_type == TMY_TYPE_ADDRESS_GROUP) | |
522 | ++ acl->u.group = group; | |
523 | ++ else if (record_type == TMY_TYPE_IPv4) { | |
524 | ++ acl->u.ipv4.min = min_ip; | |
525 | ++ acl->u.ipv4.max = max_ip; | |
526 | ++ } else { | |
527 | ++ memmove(acl->u.ipv6.min, min_address, 16); | |
528 | ++ memmove(acl->u.ipv6.max, max_address, 16); | |
529 | ++ } | |
530 | ++ | |
531 | ++ acl->min_port = min_port; | |
532 | ++ acl->max_port = max_port; | |
533 | ++ error = tmy_add_acl(domain, | |
534 | ++ (struct acl_info *) acl); | |
535 | ++ goto ok; | |
536 | ++remove: ; | |
537 | ++ error = -ENOENT; | |
538 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
539 | ++ acl = (struct net_acl *) ptr; | |
540 | ++ if (ptr->type != TMY_TYPE_IP_NETWORK_ACL || | |
541 | ++ ptr->cond != cond || | |
542 | ++ ptr->is_deleted || | |
543 | ++ acl->operation_type != operation || | |
544 | ++ acl->record_type != record_type || | |
545 | ++ acl->min_port != min_port || | |
546 | ++ acl->max_port != max_port || | |
547 | ++ !tmy_cmp_network_entry(record_type, acl, | |
548 | ++ group, min_ip, max_ip, | |
549 | ++ min_address, | |
550 | ++ max_address)) | |
551 | ++ continue; | |
552 | ++ error = tmy_del_acl(ptr); | |
553 | ++ break; | |
554 | ++ } | |
555 | ++ok: ; | |
556 | ++ mutex_unlock(&domain_acl_lock); | |
557 | ++ | |
558 | ++ return error; | |
559 | ++} | |
560 | ++ | |
561 | ++/* Check network permission. */ | |
562 | ++static int tmy_network_entry(const bool is_ipv6, | |
563 | ++ const int operation, | |
564 | ++ const u32 *address, | |
565 | ++ const u16 port) | |
566 | ++{ | |
567 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
568 | ++ const u8 profile = domain->profile; | |
569 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_NETWORK); | |
570 | ++ struct acl_info *ptr; | |
571 | ++ const char *keyword = tmy_network2keyword(operation); | |
572 | ++ const bool is_enforce = (mode == 3); | |
573 | ++ /* using host byte order to allow u32 comparison than memcmp().*/ | |
574 | ++ const u32 ip = ntohl(*address); | |
575 | ++ bool found = 0; | |
576 | ++ | |
577 | ++ if (!mode) | |
578 | ++ return 0; | |
579 | ++ | |
580 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
581 | ++ struct net_acl *acl = (struct net_acl *) ptr; | |
582 | ++ if (ptr->type != TMY_TYPE_IP_NETWORK_ACL || | |
583 | ++ ptr->is_deleted || | |
584 | ++ acl->operation_type != operation || | |
585 | ++ port < acl->min_port || | |
586 | ++ acl->max_port < port || | |
587 | ++ tmy_check_condition(ptr->cond, NULL)) | |
588 | ++ continue; | |
589 | ++ | |
590 | ++ if (acl->record_type == TMY_TYPE_ADDRESS_GROUP) { | |
591 | ++ if (tmy_address_match_group(is_ipv6, address, | |
592 | ++ acl->u.group)) { | |
593 | ++ found = 1; | |
594 | ++ break; | |
595 | ++ } | |
596 | ++ } else if (acl->record_type == TMY_TYPE_IPv4) { | |
597 | ++ if (!is_ipv6 && | |
598 | ++ (acl->u.ipv4.min <= ip && ip <= acl->u.ipv4.max)) { | |
599 | ++ found = 1; | |
600 | ++ break; | |
601 | ++ } | |
602 | ++ } else { | |
603 | ++ if (is_ipv6 && | |
604 | ++ memcmp(acl->u.ipv6.min, address, 16) <= 0 && | |
605 | ++ memcmp(address, acl->u.ipv6.max, 16) <= 0) { | |
606 | ++ found = 1; | |
607 | ++ break; | |
608 | ++ } | |
609 | ++ } | |
610 | ++ } | |
611 | ++ | |
612 | ++ tmy_audit_network_log(is_ipv6, keyword, address, | |
613 | ++ port, found, profile, mode); | |
614 | ++ | |
615 | ++ if (found) | |
616 | ++ return 0; | |
617 | ++ | |
618 | ++ if (tmy_flags(TMY_VERBOSE)) { | |
619 | ++ if (is_ipv6) { | |
620 | ++ char buf[64]; | |
621 | ++ tmy_print_ipv6(buf, sizeof(buf), (const u16 *) address); | |
622 | ++ tmy_audit("TOMOYO-%s: %s to %s %u denied for %s\n", | |
623 | ++ tmy_getmsg(is_enforce), keyword, buf, port, | |
624 | ++ tmy_lastname(domain)); | |
625 | ++ } else { | |
626 | ++ tmy_audit("TOMOYO-%s: %s to %u.%u.%u.%u %u denied for " | |
627 | ++ "%s\n", tmy_getmsg(is_enforce), keyword, | |
628 | ++ HIPQUAD(ip), port, tmy_lastname(domain)); | |
629 | ++ } | |
630 | ++ } | |
631 | ++ | |
632 | ++ if (is_enforce) { | |
633 | ++ | |
634 | ++ if (is_ipv6) { | |
635 | ++ | |
636 | ++ char buf[64]; | |
637 | ++ | |
638 | ++ tmy_print_ipv6(buf, sizeof(buf), (const u16 *) address); | |
639 | ++ return tmy_supervisor("%s\n" TMY_ALLOW_NETWORK | |
640 | ++ "%s %s %u\n", | |
641 | ++ domain->domainname->name, keyword, | |
642 | ++ buf, port); | |
643 | ++ | |
644 | ++ } | |
645 | ++ | |
646 | ++ return tmy_supervisor("%s\n" TMY_ALLOW_NETWORK | |
647 | ++ "%s " NIPQUAD_FMT " %u\n", | |
648 | ++ domain->domainname->name, keyword, | |
649 | ++ HIPQUAD(ip), port); | |
650 | ++ | |
651 | ++ } | |
652 | ++ | |
653 | ++ if (mode == 1 && tmy_quota()) | |
654 | ++ tmy_add_network_entry(operation, | |
655 | ++ is_ipv6 ? TMY_TYPE_IPv6 : TMY_TYPE_IPv4, | |
656 | ++ NULL, address, address, | |
657 | ++ port, port, domain, NULL, 0); | |
658 | ++ | |
659 | ++ return 0; | |
660 | ++} | |
661 | ++ | |
662 | ++/** | |
663 | ++ * tmy_add_signal_policy - add or delete signal policy. | |
664 | ++ * @data: a line to parse. | |
665 | ++ * @domain: pointer to "struct domain_info". | |
666 | ++ * @cond: pointer to "struct condition_list". May be NULL. | |
667 | ++ * @is_delete: is this delete request? | |
668 | ++ * | |
669 | ++ * Returns zero on success. | |
670 | ++ * Returns nonzero on failure. | |
671 | ++ */ | |
672 | ++int tmy_add_network_policy(char *data, | |
673 | ++ struct domain_info *domain, | |
674 | ++ const struct condition_list *cond, | |
675 | ++ const bool is_delete) | |
676 | ++{ | |
677 | ++ u8 sock_type; | |
678 | ++ u8 operation; | |
679 | ++ u8 record_type; | |
680 | ++ u16 min_address[8]; | |
681 | ++ u16 max_address[8]; | |
682 | ++ unsigned int min[8]; | |
683 | ++ unsigned int max[8]; | |
684 | ++ struct address_group_entry *group = NULL; | |
685 | ++ u16 min_port; | |
686 | ++ u16 max_port; | |
687 | ++ int count; | |
688 | ++ char *cp1 = NULL; | |
689 | ++ char *cp2 = NULL; | |
690 | ++ | |
691 | ++ cp1 = strchr(data, ' '); | |
692 | ++ if (!cp1) | |
693 | ++ goto out; | |
694 | ++ cp1++; | |
695 | ++ | |
696 | ++ if (strncmp(data, "TCP ", 4) == 0) | |
697 | ++ sock_type = SOCK_STREAM; | |
698 | ++ else if (strncmp(data, "UDP ", 4) == 0) | |
699 | ++ sock_type = SOCK_DGRAM; | |
700 | ++ else if (strncmp(data, "RAW ", 4) == 0) | |
701 | ++ sock_type = SOCK_RAW; | |
702 | ++ else | |
703 | ++ goto out; | |
704 | ++ | |
705 | ++ cp2 = strchr(cp1, ' '); | |
706 | ++ if (!cp2) | |
707 | ++ goto out; | |
708 | ++ cp2++; | |
709 | ++ | |
710 | ++ if (strncmp(cp1, "bind ", 5) == 0) { | |
711 | ++ switch (sock_type) { | |
712 | ++ case SOCK_STREAM: | |
713 | ++ operation = TMY_NETWORK_ACL_TCP_BIND; | |
714 | ++ break; | |
715 | ++ case SOCK_DGRAM: | |
716 | ++ operation = TMY_NETWORK_ACL_UDP_BIND; | |
717 | ++ break; | |
718 | ++ default: | |
719 | ++ operation = TMY_NETWORK_ACL_RAW_BIND; | |
720 | ++ break; | |
721 | ++ } | |
722 | ++ } else if (strncmp(cp1, "connect ", 8) == 0) { | |
723 | ++ switch (sock_type) { | |
724 | ++ case SOCK_STREAM: | |
725 | ++ operation = TMY_NETWORK_ACL_TCP_CONNECT; | |
726 | ++ break; | |
727 | ++ case SOCK_DGRAM: | |
728 | ++ operation = TMY_NETWORK_ACL_UDP_CONNECT; | |
729 | ++ break; | |
730 | ++ default: | |
731 | ++ operation = TMY_NETWORK_ACL_RAW_CONNECT; | |
732 | ++ break; | |
733 | ++ } | |
734 | ++ } else if (sock_type == SOCK_STREAM && | |
735 | ++ strncmp(cp1, "listen ", 7) == 0) | |
736 | ++ operation = TMY_NETWORK_ACL_TCP_LISTEN; | |
737 | ++ | |
738 | ++ else if (sock_type == SOCK_STREAM && | |
739 | ++ strncmp(cp1, "accept ", 7) == 0) | |
740 | ++ operation = TMY_NETWORK_ACL_TCP_ACCEPT; | |
741 | ++ | |
742 | ++ else | |
743 | ++ goto out; | |
744 | ++ | |
745 | ++ cp1 = strchr(cp2, ' '); | |
746 | ++ if (!cp1) | |
747 | ++ goto out; | |
748 | ++ *cp1++ = '\0'; | |
749 | ++ | |
750 | ++ count = sscanf(cp2, | |
751 | ++ NIP6_FMT "-" NIP6_FMT, | |
752 | ++ &min[0], &min[1], &min[2], &min[3], | |
753 | ++ &min[4], &min[5], &min[6], &min[7], | |
754 | ++ &max[0], &max[1], &max[2], &max[3], | |
755 | ++ &max[4], &max[5], &max[6], &max[7]); | |
756 | ++ | |
757 | ++ if (count == 8 || count == 16) { | |
758 | ++ | |
759 | ++ int i; | |
760 | ++ | |
761 | ++ for (i = 0; i < 8; i++) { | |
762 | ++ min_address[i] = htons((u16) min[i]); | |
763 | ++ max_address[i] = htons((u16) max[i]); | |
764 | ++ } | |
765 | ++ | |
766 | ++ if (count == 8) | |
767 | ++ memmove(max_address, min_address, sizeof(min_address)); | |
768 | ++ record_type = TMY_TYPE_IPv6; | |
769 | ++ | |
770 | ++ goto ok; | |
771 | ++ | |
772 | ++ } | |
773 | ++ | |
774 | ++ count = sscanf(cp2, | |
775 | ++ NIPQUAD_FMT "-" NIPQUAD_FMT, | |
776 | ++ &min[0], &min[1], &min[2], &min[3], | |
777 | ++ &max[0], &max[1], &max[2], &max[3]); | |
778 | ++ | |
779 | ++ if (count == 4 || count == 8) { | |
780 | ++ | |
781 | ++ u32 ip = htonl((((u8) min[0]) << 24) + | |
782 | ++ (((u8) min[1]) << 16) + | |
783 | ++ (((u8) min[2]) << 8) + | |
784 | ++ (u8) min[3]); | |
785 | ++ *(u32 *) min_address = ip; | |
786 | ++ | |
787 | ++ if (count == 8) | |
788 | ++ ip = htonl((((u8) max[0]) << 24) + | |
789 | ++ (((u8) max[1]) << 16) + | |
790 | ++ (((u8) max[2]) << 8) + | |
791 | ++ (u8) max[3]); | |
792 | ++ *(u32 *) max_address = ip; | |
793 | ++ record_type = TMY_TYPE_IPv4; | |
794 | ++ | |
795 | ++ goto ok; | |
796 | ++ | |
797 | ++ } | |
798 | ++ | |
799 | ++ if (*cp2 == '@') { | |
800 | ++ | |
801 | ++ group = tmy_new_address_group(cp2 + 1); | |
802 | ++ if (!group) | |
803 | ++ return -ENOMEM; | |
804 | ++ record_type = TMY_TYPE_ADDRESS_GROUP; | |
805 | ++ | |
806 | ++ goto ok; | |
807 | ++ } | |
808 | ++ | |
809 | ++ goto out; | |
810 | ++ | |
811 | ++ok: ; | |
812 | ++ if (strchr(cp1, ' ')) | |
813 | ++ goto out; | |
814 | ++ | |
815 | ++ count = sscanf(cp1, "%hu-%hu", &min_port, &max_port); | |
816 | ++ if (count != 1 && count != 2) | |
817 | ++ goto out; | |
818 | ++ | |
819 | ++ if (count == 1) | |
820 | ++ max_port = min_port; | |
821 | ++ | |
822 | ++ return tmy_add_network_entry(operation, record_type, group, | |
823 | ++ (u32 *) min_address, | |
824 | ++ (u32 *) max_address, | |
825 | ++ min_port, max_port, domain, | |
826 | ++ cond, is_delete); | |
827 | ++ | |
828 | ++out: ; | |
829 | ++ return -EINVAL; | |
830 | ++} | |
831 | ++ | |
832 | ++/** | |
833 | ++ * tmy_network_listen_acl - check permission for listen(2) operation. | |
834 | ++ * @is_ipv6: is @address an IPv6 address? | |
835 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
836 | ++ * @port: TCP or UDP's port number. | |
837 | ++ * | |
838 | ++ * Returns zero if permission granted. | |
839 | ++ * Returns nonzero if permission denied. | |
840 | ++ */ | |
841 | ++int tmy_network_listen_acl(const bool is_ipv6, | |
842 | ++ const u8 *address, | |
843 | ++ const u16 port) | |
844 | ++{ | |
845 | ++ return tmy_network_entry(is_ipv6, | |
846 | ++ TMY_NETWORK_ACL_TCP_LISTEN, | |
847 | ++ (const u32 *) address, | |
848 | ++ ntohs(port)); | |
849 | ++} | |
850 | ++ | |
851 | ++/** | |
852 | ++ * tmy_network_connect_acl - check permission for connect(2) operation. | |
853 | ++ * @is_ipv6: is @address an IPv6 address? | |
854 | ++ * @sock_type: socket type (TCP, UDP or IP). | |
855 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
856 | ++ * @port: TCP or UDP's port number or IP's protocol number. | |
857 | ++ * | |
858 | ++ * Returns zero if permission granted. | |
859 | ++ * Returns nonzero if permission denied. | |
860 | ++ */ | |
861 | ++int tmy_network_connect_acl(const bool is_ipv6, | |
862 | ++ const int sock_type, | |
863 | ++ const u8 *address, | |
864 | ++ const u16 port) | |
865 | ++{ | |
866 | ++ int type; | |
867 | ++ | |
868 | ++ switch (sock_type) { | |
869 | ++ case SOCK_STREAM: | |
870 | ++ type = TMY_NETWORK_ACL_TCP_CONNECT; | |
871 | ++ break; | |
872 | ++ case SOCK_DGRAM: | |
873 | ++ type = TMY_NETWORK_ACL_UDP_CONNECT; | |
874 | ++ break; | |
875 | ++ default: | |
876 | ++ type = TMY_NETWORK_ACL_RAW_CONNECT; | |
877 | ++ break; | |
878 | ++ } | |
879 | ++ | |
880 | ++ return tmy_network_entry(is_ipv6, type, | |
881 | ++ (const u32 *) address, ntohs(port)); | |
882 | ++} | |
883 | ++ | |
884 | ++/** | |
885 | ++ * tmy_network_bind_acl - check permission for bind(2) operation. | |
886 | ++ * @is_ipv6: is @address an IPv6 address? | |
887 | ++ * @sock_type: socket type (TCP, UDP or IP). | |
888 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
889 | ++ * @port: TCP or UDP's port number or IP's protocol number. | |
890 | ++ * | |
891 | ++ * Returns zero if permission granted. | |
892 | ++ * Returns nonzero if permission denied. | |
893 | ++ */ | |
894 | ++int tmy_network_bind_acl(const bool is_ipv6, | |
895 | ++ const int sock_type, | |
896 | ++ const u8 *address, | |
897 | ++ const u16 port) | |
898 | ++{ | |
899 | ++ int type; | |
900 | ++ | |
901 | ++ switch (sock_type) { | |
902 | ++ case SOCK_STREAM: | |
903 | ++ type = TMY_NETWORK_ACL_TCP_BIND; | |
904 | ++ break; | |
905 | ++ case SOCK_DGRAM: | |
906 | ++ type = TMY_NETWORK_ACL_UDP_BIND; | |
907 | ++ break; | |
908 | ++ default: | |
909 | ++ type = TMY_NETWORK_ACL_RAW_BIND; | |
910 | ++ break; | |
911 | ++ } | |
912 | ++ | |
913 | ++ return tmy_network_entry(is_ipv6, type, | |
914 | ++ (const u32 *) address, ntohs(port)); | |
915 | ++} | |
916 | ++ | |
917 | ++/** | |
918 | ++ * tmy_network_sendmsg_acl - check permission for sendmsg(2) operation. | |
919 | ++ * @is_ipv6: is @address an IPv6 address? | |
920 | ++ * @sock_type: socket type (UDP or IP). | |
921 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
922 | ++ * @port: UDP's port number or IP's protocol number. | |
923 | ++ * | |
924 | ++ * Returns zero if permission granted. | |
925 | ++ * Returns nonzero if permission denied. | |
926 | ++ */ | |
927 | ++int tmy_network_sendmsg_acl(const bool is_ipv6, | |
928 | ++ const int sock_type, | |
929 | ++ const u8 *address, | |
930 | ++ const u16 port) | |
931 | ++{ | |
932 | ++ int type; | |
933 | ++ | |
934 | ++ if (sock_type == SOCK_DGRAM) | |
935 | ++ type = TMY_NETWORK_ACL_UDP_CONNECT; | |
936 | ++ else | |
937 | ++ type = TMY_NETWORK_ACL_RAW_CONNECT; | |
938 | ++ | |
939 | ++ return tmy_network_entry(is_ipv6, type, | |
940 | ++ (const u32 *) address, ntohs(port)); | |
941 | ++} | |
942 | ++ | |
943 | ++/** | |
944 | ++ * tmy_network_accept_acl - check permission for accept(2) operation. | |
945 | ++ * @is_ipv6: is @address an IPv6 address? | |
946 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
947 | ++ * @port: TCP client's port number. | |
948 | ++ * | |
949 | ++ * Returns zero if permission granted. | |
950 | ++ * Returns nonzero if permission denied. | |
951 | ++ */ | |
952 | ++int tmy_network_accept_acl(const bool is_ipv6, const u8 *address, | |
953 | ++ const u16 port) | |
954 | ++{ | |
955 | ++ return tmy_network_entry(is_ipv6, TMY_NETWORK_ACL_TCP_ACCEPT, | |
956 | ++ (const u32 *) address, ntohs(port)); | |
957 | ++} | |
958 | ++ | |
959 | ++/** | |
960 | ++ * tmy_network_recvmsg_acl - check permission for recvmsg(2) operation. | |
961 | ++ * @is_ipv6: is @address an IPv6 address? | |
962 | ++ * @sock_type: socket type (UDP or IP). | |
963 | ++ * @address: pointer to IPv4/IPv6 address in network byte order. | |
964 | ++ * @port: UDP's port number or IP's protocol number. | |
965 | ++ * | |
966 | ++ * Returns zero if permission granted. | |
967 | ++ * Returns nonzero if permission denied. | |
968 | ++ */ | |
969 | ++int tmy_network_recvmsg_acl(const bool is_ipv6, const int sock_type, | |
970 | ++ const u8 *address, const u16 port) | |
971 | ++{ | |
972 | ++ return tmy_network_entry(is_ipv6, sock_type == SOCK_DGRAM ? | |
973 | ++ TMY_NETWORK_ACL_UDP_CONNECT : | |
974 | ++ TMY_NETWORK_ACL_RAW_CONNECT, | |
975 | ++ (const u32 *) address, ntohs(port)); | |
976 | ++} |
@@ -0,0 +1,692 @@ | ||
1 | +This patch allows administrators use conditional permission. | |
2 | +TOMOYO Linux supports conditional permission based on | |
3 | +process's UID,GID etc. and/or requested pathname's UID/GID. | |
4 | + | |
5 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
6 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
7 | + security/tomoyo/condition.c | 680 ++++++++++++++++++++++++++++++++++++++++++++ | |
8 | + 1 file changed, 680 insertions(+) | |
9 | + | |
10 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
11 | ++++ linux-2.6.23/security/tomoyo/condition.c 2007-11-02 13:48:04.000000000 +0900 | |
12 | +@@ -0,0 +1,680 @@ | |
13 | ++/* | |
14 | ++ * security/tomoyo/condition.c | |
15 | ++ * | |
16 | ++ * Functions to support conditional access control for TOMOYO Linux. | |
17 | ++ */ | |
18 | ++ | |
19 | ++#include "tomoyo.h" | |
20 | ++#include "realpath.h" | |
21 | ++ | |
22 | ++/** | |
23 | ++ * tmy_find_condition_part - check whether a line contains condition part. | |
24 | ++ * @data: a line to check. | |
25 | ++ * | |
26 | ++ * Returns pointer to condition part if found. | |
27 | ++ * Returns NULL if not found. | |
28 | ++ * | |
29 | ++ * Since the trailing spaces are removed by tmy_normalize_line(), | |
30 | ++ * the last "\040if\040" sequence corresponds to condition part. | |
31 | ++ */ | |
32 | ++char *tmy_find_condition_part(char *data) | |
33 | ++{ | |
34 | ++ char *cp = strstr(data, " if "); | |
35 | ++ if (cp) { | |
36 | ++ char *cp2; | |
37 | ++ while ((cp2 = strstr(cp + 3, " if ")) != NULL) | |
38 | ++ cp = cp2; | |
39 | ++ *cp++ = '\0'; | |
40 | ++ } | |
41 | ++ return cp; | |
42 | ++} | |
43 | ++ | |
44 | ++#define VALUE_TYPE_DECIMAL 1 /* 01 */ | |
45 | ++#define VALUE_TYPE_OCTAL 2 /* 10 */ | |
46 | ++#define VALUE_TYPE_HEXADECIMAL 3 /* 11 */ | |
47 | ++ | |
48 | ++static int tmy_parse_ulong(unsigned long *result, const char **str) | |
49 | ++{ | |
50 | ++ const char *cp = *str; | |
51 | ++ char *ep; | |
52 | ++ int base = 10; | |
53 | ++ if (*cp == '0') { | |
54 | ++ char c = *(cp + 1); | |
55 | ++ if (c == 'x' || c == 'X') { | |
56 | ++ base = 16; cp += 2; | |
57 | ++ } else if (c >= '0' && c <= '7') { | |
58 | ++ base = 8; cp++; | |
59 | ++ } | |
60 | ++ } | |
61 | ++ *result = simple_strtoul(cp, &ep, base); | |
62 | ++ if (cp == ep) return 0; /* 00 */ | |
63 | ++ *str = ep; | |
64 | ++ return (base == 16 ? VALUE_TYPE_HEXADECIMAL : | |
65 | ++ (base == 8 ? VALUE_TYPE_OCTAL : VALUE_TYPE_DECIMAL)); | |
66 | ++} | |
67 | ++ | |
68 | ++static void tmy_print_ulong(char *buffer, const int buffer_len, | |
69 | ++ const unsigned long value, const int type) | |
70 | ++{ | |
71 | ++ if (type == VALUE_TYPE_DECIMAL) | |
72 | ++ snprintf(buffer, buffer_len, "%lu", value); | |
73 | ++ else if (type == VALUE_TYPE_OCTAL) | |
74 | ++ snprintf(buffer, buffer_len, "0%lo", value); | |
75 | ++ else | |
76 | ++ snprintf(buffer, buffer_len, "0x%lX", value); | |
77 | ++} | |
78 | ++ | |
79 | ++/* List of conditins. */ | |
80 | ++static struct condition_list { | |
81 | ++ struct condition_list *next; | |
82 | ++ int length; | |
83 | ++ /* "unsigned long condition[length]" comes here.*/ | |
84 | ++} head; | |
85 | ++ | |
86 | ++#define TASK_UID 0 | |
87 | ++#define TASK_EUID 1 | |
88 | ++#define TASK_SUID 2 | |
89 | ++#define TASK_FSUID 3 | |
90 | ++#define TASK_GID 4 | |
91 | ++#define TASK_EGID 5 | |
92 | ++#define TASK_SGID 6 | |
93 | ++#define TASK_FSGID 7 | |
94 | ++#define TASK_PID 8 | |
95 | ++#define TASK_PPID 9 | |
96 | ++#define PATH1_UID 10 | |
97 | ++#define PATH1_GID 11 | |
98 | ++#define PATH1_INO 12 | |
99 | ++#define PATH1_PARENT_UID 13 | |
100 | ++#define PATH1_PARENT_GID 14 | |
101 | ++#define PATH1_PARENT_INO 15 | |
102 | ++#define PATH2_PARENT_UID 16 | |
103 | ++#define PATH2_PARENT_GID 17 | |
104 | ++#define PATH2_PARENT_INO 18 | |
105 | ++#define MAX_KEYWORD 19 | |
106 | ++ | |
107 | ++static struct { | |
108 | ++ const char *keyword; | |
109 | ++ const int keyword_len; /* strlen(keyword) */ | |
110 | ++} cc_keyword[MAX_KEYWORD] = { | |
111 | ++ [TASK_UID] = { "task.uid", 8 }, | |
112 | ++ [TASK_EUID] = { "task.euid", 9 }, | |
113 | ++ [TASK_SUID] = { "task.suid", 9 }, | |
114 | ++ [TASK_FSUID] = { "task.fsuid", 10 }, | |
115 | ++ [TASK_GID] = { "task.gid", 8 }, | |
116 | ++ [TASK_EGID] = { "task.egid", 9 }, | |
117 | ++ [TASK_SGID] = { "task.sgid", 9 }, | |
118 | ++ [TASK_FSGID] = { "task.fsgid", 10 }, | |
119 | ++ [TASK_PID] = { "task.pid", 8 }, | |
120 | ++ [TASK_PPID] = { "task.ppid", 9 }, | |
121 | ++ [PATH1_UID] = { "path1.uid", 9 }, | |
122 | ++ [PATH1_GID] = { "path1.gid", 9 }, | |
123 | ++ [PATH1_INO] = { "path1.ino", 9 }, | |
124 | ++ [PATH1_PARENT_UID] = { "path1.parent.uid", 16 }, | |
125 | ++ [PATH1_PARENT_GID] = { "path1.parent.gid", 16 }, | |
126 | ++ [PATH1_PARENT_INO] = { "path1.parent.ino", 16 }, | |
127 | ++ [PATH2_PARENT_UID] = { "path2.parent.uid", 16 }, | |
128 | ++ [PATH2_PARENT_GID] = { "path2.parent.gid", 16 }, | |
129 | ++ [PATH2_PARENT_INO] = { "path2.parent.ino", 16 } | |
130 | ++}; | |
131 | ++ | |
132 | ++/** | |
133 | ++ * tmy_assign_condition - create condition part. | |
134 | ++ * @condition: pointer to condition part. | |
135 | ++ * | |
136 | ++ * Returns pointer to "struct condition_list" on success. | |
137 | ++ * Returns NULL on failure. | |
138 | ++ */ | |
139 | ++const struct condition_list *tmy_assign_condition(const char *condition) | |
140 | ++{ | |
141 | ++ const char *start; | |
142 | ++ struct condition_list *ptr; | |
143 | ++ struct condition_list *new_ptr; | |
144 | ++ unsigned long *ptr2; | |
145 | ++ int counter = 0; | |
146 | ++ int size; | |
147 | ++ int left; | |
148 | ++ int right; | |
149 | ++ unsigned long left_min = 0; | |
150 | ++ unsigned long left_max = 0; | |
151 | ++ unsigned long right_min = 0; | |
152 | ++ unsigned long right_max = 0; | |
153 | ++ if (strncmp(condition, "if ", 3)) | |
154 | ++ return NULL; | |
155 | ++ condition += 3; | |
156 | ++ start = condition; | |
157 | ++ while (*condition) { | |
158 | ++ if (*condition == ' ') | |
159 | ++ condition++; | |
160 | ++ for (left = 0; left < MAX_KEYWORD; left++) { | |
161 | ++ if (strncmp(condition, cc_keyword[left].keyword, | |
162 | ++ cc_keyword[left].keyword_len)) | |
163 | ++ continue; | |
164 | ++ condition += cc_keyword[left].keyword_len; | |
165 | ++ break; | |
166 | ++ } | |
167 | ++ if (left == MAX_KEYWORD) { | |
168 | ++ if (!tmy_parse_ulong(&left_min, &condition)) | |
169 | ++ goto out; | |
170 | ++ counter++; /* body */ | |
171 | ++ if (*condition != '-') | |
172 | ++ goto not_range1; | |
173 | ++ condition++; | |
174 | ++ if (!tmy_parse_ulong(&left_max, &condition) | |
175 | ++ || left_min > left_max) | |
176 | ++ goto out; | |
177 | ++ counter++; /* body */ | |
178 | ++not_range1: ; | |
179 | ++ } | |
180 | ++ if (strncmp(condition, "!=", 2) == 0) | |
181 | ++ condition += 2; | |
182 | ++ else if (*condition == '=') | |
183 | ++ condition++; | |
184 | ++ else | |
185 | ++ goto out; | |
186 | ++ counter++; /* header */ | |
187 | ++ for (right = 0; right < MAX_KEYWORD; right++) { | |
188 | ++ if (strncmp(condition, cc_keyword[right].keyword, | |
189 | ++ cc_keyword[right].keyword_len)) | |
190 | ++ continue; | |
191 | ++ condition += cc_keyword[right].keyword_len; | |
192 | ++ break; | |
193 | ++ } | |
194 | ++ if (right == MAX_KEYWORD) { | |
195 | ++ if (!tmy_parse_ulong(&right_min, &condition)) | |
196 | ++ goto out; | |
197 | ++ counter++; /* body */ | |
198 | ++ if (*condition != '-') | |
199 | ++ goto not_range2; | |
200 | ++ condition++; | |
201 | ++ if (!tmy_parse_ulong(&right_max, &condition) | |
202 | ++ || right_min > right_max) | |
203 | ++ goto out; | |
204 | ++ counter++; /* body */ | |
205 | ++not_range2: ; | |
206 | ++ } | |
207 | ++ } | |
208 | ++ size = sizeof(*new_ptr) + counter * sizeof(unsigned long); | |
209 | ++ new_ptr = tmy_alloc(size); | |
210 | ++ if (!new_ptr) | |
211 | ++ return NULL; | |
212 | ++ new_ptr->length = counter; | |
213 | ++ ptr2 = (unsigned long *) (((u8 *) new_ptr) + sizeof(*new_ptr)); | |
214 | ++ condition = start; | |
215 | ++ while (*condition) { | |
216 | ++ unsigned int match = 0; | |
217 | ++ if (*condition == ' ') | |
218 | ++ condition++; | |
219 | ++ for (left = 0; left < MAX_KEYWORD; left++) { | |
220 | ++ if (strncmp(condition, cc_keyword[left].keyword, | |
221 | ++ cc_keyword[left].keyword_len)) | |
222 | ++ continue; | |
223 | ++ condition += cc_keyword[left].keyword_len; | |
224 | ++ break; | |
225 | ++ } | |
226 | ++ if (left == MAX_KEYWORD) { | |
227 | ++ match |= tmy_parse_ulong(&left_min, &condition) << 2; | |
228 | ++ counter--; /* body */ | |
229 | ++ if (*condition != '-') | |
230 | ++ goto not_range3; | |
231 | ++ condition++; | |
232 | ++ match |= tmy_parse_ulong(&left_max, &condition) << 4; | |
233 | ++ counter--; /* body */ | |
234 | ++ left++; | |
235 | ++not_range3: ; | |
236 | ++ } | |
237 | ++ if (strncmp(condition, "!=", 2) == 0) | |
238 | ++ condition += 2; | |
239 | ++ else if (*condition == '=') { | |
240 | ++ match |= 1; condition++; | |
241 | ++ } else | |
242 | ++ goto out2; | |
243 | ++ counter--; /* header */ | |
244 | ++ for (right = 0; right < MAX_KEYWORD; right++) { | |
245 | ++ if (strncmp(condition, cc_keyword[right].keyword, | |
246 | ++ cc_keyword[right].keyword_len)) | |
247 | ++ continue; | |
248 | ++ condition += cc_keyword[right].keyword_len; | |
249 | ++ break; | |
250 | ++ } | |
251 | ++ if (right == MAX_KEYWORD) { | |
252 | ++ match |= tmy_parse_ulong(&right_min, &condition) << 6; | |
253 | ++ counter--; /* body */ | |
254 | ++ if (*condition != '-') | |
255 | ++ goto not_range4; | |
256 | ++ condition++; | |
257 | ++ match |= tmy_parse_ulong(&right_max, &condition) << 8; | |
258 | ++ counter--; /* body */ | |
259 | ++ right++; | |
260 | ++not_range4: ; | |
261 | ++ } | |
262 | ++ if (counter < 0) | |
263 | ++ goto out2; | |
264 | ++ *ptr2++ = (match << 16) | (left << 8) | right; | |
265 | ++ if (left >= MAX_KEYWORD) | |
266 | ++ *ptr2++ = left_min; | |
267 | ++ if (left == MAX_KEYWORD + 1) | |
268 | ++ *ptr2++ = left_max; | |
269 | ++ if (right >= MAX_KEYWORD) | |
270 | ++ *ptr2++ = right_min; | |
271 | ++ if (right == MAX_KEYWORD + 1) | |
272 | ++ *ptr2++ = right_max; | |
273 | ++ } | |
274 | ++ { | |
275 | ++ static DEFINE_MUTEX(mutex); | |
276 | ++ struct condition_list *prev = NULL; | |
277 | ++ mutex_lock(&mutex); | |
278 | ++ for (ptr = &head; ptr; prev = ptr, ptr = ptr->next) { | |
279 | ++ /* Don't compare if size differs. */ | |
280 | ++ if (ptr->length != new_ptr->length) | |
281 | ++ continue; | |
282 | ++ /* | |
283 | ++ * Compare ptr and new_ptr | |
284 | ++ * except ptr->next and new_ptr->next . | |
285 | ++ */ | |
286 | ++ if (memcmp(((u8 *) ptr) + sizeof(ptr->next), | |
287 | ++ ((u8 *) new_ptr) + sizeof(new_ptr->next), | |
288 | ++ size - sizeof(ptr->next))) | |
289 | ++ continue; | |
290 | ++ /* Same entry found. Share this entry. */ | |
291 | ++ tmy_free(new_ptr); | |
292 | ++ new_ptr = ptr; | |
293 | ++ goto ok; | |
294 | ++ } | |
295 | ++ /* Same entry not found. Save this entry. */ | |
296 | ++ ptr = tmy_alloc_element(size); | |
297 | ++ if (ptr) | |
298 | ++ memmove(ptr, new_ptr, size); | |
299 | ++ tmy_free(new_ptr); | |
300 | ++ new_ptr = ptr; | |
301 | ++ /* Append to chain. */ | |
302 | ++ prev->next = new_ptr; | |
303 | ++ok: ; | |
304 | ++ mutex_unlock(&mutex); | |
305 | ++ } | |
306 | ++ return new_ptr; | |
307 | ++out2: ; | |
308 | ++ tmy_free(new_ptr); | |
309 | ++out: ; | |
310 | ++ return NULL; | |
311 | ++} | |
312 | ++ | |
313 | ++/* Get inode's attribute information. */ | |
314 | ++static void tmy_get_attributes(struct obj_info *obj) | |
315 | ++{ | |
316 | ++ struct vfsmount *mnt; | |
317 | ++ struct dentry *dentry; | |
318 | ++ struct inode *inode; | |
319 | ++ struct kstat stat; | |
320 | ++ | |
321 | ++ mnt = obj->path1_vfsmnt; | |
322 | ++ dentry = obj->path1_dentry; | |
323 | ++ inode = dentry->d_inode; | |
324 | ++ if (inode) { | |
325 | ++ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) { | |
326 | ++ /* Nothing to do. */ | |
327 | ++ } else { | |
328 | ++ obj->path1_stat.uid = stat.uid; | |
329 | ++ obj->path1_stat.gid = stat.gid; | |
330 | ++ obj->path1_stat.ino = stat.ino; | |
331 | ++ obj->path1_valid = 1; | |
332 | ++ } | |
333 | ++ } | |
334 | ++ | |
335 | ++ dentry = dget_parent(obj->path1_dentry); | |
336 | ++ inode = dentry->d_inode; | |
337 | ++ if (inode) { | |
338 | ++ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) { | |
339 | ++ /* Nothing to do. */ | |
340 | ++ } else { | |
341 | ++ obj->path1_parent_stat.uid = stat.uid; | |
342 | ++ obj->path1_parent_stat.gid = stat.gid; | |
343 | ++ obj->path1_parent_stat.ino = stat.ino; | |
344 | ++ obj->path1_parent_valid = 1; | |
345 | ++ } | |
346 | ++ } | |
347 | ++ dput(dentry); | |
348 | ++ | |
349 | ++ mnt = obj->path2_vfsmnt; | |
350 | ++ if (mnt) { | |
351 | ++ dentry = dget_parent(obj->path2_dentry); | |
352 | ++ inode = dentry->d_inode; | |
353 | ++ if (inode) { | |
354 | ++ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) { | |
355 | ++ /* Nothing to do. */ | |
356 | ++ } else { | |
357 | ++ obj->path2_parent_stat.uid = stat.uid; | |
358 | ++ obj->path2_parent_stat.gid = stat.gid; | |
359 | ++ obj->path2_parent_stat.ino = stat.ino; | |
360 | ++ obj->path2_parent_valid = 1; | |
361 | ++ } | |
362 | ++ } | |
363 | ++ dput(dentry); | |
364 | ++ } | |
365 | ++} | |
366 | ++ | |
367 | ++/** | |
368 | ++ * tmy_check_condition - check condition part. | |
369 | ++ * @ptr: pointer to "struct condition_list". | |
370 | ++ * @obj: pointer to "struct obj_info". May be NULL. | |
371 | ++ * | |
372 | ++ * Returns zero on success. | |
373 | ++ * Returns nonzero on failure. | |
374 | ++ */ | |
375 | ++int tmy_check_condition(const struct condition_list *ptr, struct obj_info *obj) | |
376 | ++{ | |
377 | ++ struct task_struct *task = current; | |
378 | ++ int i; | |
379 | ++ unsigned long left_min = 0; | |
380 | ++ unsigned long left_max = 0; | |
381 | ++ unsigned long right_min = 0; | |
382 | ++ unsigned long right_max = 0; | |
383 | ++ const unsigned long *ptr2; | |
384 | ++ if (!ptr) | |
385 | ++ return 0; | |
386 | ++ ptr2 = (unsigned long *) (((u8 *) ptr) + sizeof(*ptr)); | |
387 | ++ for (i = 0; i < ptr->length; i++) { | |
388 | ++ const bool match = ((*ptr2) >> 16) & 1; | |
389 | ++ const u8 left = (*ptr2) >> 8; | |
390 | ++ const u8 right = *ptr2; | |
391 | ++ ptr2++; | |
392 | ++ if ((left >= PATH1_UID && left < MAX_KEYWORD) | |
393 | ++ || (right >= PATH1_UID && right < MAX_KEYWORD)) { | |
394 | ++ if (!obj) | |
395 | ++ goto out; | |
396 | ++ if (!obj->validate_done) { | |
397 | ++ tmy_get_attributes(obj); | |
398 | ++ obj->validate_done = 1; | |
399 | ++ } | |
400 | ++ } | |
401 | ++ switch (left) { | |
402 | ++ case TASK_UID: | |
403 | ++ left_min = task->uid; | |
404 | ++ left_max = left_min; | |
405 | ++ break; | |
406 | ++ case TASK_EUID: | |
407 | ++ left_min = task->euid; | |
408 | ++ left_max = left_min; | |
409 | ++ break; | |
410 | ++ case TASK_SUID: | |
411 | ++ left_min = task->suid; | |
412 | ++ left_max = left_min; | |
413 | ++ break; | |
414 | ++ case TASK_FSUID: | |
415 | ++ left_min = task->fsuid; | |
416 | ++ left_max = left_min; | |
417 | ++ break; | |
418 | ++ case TASK_GID: | |
419 | ++ left_min = task->gid; | |
420 | ++ left_max = left_min; | |
421 | ++ break; | |
422 | ++ case TASK_EGID: | |
423 | ++ left_min = task->egid; | |
424 | ++ left_max = left_min; | |
425 | ++ break; | |
426 | ++ case TASK_SGID: | |
427 | ++ left_min = task->sgid; | |
428 | ++ left_max = left_min; | |
429 | ++ break; | |
430 | ++ case TASK_FSGID: | |
431 | ++ left_min = task->fsgid; | |
432 | ++ left_max = left_min; | |
433 | ++ break; | |
434 | ++ case TASK_PID: | |
435 | ++ left_min = task->pid; | |
436 | ++ left_max = left_min; | |
437 | ++ break; | |
438 | ++ case TASK_PPID: | |
439 | ++ left_min = sys_getppid(); | |
440 | ++ left_max = left_min; | |
441 | ++ break; | |
442 | ++ case PATH1_UID: | |
443 | ++ if (!obj->path1_valid) | |
444 | ++ goto out; | |
445 | ++ left_min = obj->path1_stat.uid; | |
446 | ++ left_max = left_min; | |
447 | ++ break; | |
448 | ++ case PATH1_GID: | |
449 | ++ if (!obj->path1_valid) | |
450 | ++ goto out; | |
451 | ++ left_min = obj->path1_stat.gid; | |
452 | ++ left_max = left_min; | |
453 | ++ break; | |
454 | ++ case PATH1_INO: | |
455 | ++ if (!obj->path1_valid) | |
456 | ++ goto out; | |
457 | ++ left_min = obj->path1_stat.ino; | |
458 | ++ left_max = left_min; | |
459 | ++ break; | |
460 | ++ case PATH1_PARENT_UID: | |
461 | ++ if (!obj->path1_parent_valid) | |
462 | ++ goto out; | |
463 | ++ left_min = obj->path1_parent_stat.uid; | |
464 | ++ left_max = left_min; | |
465 | ++ break; | |
466 | ++ case PATH1_PARENT_GID: | |
467 | ++ if (!obj->path1_parent_valid) | |
468 | ++ goto out; | |
469 | ++ left_min = obj->path1_parent_stat.gid; | |
470 | ++ left_max = left_min; | |
471 | ++ break; | |
472 | ++ case PATH1_PARENT_INO: | |
473 | ++ if (!obj->path1_parent_valid) | |
474 | ++ goto out; | |
475 | ++ left_min = obj->path1_parent_stat.ino; | |
476 | ++ left_max = left_min; | |
477 | ++ break; | |
478 | ++ case PATH2_PARENT_UID: | |
479 | ++ if (!obj->path2_parent_valid) | |
480 | ++ goto out; | |
481 | ++ left_min = obj->path2_parent_stat.uid; | |
482 | ++ left_max = left_min; | |
483 | ++ break; | |
484 | ++ case PATH2_PARENT_GID: | |
485 | ++ if (!obj->path2_parent_valid) | |
486 | ++ goto out; | |
487 | ++ left_min = obj->path2_parent_stat.gid; | |
488 | ++ left_max = left_min; | |
489 | ++ break; | |
490 | ++ case PATH2_PARENT_INO: | |
491 | ++ if (!obj->path2_parent_valid) | |
492 | ++ goto out; | |
493 | ++ left_min = obj->path2_parent_stat.ino; | |
494 | ++ left_max = left_min; | |
495 | ++ break; | |
496 | ++ case MAX_KEYWORD: | |
497 | ++ left_min = *ptr2++; | |
498 | ++ left_max = left_min; | |
499 | ++ i++; | |
500 | ++ break; | |
501 | ++ case MAX_KEYWORD + 1: | |
502 | ++ left_min = *ptr2++; | |
503 | ++ left_max = *ptr2++; | |
504 | ++ i += 2; | |
505 | ++ break; | |
506 | ++ } | |
507 | ++ switch (right) { | |
508 | ++ case TASK_UID: | |
509 | ++ right_min = task->uid; | |
510 | ++ right_max = right_min; | |
511 | ++ break; | |
512 | ++ case TASK_EUID: | |
513 | ++ right_min = task->euid; | |
514 | ++ right_max = right_min; | |
515 | ++ break; | |
516 | ++ case TASK_SUID: | |
517 | ++ right_min = task->suid; | |
518 | ++ right_max = right_min; | |
519 | ++ break; | |
520 | ++ case TASK_FSUID: | |
521 | ++ right_min = task->fsuid; | |
522 | ++ right_max = right_min; | |
523 | ++ break; | |
524 | ++ case TASK_GID: | |
525 | ++ right_min = task->gid; | |
526 | ++ right_max = right_min; | |
527 | ++ break; | |
528 | ++ case TASK_EGID: | |
529 | ++ right_min = task->egid; | |
530 | ++ right_max = right_min; | |
531 | ++ break; | |
532 | ++ case TASK_SGID: | |
533 | ++ right_min = task->sgid; | |
534 | ++ right_max = right_min; | |
535 | ++ break; | |
536 | ++ case TASK_FSGID: | |
537 | ++ right_min = task->fsgid; | |
538 | ++ right_max = right_min; | |
539 | ++ break; | |
540 | ++ case TASK_PID: | |
541 | ++ right_min = task->pid; | |
542 | ++ right_max = right_min; | |
543 | ++ break; | |
544 | ++ case TASK_PPID: | |
545 | ++ right_min = sys_getppid(); | |
546 | ++ right_max = right_min; | |
547 | ++ break; | |
548 | ++ case PATH1_UID: | |
549 | ++ if (!obj->path1_valid) | |
550 | ++ goto out; | |
551 | ++ right_min = obj->path1_stat.uid; | |
552 | ++ right_max = right_min; | |
553 | ++ break; | |
554 | ++ case PATH1_GID: | |
555 | ++ if (!obj->path1_valid) | |
556 | ++ goto out; | |
557 | ++ right_min = obj->path1_stat.gid; | |
558 | ++ right_max = right_min; | |
559 | ++ break; | |
560 | ++ case PATH1_INO: | |
561 | ++ if (!obj->path1_valid) | |
562 | ++ goto out; | |
563 | ++ right_min = obj->path1_stat.ino; | |
564 | ++ right_max = right_min; | |
565 | ++ break; | |
566 | ++ case PATH1_PARENT_UID: | |
567 | ++ if (!obj->path1_parent_valid) | |
568 | ++ goto out; | |
569 | ++ right_min = obj->path1_parent_stat.uid; | |
570 | ++ right_max = right_min; | |
571 | ++ break; | |
572 | ++ case PATH1_PARENT_GID: | |
573 | ++ if (!obj->path1_parent_valid) | |
574 | ++ goto out; | |
575 | ++ right_min = obj->path1_parent_stat.gid; | |
576 | ++ right_max = right_min; | |
577 | ++ break; | |
578 | ++ case PATH1_PARENT_INO: | |
579 | ++ if (!obj->path1_parent_valid) | |
580 | ++ goto out; | |
581 | ++ right_min = obj->path1_parent_stat.ino; | |
582 | ++ right_max = right_min; | |
583 | ++ break; | |
584 | ++ case PATH2_PARENT_UID: | |
585 | ++ if (!obj->path2_parent_valid) | |
586 | ++ goto out; | |
587 | ++ right_min = obj->path2_parent_stat.uid; | |
588 | ++ right_max = right_min; | |
589 | ++ break; | |
590 | ++ case PATH2_PARENT_GID: | |
591 | ++ if (!obj->path2_parent_valid) | |
592 | ++ goto out; | |
593 | ++ right_min = obj->path2_parent_stat.gid; | |
594 | ++ right_max = right_min; | |
595 | ++ break; | |
596 | ++ case PATH2_PARENT_INO: | |
597 | ++ if (!obj->path2_parent_valid) | |
598 | ++ goto out; | |
599 | ++ right_min = obj->path2_parent_stat.ino; | |
600 | ++ right_max = right_min; | |
601 | ++ break; | |
602 | ++ case MAX_KEYWORD: | |
603 | ++ right_min = *ptr2++; | |
604 | ++ right_max = right_min; | |
605 | ++ i++; | |
606 | ++ break; | |
607 | ++ case MAX_KEYWORD + 1: | |
608 | ++ right_min = *ptr2++; | |
609 | ++ right_max = *ptr2++; | |
610 | ++ i += 2; | |
611 | ++ break; | |
612 | ++ } | |
613 | ++ if (match) { | |
614 | ++ if (left_min <= right_max && left_max >= right_min) | |
615 | ++ continue; | |
616 | ++ } else { | |
617 | ++ if (left_min > right_max || left_max < right_min) | |
618 | ++ continue; | |
619 | ++ } | |
620 | ++out: ; | |
621 | ++ return -EPERM; | |
622 | ++ } | |
623 | ++ return 0; | |
624 | ++} | |
625 | ++ | |
626 | ++/** | |
627 | ++ * tmy_dump_condition - dump condition part. | |
628 | ++ * @head: pointer to "struct io_buffer". | |
629 | ++ * @ptr: pointer to "struct condition_list". May be NULL. | |
630 | ++ * | |
631 | ++ * Returns nonzero if reading incomplete. | |
632 | ++ * Returns zero otherwise. | |
633 | ++ */ | |
634 | ++int tmy_dump_condition(struct io_buffer *head, const struct condition_list *ptr) | |
635 | ++{ | |
636 | ++ int i; | |
637 | ++ const unsigned long *ptr2; | |
638 | ++ char buffer[32]; | |
639 | ++ if (!ptr) | |
640 | ++ goto last; | |
641 | ++ ptr2 = (unsigned long *) (((u8 *) ptr) + sizeof(*ptr)); | |
642 | ++ memset(buffer, 0, sizeof(buffer)); | |
643 | ++ for (i = 0; i < ptr->length; i++) { | |
644 | ++ const u16 match = (*ptr2) >> 16; | |
645 | ++ const u8 left = (*ptr2) >> 8; | |
646 | ++ const u8 right = *ptr2; | |
647 | ++ ptr2++; | |
648 | ++ if (tmy_io_printf(head, "%s", i ? " " : " if ")) | |
649 | ++ break; | |
650 | ++ if (left < MAX_KEYWORD) { | |
651 | ++ if (tmy_io_printf(head, "%s", cc_keyword[left].keyword)) | |
652 | ++ break; | |
653 | ++ } else { | |
654 | ++ tmy_print_ulong(buffer, sizeof(buffer) - 1, *ptr2++, | |
655 | ++ (match >> 2) & 3); | |
656 | ++ if (tmy_io_printf(head, "%s", buffer)) | |
657 | ++ break; | |
658 | ++ i++; | |
659 | ++ if (left == MAX_KEYWORD + 1) { | |
660 | ++ tmy_print_ulong(buffer, sizeof(buffer) - 1, | |
661 | ++ *ptr2++, (match >> 4) & 3); | |
662 | ++ if (tmy_io_printf(head, "-%s", buffer)) | |
663 | ++ break; | |
664 | ++ i++; | |
665 | ++ } | |
666 | ++ } | |
667 | ++ if (tmy_io_printf(head, "%s", (match & 1) ? "=" : "!=")) | |
668 | ++ break; | |
669 | ++ if (right < MAX_KEYWORD) { | |
670 | ++ if (tmy_io_printf(head, "%s", | |
671 | ++ cc_keyword[right].keyword)) | |
672 | ++ break; | |
673 | ++ } else { | |
674 | ++ tmy_print_ulong(buffer, sizeof(buffer) - 1, *ptr2++, | |
675 | ++ (match >> 6) & 3); | |
676 | ++ if (tmy_io_printf(head, "%s", buffer)) | |
677 | ++ break; | |
678 | ++ i++; | |
679 | ++ if (right == MAX_KEYWORD + 1) { | |
680 | ++ tmy_print_ulong(buffer, sizeof(buffer) - 1, | |
681 | ++ *ptr2++, (match >> 8) & 3); | |
682 | ++ if (tmy_io_printf(head, "-%s", buffer)) | |
683 | ++ break; | |
684 | ++ i++; | |
685 | ++ } | |
686 | ++ } | |
687 | ++ } | |
688 | ++ if (i < ptr->length) | |
689 | ++ return -ENOMEM; | |
690 | ++last: ; | |
691 | ++ return tmy_io_printf(head, "\n") ? -ENOMEM : 0; | |
692 | ++} |
@@ -0,0 +1,2456 @@ | ||
1 | +Common functions for TOMOYO Linux. | |
2 | + | |
3 | +TOMOYO Linux uses /sys/kernel/security/tomoyo interface for configuration. | |
4 | + | |
5 | +/sys/kernel/security/tomoyo/domain_policy is the domain-based access policy. | |
6 | +Access control list for files, networks, argv[0], capabilities and signal is | |
7 | +stored in domain_policy. | |
8 | + | |
9 | +/sys/kernel/security/tomoyo/system_policy is the system-wide access policy. | |
10 | +Access control list for mount, umount and pivot_root is | |
11 | +stored in system_policy. | |
12 | + | |
13 | +/sys/kernel/security/tomoyo/exception_policy is the other settings such as | |
14 | +globally readable files, domain transition configurations | |
15 | +or pre-defined patterned pathnames. | |
16 | + | |
17 | +/sys/kernel/security/tomoyo/profile has some profiles, which configure | |
18 | +the access control level of TOMOYO Linux. A profile is assigned to a domain. | |
19 | + | |
20 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
21 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
22 | + security/tomoyo/common.c | 2429 +++++++++++++++++++++++++++++++++++++++++++++++ | |
23 | + 1 file changed, 2429 insertions(+) | |
24 | + | |
25 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
26 | ++++ linux-2.6.23/security/tomoyo/common.c 2007-11-21 09:31:43.462446264 +0900 | |
27 | +@@ -0,0 +1,2429 @@ | |
28 | ++/* | |
29 | ++ * security/tomoyo/common.c | |
30 | ++ * | |
31 | ++ * Common functions for TOMOYO Linux. | |
32 | ++ */ | |
33 | ++ | |
34 | ++#include "tomoyo.h" | |
35 | ++#include "realpath.h" | |
36 | ++ | |
37 | ++#define MAX_ACCEPT_ENTRY 2048 | |
38 | ++ | |
39 | ++static int tmy_read_control(struct file *file, | |
40 | ++ char __user *buffer, | |
41 | ++ const int buffer_len); | |
42 | ++ | |
43 | ++/************************* VARIABLES *************************/ | |
44 | ++ | |
45 | ++/* /sbin/init started? */ | |
46 | ++bool sbin_init_started; | |
47 | ++ | |
48 | ++static struct { | |
49 | ++ const char *keyword; | |
50 | ++ unsigned int current_value; | |
51 | ++ const unsigned int max_value; | |
52 | ++} tmy_control[TMY_MAX_CONTROL_INDEX] = { | |
53 | ++ [TMY_COMMENT] = { "COMMENT", 0, 0 }, | |
54 | ++ [TMY_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, | |
55 | ++ [TMY_MAC_FOR_ARGV0] = { "MAC_FOR_ARGV0", 0, 3 }, | |
56 | ++ [TMY_MAC_FOR_NETWORK] = { "MAC_FOR_NETWORK", 0, 3 }, | |
57 | ++ [TMY_MAC_FOR_SIGNAL] = { "MAC_FOR_SIGNAL", 0, 3 }, | |
58 | ++ [TMY_DENY_CONCEAL_MOUNT] = { "DENY_CONCEAL_MOUNT", 0, 3 }, | |
59 | ++ [TMY_RESTRICT_MOUNT] = { "RESTRICT_MOUNT", 0, 3 }, | |
60 | ++ [TMY_RESTRICT_UMOUNT] = { "RESTRICT_UNMOUNT", 0, 3 }, | |
61 | ++ [TMY_RESTRICT_PIVOT_ROOT] = { "RESTRICT_PIVOT_ROOT", 0, 3 }, | |
62 | ++ [TMY_MAX_ACCEPT_ENTRY] = | |
63 | ++ { "MAX_ACCEPT_ENTRY", MAX_ACCEPT_ENTRY, INT_MAX }, | |
64 | ++ [TMY_MAX_GRANT_LOG] = { "MAX_GRANT_LOG", 1024, INT_MAX }, | |
65 | ++ [TMY_MAX_REJECT_LOG] = { "MAX_REJECT_LOG", 1024, INT_MAX }, | |
66 | ++ [TMY_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, | |
67 | ++ [TMY_ALLOW_ENFORCE_GRACE] = { "ALLOW_ENFORCE_GRACE", 0, 1 }, | |
68 | ++}; | |
69 | ++ | |
70 | ++struct profile { | |
71 | ++ unsigned int value[TMY_MAX_CONTROL_INDEX]; | |
72 | ++ const struct path_info *comment; | |
73 | ++}; | |
74 | ++ | |
75 | ++static struct profile *profile_ptr[TMY_MAX_PROFILES]; | |
76 | ++ | |
77 | ++/************************* UTILITY FUNCTIONS *************************/ | |
78 | ++ | |
79 | ++/* Is the current process running as root? */ | |
80 | ++static int tmy_is_root(void) | |
81 | ++{ | |
82 | ++ return !current->uid && !current->euid; | |
83 | ++} | |
84 | ++ | |
85 | ++/** | |
86 | ++ * tmy_normalize_line - make a line tidy. | |
87 | ++ * @buffer: the line to make tidy. | |
88 | ++ * | |
89 | ++ * All tokens (such as pathnames) used in TOMOYO Linux contains | |
90 | ++ * only ASCII printable (i.e. 0x21-0x7E) range characters. | |
91 | ++ * This allows policy files and auditing logs split monotonically | |
92 | ++ * using space (i.e. ' ') and new line (i.e. '\n') characters. | |
93 | ++ * | |
94 | ++ * Remove leading and trailing non ASCII printable chracters and | |
95 | ++ * replace one or more non ASCII printable chracters with single space. | |
96 | ++ */ | |
97 | ++static void tmy_normalize_line(unsigned char *buffer) | |
98 | ++{ | |
99 | ++ unsigned char *sp = buffer; | |
100 | ++ unsigned char *dp = buffer; | |
101 | ++ int first = 1; | |
102 | ++ | |
103 | ++ while (*sp && (*sp <= 0x20 || *sp >= 0x7F)) | |
104 | ++ sp++; | |
105 | ++ | |
106 | ++ while (*sp) { | |
107 | ++ if (!first) | |
108 | ++ *dp++ = ' '; | |
109 | ++ first = 0; | |
110 | ++ while (*sp > 0x20 && *sp < 0x7F) | |
111 | ++ *dp++ = *sp++; | |
112 | ++ while (*sp && (*sp <= 0x20 || *sp >= 0x7F)) | |
113 | ++ sp++; | |
114 | ++ } | |
115 | ++ | |
116 | ++ *dp = '\0'; | |
117 | ++} | |
118 | ++ | |
119 | ++/* Is @c the first letter of "\ooo" expression? */ | |
120 | ++static int tmy_char_is_0to3(const unsigned char c) | |
121 | ++{ | |
122 | ++ return c >= '0' && c <= '3'; | |
123 | ++} | |
124 | ++ | |
125 | ++/* Is @c the second or third letter of "\ooo" expression? */ | |
126 | ++static int tmy_char_is_0to7(const unsigned char c) | |
127 | ++{ | |
128 | ++ return c >= '0' && c <= '7'; | |
129 | ++} | |
130 | ++ | |
131 | ++/* Is @c a decimal letter? */ | |
132 | ++static int tmy_char_is_0to9(const unsigned char c) | |
133 | ++{ | |
134 | ++ return c >= '0' && c <= '9'; | |
135 | ++} | |
136 | ++ | |
137 | ++/* Is @c a hexadecimal letter? */ | |
138 | ++static int tmy_char_is_hex(const unsigned char c) | |
139 | ++{ | |
140 | ++ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') | |
141 | ++ || (c >= 'a' && c <= 'f'); | |
142 | ++} | |
143 | ++ | |
144 | ++/* Is @c an alphabet letter? */ | |
145 | ++static int tmy_char_is_alpha(const unsigned char c) | |
146 | ++{ | |
147 | ++ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
148 | ++} | |
149 | ++ | |
150 | ++/* Convert \ooo style expression to a byte. */ | |
151 | ++static unsigned char tmy_str2chr(const unsigned char c, | |
152 | ++ const unsigned char d, | |
153 | ++ const unsigned char e) | |
154 | ++{ | |
155 | ++ return (((unsigned char) (c - '0')) << 6) + | |
156 | ++ (((unsigned char) (d - '0')) << 3) + | |
157 | ++ (((unsigned char) (e - '0'))); | |
158 | ++} | |
159 | ++ | |
160 | ++/* Does the @src starts with @find? */ | |
161 | ++static int tmy_strstarts(char **src, const char *find) | |
162 | ++{ | |
163 | ++ const int len = strlen(find); | |
164 | ++ char *tmp = *src; | |
165 | ++ | |
166 | ++ if (strncmp(tmp, find, len) == 0) { | |
167 | ++ tmp += len; | |
168 | ++ *src = tmp; | |
169 | ++ return 1; | |
170 | ++ } | |
171 | ++ | |
172 | ++ return 0; | |
173 | ++} | |
174 | ++ | |
175 | ++/** | |
176 | ++ * tmy_correct_path - validate a pathname. | |
177 | ++ * @filename: the pathname to check | |
178 | ++ * @start_type: 1 if the pathname must start with '/' | |
179 | ++ * -1 if the pathname must not start with '/' | |
180 | ++ * 0 if it does not matter | |
181 | ++ * @pattern_type: 1 if the pathname must contain patterns | |
182 | ++ * -1 if the pathname must not contain patterns | |
183 | ++ * 0 if it does not matter | |
184 | ++ * @end_type: 1 if the pathname must end with '/' | |
185 | ++ * -1 if the pathname must not end with '/' | |
186 | ++ * 0 if it does not matter | |
187 | ++ * @function: string to display if the @filename is invalid | |
188 | ++ * | |
189 | ++ * Returns true if the pathname is valid. | |
190 | ++ * Returns false otherwise. | |
191 | ++ * | |
192 | ++ * Check whether the given pathname follows the naming rules. | |
193 | ++ */ | |
194 | ++bool tmy_correct_path(const char *filename, | |
195 | ++ const int start_type, | |
196 | ++ const int pattern_type, | |
197 | ++ const int end_type, | |
198 | ++ const char *function) | |
199 | ++{ | |
200 | ++ int contains_pattern = 0; | |
201 | ++ char c; | |
202 | ++ char d; | |
203 | ++ char e; | |
204 | ++ const char *original_filename = filename; | |
205 | ++ | |
206 | ++ if (!filename) | |
207 | ++ goto out; | |
208 | ++ | |
209 | ++ c = *filename; | |
210 | ++ if (start_type == 1) { /* Must start with '/' */ | |
211 | ++ if (c != '/') | |
212 | ++ goto out; | |
213 | ++ } else if (start_type == -1) { /* Must not start with '/' */ | |
214 | ++ if (c == '/') | |
215 | ++ goto out; | |
216 | ++ } | |
217 | ++ | |
218 | ++ if (c) | |
219 | ++ c = *(strchr(filename, '\0') - 1); | |
220 | ++ if (end_type == 1) { /* Must end with '/' */ | |
221 | ++ if (c != '/') | |
222 | ++ goto out; | |
223 | ++ } else if (end_type == -1) { /* Must not end with '/' */ | |
224 | ++ if (c == '/') | |
225 | ++ goto out; | |
226 | ++ } | |
227 | ++ | |
228 | ++ while ((c = *filename++) != '\0') { | |
229 | ++ if (c == '\\') { | |
230 | ++ unsigned char f; | |
231 | ++ switch ((c = *filename++)) { | |
232 | ++ case '\\': /* "\\" */ | |
233 | ++ continue; | |
234 | ++ case '$': /* "\$" */ | |
235 | ++ case '+': /* "\+" */ | |
236 | ++ case '?': /* "\?" */ | |
237 | ++ case '*': /* "\*" */ | |
238 | ++ case '@': /* "\@" */ | |
239 | ++ case 'x': /* "\x" */ | |
240 | ++ case 'X': /* "\X" */ | |
241 | ++ case 'a': /* "\a" */ | |
242 | ++ case 'A': /* "\A" */ | |
243 | ++ case '-': /* "\-" */ | |
244 | ++ if (pattern_type == -1) | |
245 | ++ goto out; /* Must not contain pattern */ | |
246 | ++ contains_pattern = 1; | |
247 | ++ continue; | |
248 | ++ case '0': /* "\ooo" */ | |
249 | ++ case '1': | |
250 | ++ case '2': | |
251 | ++ case '3': | |
252 | ++ d = *filename++; | |
253 | ++ if (!tmy_char_is_0to7(d)) | |
254 | ++ goto out; | |
255 | ++ e = *filename++; | |
256 | ++ if (!tmy_char_is_0to7(e)) | |
257 | ++ goto out; | |
258 | ++ f = tmy_str2chr(c, d, e); | |
259 | ++ if (f && (f <= 0x20 || f >= 0x7F)) | |
260 | ++ /* valid \ooo expression */ | |
261 | ++ continue; | |
262 | ++ } | |
263 | ++ goto out; | |
264 | ++ } else if (c <= 0x20 || c >= 0x7F) | |
265 | ++ goto out; | |
266 | ++ } | |
267 | ++ | |
268 | ++ if (pattern_type == 1) { /* Must contain pattern */ | |
269 | ++ if (!contains_pattern) | |
270 | ++ goto out; | |
271 | ++ } | |
272 | ++ | |
273 | ++ return 1; | |
274 | ++ | |
275 | ++out: ; | |
276 | ++ printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", | |
277 | ++ function, original_filename); | |
278 | ++ return 0; | |
279 | ++} | |
280 | ++ | |
281 | ++/** | |
282 | ++ * tmy_is_correct_domain - validate a domainname. | |
283 | ++ * @domainname: the domainname to check. | |
284 | ++ * @function: string to display if the @domainname is invalid. | |
285 | ++ * | |
286 | ++ * Returns true if the domainname is valid. | |
287 | ++ * Returns false otherwise. | |
288 | ++ * | |
289 | ++ * Check whether the given domainname follows the naming rules. | |
290 | ++ */ | |
291 | ++bool tmy_is_correct_domain(const unsigned char *domainname, | |
292 | ++ const char *function) | |
293 | ++{ | |
294 | ++ unsigned char c; | |
295 | ++ unsigned char d; | |
296 | ++ unsigned char e; | |
297 | ++ const char *org_domainname = domainname; | |
298 | ++ | |
299 | ++ if (!domainname || !tmy_strstarts((char **) &domainname, TMY_ROOT_NAME)) | |
300 | ++ goto out; | |
301 | ++ | |
302 | ++ if (!*domainname) | |
303 | ++ return 1; | |
304 | ++ | |
305 | ++ do { | |
306 | ++ /* 0x20 is a domainname separator. */ | |
307 | ++ if (*domainname++ != ' ') | |
308 | ++ goto out; | |
309 | ++ /* Must starts with '/'. */ | |
310 | ++ if (*domainname++ != '/') | |
311 | ++ goto out; | |
312 | ++ while ((c = *domainname) != '\0' && c != ' ') { | |
313 | ++ domainname++; | |
314 | ++ if (c == '\\') { | |
315 | ++ unsigned char f; | |
316 | ++ switch ((c = *domainname++)) { | |
317 | ++ case '\\': /* "\\" */ | |
318 | ++ continue; | |
319 | ++ case '0': /* "\ooo" */ | |
320 | ++ case '1': | |
321 | ++ case '2': | |
322 | ++ case '3': | |
323 | ++ d = *domainname++; | |
324 | ++ if (!tmy_char_is_0to7(d)) | |
325 | ++ goto out; | |
326 | ++ e = *domainname++; | |
327 | ++ if (!tmy_char_is_0to7(e)) | |
328 | ++ goto out; | |
329 | ++ f = tmy_str2chr(c, d, e); | |
330 | ++ if (f && (f <= 0x20 || f >= 0x7F)) | |
331 | ++ continue; | |
332 | ++ } | |
333 | ++ goto out; | |
334 | ++ } else if (c < 0x20 || c >= 0x7F) | |
335 | ++ /* 0x20 is a domainname separator. */ | |
336 | ++ goto out; | |
337 | ++ } | |
338 | ++ } while (*domainname); | |
339 | ++ | |
340 | ++ return 1; | |
341 | ++ | |
342 | ++out: ; | |
343 | ++ printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", | |
344 | ++ function, org_domainname); | |
345 | ++ return 0; | |
346 | ++} | |
347 | ++ | |
348 | ++/** | |
349 | ++ * tmy_path_depth - evaluate the number of '/' characters in a token. | |
350 | ++ * @pathname: the token to evaluate. | |
351 | ++ * | |
352 | ++ * Each '/' character but the trailing '/' scores 2. | |
353 | ++ * The trailing '/' scores 1. | |
354 | ++ * | |
355 | ++ * If @pathname ends with '/', the return value is an odd integer. | |
356 | ++ * If @pathname does not end with '/', the return value is an even integer. | |
357 | ++ */ | |
358 | ++static int tmy_path_depth(const char *pathname) | |
359 | ++{ | |
360 | ++ int i = 0; | |
361 | ++ | |
362 | ++ if (pathname) { | |
363 | ++ char *ep = strchr(pathname, '\0'); | |
364 | ++ | |
365 | ++ if (pathname < ep--) { | |
366 | ++ if (*ep != '/') | |
367 | ++ i++; | |
368 | ++ while (pathname <= ep) | |
369 | ++ if (*ep-- == '/') | |
370 | ++ i += 2; | |
371 | ++ } | |
372 | ++ } | |
373 | ++ | |
374 | ++ return i; | |
375 | ++} | |
376 | ++ | |
377 | ++/** | |
378 | ++ * tmy_const_part_length - calculate the constant part in a token. | |
379 | ++ * @filename: the token to calculate. | |
380 | ++ * | |
381 | ++ * Returns leading length of @filename that can be compared using strncmp(). | |
382 | ++ */ | |
383 | ++static int tmy_const_part_length(const char *filename) | |
384 | ++{ | |
385 | ++ int len = 0; | |
386 | ++ | |
387 | ++ if (filename) { | |
388 | ++ char c; | |
389 | ++ | |
390 | ++ while ((c = *filename++) != '\0') { | |
391 | ++ if (c != '\\') { | |
392 | ++ len++; | |
393 | ++ continue; | |
394 | ++ } | |
395 | ++ switch (c = *filename++) { | |
396 | ++ case '\\': /* "\\" */ | |
397 | ++ len += 2; | |
398 | ++ continue; | |
399 | ++ case '0': /* "\ooo" */ | |
400 | ++ case '1': | |
401 | ++ case '2': | |
402 | ++ case '3': | |
403 | ++ if (!tmy_char_is_0to7(*filename++)) | |
404 | ++ break; | |
405 | ++ if (!tmy_char_is_0to7(*filename++)) | |
406 | ++ break; | |
407 | ++ len += 4; | |
408 | ++ continue; | |
409 | ++ } | |
410 | ++ break; | |
411 | ++ } | |
412 | ++ } | |
413 | ++ | |
414 | ++ return len; | |
415 | ++} | |
416 | ++ | |
417 | ++/** | |
418 | ++ * tmy_fill_path_info - fill "struct path_info" entry. | |
419 | ++ * @ptr: pointer to "struct path_info". | |
420 | ++ * | |
421 | ++ * Caller stores a token in "struct path_info"->name . | |
422 | ++ * This function will fill rest of "struct path_info" members. | |
423 | ++ */ | |
424 | ++void tmy_fill_path_info(struct path_info *ptr) | |
425 | ++{ | |
426 | ++ const char *name = ptr->name; | |
427 | ++ const int len = strlen(name); | |
428 | ++ ptr->total_len = len; | |
429 | ++ ptr->const_len = tmy_const_part_length(name); | |
430 | ++ ptr->is_dir = len && (name[len - 1] == '/'); | |
431 | ++ ptr->is_patterned = (ptr->const_len < len); | |
432 | ++ ptr->hash = full_name_hash(name, len); | |
433 | ++ ptr->depth = tmy_path_depth(name); | |
434 | ++} | |
435 | ++ | |
436 | ++/** | |
437 | ++ * tmy_file_match2 - internal function for tmy_path_match(). | |
438 | ++ * @filename: start address of the token. | |
439 | ++ * @filename_end: end address of the token. | |
440 | ++ * @pattern: start address of the pattern. | |
441 | ++ * @pattern_end: end address of the pattern. | |
442 | ++ * | |
443 | ++ * Handle all patterns other than '\-' pattern. | |
444 | ++ * Returns true if matches. | |
445 | ++ * Returns false otherwise. | |
446 | ++ * | |
447 | ++ * @filename and @pattern do not contain '/'. | |
448 | ++ * @filename and @pattern are not '\0'-terminated. | |
449 | ++ * @pattern does not contain '\-'. | |
450 | ++ */ | |
451 | ++static bool tmy_file_match2(const char *filename, | |
452 | ++ const char *filename_end, | |
453 | ++ const char *pattern, | |
454 | ++ const char *pattern_end) | |
455 | ++{ | |
456 | ++ while (filename < filename_end && pattern < pattern_end) { | |
457 | ++ char c; | |
458 | ++ int i; | |
459 | ++ int j; | |
460 | ++ | |
461 | ++ if (*pattern != '\\') { | |
462 | ++ if (*filename++ != *pattern++) | |
463 | ++ goto out; | |
464 | ++ continue; | |
465 | ++ } | |
466 | ++ | |
467 | ++ c = *filename; | |
468 | ++ pattern++; | |
469 | ++ | |
470 | ++ switch (*pattern) { | |
471 | ++ case '?': | |
472 | ++ if (c == '/') | |
473 | ++ goto out; | |
474 | ++ if (c != '\\') | |
475 | ++ break; | |
476 | ++ /* filename[0] != '\0' */ | |
477 | ++ c = filename[1]; | |
478 | ++ if (c == '\\') | |
479 | ++ filename++; | |
480 | ++ else if (tmy_char_is_0to3(c) && | |
481 | ++ /* filename[1] != '\0' */ | |
482 | ++ tmy_char_is_0to7(filename[2]) && | |
483 | ++ /* filename[2] != '\0' */ | |
484 | ++ tmy_char_is_0to7(filename[3])) | |
485 | ++ /* filename[3] != '\0' */ | |
486 | ++ filename += 3; | |
487 | ++ /* | |
488 | ++ * Why not "filename += 4;" here | |
489 | ++ * because it processed 4 (i.e. "\ooo") bytes? | |
490 | ++ * - Because "filename++;" is done | |
491 | ++ * after "break;". | |
492 | ++ */ | |
493 | ++ else | |
494 | ++ goto out; | |
495 | ++ break; | |
496 | ++ case '\\': | |
497 | ++ if (c != '\\') | |
498 | ++ goto out; | |
499 | ++ /* filename[0] != '\0' */ | |
500 | ++ if (*++filename != '\\') | |
501 | ++ goto out; | |
502 | ++ break; | |
503 | ++ case '+': | |
504 | ++ if (!tmy_char_is_0to9(c)) | |
505 | ++ goto out; | |
506 | ++ break; | |
507 | ++ case 'x': | |
508 | ++ if (!tmy_char_is_hex(c)) | |
509 | ++ goto out; | |
510 | ++ break; | |
511 | ++ case 'a': | |
512 | ++ if (!tmy_char_is_alpha(c)) | |
513 | ++ goto out; | |
514 | ++ break; | |
515 | ++ case '0': | |
516 | ++ case '1': | |
517 | ++ case '2': | |
518 | ++ case '3': | |
519 | ++ if (c != '\\') | |
520 | ++ goto out; | |
521 | ++ /* filename[0] != '\0' */ | |
522 | ++ c = filename[1]; | |
523 | ++ if (!tmy_char_is_0to3(c) || c != *pattern) | |
524 | ++ goto out; | |
525 | ++ /* filename[1] != '\0' */ | |
526 | ++ c = filename[2]; | |
527 | ++ /* pattern[0] != '\0' */ | |
528 | ++ if (!tmy_char_is_0to7(c) || c != pattern[1]) | |
529 | ++ goto out; | |
530 | ++ /* filename[2] != '\0' */ | |
531 | ++ c = filename[3]; | |
532 | ++ /* pattern[1] != '\0' */ | |
533 | ++ if (!tmy_char_is_0to7(c) || c != pattern[2]) | |
534 | ++ goto out; | |
535 | ++ /* filename[3] != '\0' */ | |
536 | ++ filename += 3; | |
537 | ++ /* pattern[2] != '\0' */ | |
538 | ++ pattern += 2; | |
539 | ++ break; | |
540 | ++ case '*': | |
541 | ++ case '@': | |
542 | ++ for (i = 0; i <= filename_end - filename; i++) { | |
543 | ++ if (tmy_file_match2(filename + i, | |
544 | ++ filename_end, | |
545 | ++ pattern + 1, | |
546 | ++ pattern_end)) | |
547 | ++ return 1; | |
548 | ++ c = filename[i]; | |
549 | ++ if (c == '.' && *pattern == '@') | |
550 | ++ break; | |
551 | ++ if (c != '\\') | |
552 | ++ continue; | |
553 | ++ /* filename[i] != '\0' */ | |
554 | ++ c = filename[i + 1]; | |
555 | ++ if (c == '\\') | |
556 | ++ /* filename[i + 1] != '\0' */ | |
557 | ++ i++; | |
558 | ++ else if (tmy_char_is_0to3(c) && | |
559 | ++ /* filename[i + 1] != '\0' */ | |
560 | ++ tmy_char_is_0to7(filename[i + 2]) && | |
561 | ++ /* filename[i + 2] != '\0' */ | |
562 | ++ tmy_char_is_0to7(filename[i + 3])) | |
563 | ++ /* filename[i + 3] != '\0' */ | |
564 | ++ i += 3; | |
565 | ++ else | |
566 | ++ break; /* Bad pattern. */ | |
567 | ++ } | |
568 | ++ goto out; | |
569 | ++ default: | |
570 | ++ j = 0; | |
571 | ++ /* | |
572 | ++ * If j == 0, for() loop does nothing. | |
573 | ++ * So I can use while() without checking first letter. | |
574 | ++ */ | |
575 | ++ c = *pattern; | |
576 | ++ if (c == '$') | |
577 | ++ while (tmy_char_is_0to9(filename[j])) | |
578 | ++ j++; | |
579 | ++ else if (c == 'X') | |
580 | ++ while (tmy_char_is_hex(filename[j])) | |
581 | ++ j++; | |
582 | ++ else if (c == 'A') | |
583 | ++ while (tmy_char_is_alpha(filename[j])) | |
584 | ++ j++; | |
585 | ++ for (i = 1; i <= j; i++) | |
586 | ++ if (tmy_file_match2(filename + i, | |
587 | ++ filename_end, | |
588 | ++ pattern + 1, | |
589 | ++ pattern_end)) | |
590 | ++ return 1; | |
591 | ++ goto out; /* Not matched or bad pattern. */ | |
592 | ++ } | |
593 | ++ filename++; /* filename[0] != '\0' */ | |
594 | ++ pattern++; /* pattern[0] != '\0' */ | |
595 | ++ } | |
596 | ++ | |
597 | ++ /* Skip trailing "\*" and "\@" patterns. */ | |
598 | ++ while (*pattern == '\\' && | |
599 | ++ (*(pattern + 1) == '*' || | |
600 | ++ *(pattern + 1) == '@')) | |
601 | ++ pattern += 2; | |
602 | ++ | |
603 | ++ /* If both are at ending position, they are equals. */ | |
604 | ++ return (filename == filename_end && pattern == pattern_end); | |
605 | ++out: ; | |
606 | ++ return 0; | |
607 | ++} | |
608 | ++ | |
609 | ++/** | |
610 | ++ * tmy_file_match - internal function for tmy_path_match(). | |
611 | ++ * @filename: start address of the token. | |
612 | ++ * @filename_end: end address of the token. | |
613 | ++ * @pattern: start address of the pattern. | |
614 | ++ * @pattern_end: end address of the pattern. | |
615 | ++ * | |
616 | ++ * Handle patterns containing '\-' pattern. | |
617 | ++ * Returns true if matches. | |
618 | ++ * Returns false otherwise. | |
619 | ++ * | |
620 | ++ * @filename and @pattern do not contain '/'. | |
621 | ++ * @filename and @pattern are not '\0'-terminated. | |
622 | ++ */ | |
623 | ++static bool tmy_file_match(const char *filename, | |
624 | ++ const char *filename_end, | |
625 | ++ const char *pattern, | |
626 | ++ const char *pattern_end) | |
627 | ++{ | |
628 | ++ const char *pattern_start = pattern; | |
629 | ++ bool first = 1; | |
630 | ++ bool result; | |
631 | ++ | |
632 | ++ while (pattern < pattern_end - 1) { | |
633 | ++ /* Split at "\-" pattern. */ | |
634 | ++ if (*pattern++ != '\\' || *pattern++ != '-') | |
635 | ++ continue; | |
636 | ++ result = tmy_file_match2(filename, | |
637 | ++ filename_end, | |
638 | ++ pattern_start, | |
639 | ++ pattern - 2); | |
640 | ++ /* If before "\-" pattern, invert the result. */ | |
641 | ++ if (first) | |
642 | ++ result = !result; | |
643 | ++ /* | |
644 | ++ * If not matched before first "\-" pattern, return 0. | |
645 | ++ * If matched after first "\-" pattern, return 0. | |
646 | ++ */ | |
647 | ++ if (result) | |
648 | ++ return 0; | |
649 | ++ first = 0; | |
650 | ++ pattern_start = pattern; | |
651 | ++ } | |
652 | ++ | |
653 | ++ result = tmy_file_match2(filename, | |
654 | ++ filename_end, pattern_start, pattern_end); | |
655 | ++ /* If after first "\-" pattern, invert the result. */ | |
656 | ++ return first ? result : !result; | |
657 | ++} | |
658 | ++ | |
659 | ++/** | |
660 | ++ * tmy_path_match - do a pattern matching. | |
661 | ++ * @pathname0: pointer to a token to compare. | |
662 | ++ * @pattern0: pointer to a pattern to compare. | |
663 | ++ * | |
664 | ++ * Returns true if @pathname0 matches to @pattern0 . | |
665 | ++ * Returns false otherwise. | |
666 | ++ * | |
667 | ++ * | |
668 | ++ * Check whether the given token matches to the given pattern. | |
669 | ++ * | |
670 | ++ * The following patterns are available. | |
671 | ++ * \\ \ itself. | |
672 | ++ * \ooo Octal representation of a byte. | |
673 | ++ * \* More than or equals to 0 character other than '/'. | |
674 | ++ * \@ More than or equals to 0 character other than '/' or '.'. | |
675 | ++ * \? 1 byte character other than '/'. | |
676 | ++ * \$ More than or equals to 1 decimal digit. | |
677 | ++ * \+ 1 decimal digit. | |
678 | ++ * \X More than or equals to 1 hexadecimal digit. | |
679 | ++ * \x 1 hexadecimal digit. | |
680 | ++ * \A More than or equals to 1 alphabet character. | |
681 | ++ * \a 1 alphabet character. | |
682 | ++ * \- Subtraction operator. | |
683 | ++ */ | |
684 | ++bool tmy_path_match(const struct path_info *pathname0, | |
685 | ++ const struct path_info *pattern0) | |
686 | ++{ | |
687 | ++ const char *pathname = pathname0->name; | |
688 | ++ const char *pattern = pattern0->name; | |
689 | ++ const int len = pattern0->const_len; | |
690 | ++ | |
691 | ++ /* If it doesn't contains patterns, I can use tmy_pathcmp() */ | |
692 | ++ if (!pattern0->is_patterned) | |
693 | ++ return !tmy_pathcmp(pathname0, pattern0); | |
694 | ++ /* Check the depth of directory. */ | |
695 | ++ if (pathname0->depth != pattern0->depth) | |
696 | ++ return 0; | |
697 | ++ /* Use strncmp() for constant part. */ | |
698 | ++ if (strncmp(pathname, pattern, len)) | |
699 | ++ return 0; | |
700 | ++ | |
701 | ++ pathname += len; | |
702 | ++ pattern += len; | |
703 | ++ | |
704 | ++ /* Split at '/' character, and compare. */ | |
705 | ++ while (*pathname && *pattern) { | |
706 | ++ const char *pathname_delimiter = strchr(pathname, '/'); | |
707 | ++ const char *pattern_delimiter = strchr(pattern, '/'); | |
708 | ++ | |
709 | ++ if (!pathname_delimiter) | |
710 | ++ pathname_delimiter = strchr(pathname, '\0'); | |
711 | ++ if (!pattern_delimiter) | |
712 | ++ pattern_delimiter = strchr(pattern, '\0'); | |
713 | ++ if (!tmy_file_match(pathname, | |
714 | ++ pathname_delimiter, | |
715 | ++ pattern, | |
716 | ++ pattern_delimiter)) | |
717 | ++ return 0; | |
718 | ++ | |
719 | ++ pathname = *pathname_delimiter ? | |
720 | ++ pathname_delimiter + 1 : | |
721 | ++ pathname_delimiter; | |
722 | ++ pattern = *pattern_delimiter ? | |
723 | ++ pattern_delimiter + 1 : | |
724 | ++ pattern_delimiter; | |
725 | ++ } | |
726 | ++ | |
727 | ++ /* Skip trailing "\*" and "\@" patterns. */ | |
728 | ++ while (*pattern == '\\' && | |
729 | ++ (*(pattern + 1) == '*' || | |
730 | ++ *(pattern + 1) == '@')) | |
731 | ++ pattern += 2; | |
732 | ++ | |
733 | ++ /* If both are at '\0' position, they are equals. */ | |
734 | ++ return (!*pathname && !*pattern); | |
735 | ++} | |
736 | ++ | |
737 | ++/** | |
738 | ++ * tmy_io_printf - transactional printf() for "struct io_buffer". | |
739 | ++ * @head: pointer to "struct io_buffer". | |
740 | ++ * @fmt: format strings for printf(). | |
741 | ++ * | |
742 | ++ * Returns zero on success. | |
743 | ++ * Returns nonzero otherwise. | |
744 | ++ * | |
745 | ++ * Transactional printf() to "struct io_buffer" structure. | |
746 | ++ * snprintf() will truncate, but tmy_io_printf() won't. | |
747 | ++ * This is needed because dumping partially truncated tokens | |
748 | ++ * is not acceptable for TOMOYO Linux. | |
749 | ++ */ | |
750 | ++int tmy_io_printf(struct io_buffer *head, const char *fmt, ...) | |
751 | ++{ | |
752 | ++ va_list args; | |
753 | ++ int len; | |
754 | ++ int pos = head->read_avail; | |
755 | ++ int size = head->readbuf_size - pos; | |
756 | ++ | |
757 | ++ if (size <= 0) | |
758 | ++ return -ENOMEM; | |
759 | ++ | |
760 | ++ va_start(args, fmt); | |
761 | ++ len = vsnprintf(head->read_buf + pos, size, fmt, args); | |
762 | ++ va_end(args); | |
763 | ++ | |
764 | ++ if (pos + len >= head->readbuf_size) | |
765 | ++ return -ENOMEM; | |
766 | ++ | |
767 | ++ head->read_avail += len; | |
768 | ++ | |
769 | ++ return 0; | |
770 | ++} | |
771 | ++ | |
772 | ++/** | |
773 | ++ * tmy_get_exe - get realpath of current process. | |
774 | ++ * | |
775 | ++ * Returns realpath(3) of current process on success. | |
776 | ++ * Returns NULL on failure. | |
777 | ++ * | |
778 | ++ * This function uses tmy_alloc(), so caller must tmy_free() | |
779 | ++ * if this function didn't return NULL. | |
780 | ++ */ | |
781 | ++const char *tmy_get_exe(void) | |
782 | ++{ | |
783 | ++ struct vm_area_struct *vma; | |
784 | ++ | |
785 | ++ if (!current->mm) | |
786 | ++ return NULL; | |
787 | ++ for (vma = current->mm->mmap; vma; vma = vma->vm_next) | |
788 | ++ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) | |
789 | ++ return tmy_realpath_dentry(vma->vm_file->f_dentry, | |
790 | ++ vma->vm_file->f_vfsmnt); | |
791 | ++ | |
792 | ++ return NULL; | |
793 | ++} | |
794 | ++ | |
795 | ++/** | |
796 | ++ * tmy_lastname - get last part of domainname. | |
797 | ++ * | |
798 | ++ * Returns last part of domainname. | |
799 | ++ */ | |
800 | ++const char *tmy_lastname(const struct domain_info *domain) | |
801 | ++{ | |
802 | ++ const char *cp0 = domain->domainname->name; | |
803 | ++ const char *cp1 = strrchr(cp0, ' '); | |
804 | ++ if (cp1) | |
805 | ++ return cp1 + 1; | |
806 | ++ return cp0; | |
807 | ++} | |
808 | ++ | |
809 | ++/** | |
810 | ++ * tmy_get_msg - get message from mode. | |
811 | ++ * @is_enforce: enforcing flag. | |
812 | ++ * | |
813 | ++ * Returns "ERROR" if enforcing, "WARNING" otherwise. | |
814 | ++ */ | |
815 | ++const char *tmy_getmsg(bool is_enforce) | |
816 | ++{ | |
817 | ++ return is_enforce ? "ERROR" : "WARNING"; | |
818 | ++} | |
819 | ++ | |
820 | ++/************************* DOMAIN POLICY HANDLER *************************/ | |
821 | ++ | |
822 | ++/** | |
823 | ++ * tmy_flags - get flags of given access control. | |
824 | ++ * @index: index to retrieve flags. | |
825 | ++ * | |
826 | ++ * Returns current value of given access control. | |
827 | ++ */ | |
828 | ++unsigned int tmy_flags(const unsigned int index) | |
829 | ++{ | |
830 | ++ const u8 profile = | |
831 | ++ TMY_SECURITY->domain->profile; | |
832 | ++ | |
833 | ++ /* All operations might sleep. See tmy_supervisor(). */ | |
834 | ++ might_sleep(); | |
835 | ++ if (in_interrupt()) | |
836 | ++ return 0; | |
837 | ++ return sbin_init_started && index < TMY_MAX_CONTROL_INDEX | |
838 | ++ && profile_ptr[profile] ? | |
839 | ++ profile_ptr[profile]->value[index] : | |
840 | ++ 0; | |
841 | ++} | |
842 | ++ | |
843 | ++/** | |
844 | ++ * tmy_quota - check quota. | |
845 | ++ * | |
846 | ++ * Returns true if not quota has not exceeded. | |
847 | ++ * Returns false otherwise. | |
848 | ++ * | |
849 | ++ * This is a safeguard for "learning mode", for appending entries | |
850 | ++ * without limit dulls the system response and consumes much memory. | |
851 | ++ */ | |
852 | ++bool tmy_quota(void) | |
853 | ++{ | |
854 | ++ unsigned int count = 0; | |
855 | ++ struct acl_info *ptr; | |
856 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
857 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
858 | ++ if (!ptr->is_deleted) | |
859 | ++ count++; | |
860 | ++ } | |
861 | ++ /* If there are so many entries, don't append any more. */ | |
862 | ++ if (count < tmy_flags(TMY_MAX_ACCEPT_ENTRY)) | |
863 | ++ return 1; | |
864 | ++ if (!domain->quota_warned) { | |
865 | ++ domain->quota_warned = 1; | |
866 | ++ printk(KERN_INFO | |
867 | ++ "TOMOYO-WARNING: Domain '%s' has so many ACLs " | |
868 | ++ "to hold. Stopped learning mode.\n", | |
869 | ++ domain->domainname->name); | |
870 | ++ } | |
871 | ++ return 0; | |
872 | ++} | |
873 | ++ | |
874 | ++/* Create a new profile. */ | |
875 | ++static struct profile *tmy_find_new_profile(const unsigned int profile) | |
876 | ++{ | |
877 | ++ static DEFINE_MUTEX(profile_lock); | |
878 | ++ struct profile *ptr = NULL; | |
879 | ++ | |
880 | ++ mutex_lock(&profile_lock); | |
881 | ++ | |
882 | ++ ptr = profile_ptr[profile]; | |
883 | ++ if (profile < TMY_MAX_PROFILES && ptr == NULL) { | |
884 | ++ ptr = tmy_alloc_element(sizeof(*ptr)); | |
885 | ++ if (ptr != NULL) { | |
886 | ++ int i; | |
887 | ++ for (i = 0; i < TMY_MAX_CONTROL_INDEX; i++) | |
888 | ++ ptr->value[i] = tmy_control[i].current_value; | |
889 | ++ mb(); /* Instead of using spinlock. */ | |
890 | ++ profile_ptr[profile] = ptr; | |
891 | ++ } | |
892 | ++ } | |
893 | ++ | |
894 | ++ mutex_unlock(&profile_lock); | |
895 | ++ | |
896 | ++ return ptr; | |
897 | ++} | |
898 | ++ | |
899 | ++/* Loading policy done? */ | |
900 | ++static int profile_loaded; | |
901 | ++ | |
902 | ++/* Update profile values. */ | |
903 | ++static int tmy_set_profile(struct io_buffer *head) | |
904 | ++{ | |
905 | ++ char *data = head->write_buf; | |
906 | ++ unsigned int i; | |
907 | ++ unsigned int value; | |
908 | ++ char *cp; | |
909 | ++ struct profile *profile; | |
910 | ++ | |
911 | ++ if (!tmy_is_root()) | |
912 | ++ return -EPERM; | |
913 | ++ | |
914 | ++ /* If profile index is not given, assume 0. */ | |
915 | ++ i = simple_strtoul(data, &cp, 10); | |
916 | ++ if (data != cp) { | |
917 | ++ if (*cp != '-') | |
918 | ++ return -EINVAL; | |
919 | ++ data = cp + 1; | |
920 | ++ } | |
921 | ++ | |
922 | ++ profile = tmy_find_new_profile(i); | |
923 | ++ if (!profile) | |
924 | ++ return -EINVAL; | |
925 | ++ cp = strchr(data, '='); | |
926 | ++ if (!cp) | |
927 | ++ return -EINVAL; | |
928 | ++ | |
929 | ++ *cp = '\0'; | |
930 | ++ profile_loaded = 1; | |
931 | ++ tmy_update_counter(TMY_UPDATE_PROFILE); | |
932 | ++ if (strcmp(data, tmy_control[TMY_COMMENT].keyword) == 0) { | |
933 | ++ profile->comment = tmy_save_name(cp + 1); | |
934 | ++ return 0; | |
935 | ++ } | |
936 | ++ | |
937 | ++ if (sscanf(cp + 1, "%u", &value) != 1) | |
938 | ++ return -EINVAL; | |
939 | ++ | |
940 | ++ if (tmy_strstarts(&data, TMY_MAC_FOR_CAPABILITY)) | |
941 | ++ return tmy_set_capability_profile(data, value, i); | |
942 | ++ | |
943 | ++ for (i = 0; i < TMY_MAX_CONTROL_INDEX; i++) { | |
944 | ++ if (strcmp(data, tmy_control[i].keyword)) | |
945 | ++ continue; | |
946 | ++ if (value > tmy_control[i].max_value) | |
947 | ++ value = tmy_control[i].max_value; | |
948 | ++ profile->value[i] = value; | |
949 | ++ return 0; | |
950 | ++ } | |
951 | ++ | |
952 | ++ return -EINVAL; | |
953 | ++} | |
954 | ++ | |
955 | ++/* Read profile values. */ | |
956 | ++static int tmy_read_profile(struct io_buffer *head) | |
957 | ++{ | |
958 | ++ int step; | |
959 | ++ | |
960 | ++ if (head->read_eof) | |
961 | ++ return 0; | |
962 | ++ if (!tmy_is_root()) | |
963 | ++ return -EPERM; | |
964 | ++ | |
965 | ++ if (head->read_var1) | |
966 | ++ goto capability; | |
967 | ++ | |
968 | ++ for (step = head->read_step; | |
969 | ++ step < TMY_MAX_PROFILES * TMY_MAX_CONTROL_INDEX; | |
970 | ++ step++) { | |
971 | ++ const int i = step / TMY_MAX_CONTROL_INDEX; | |
972 | ++ const int j = step % TMY_MAX_CONTROL_INDEX; | |
973 | ++ const struct profile *profile = profile_ptr[i]; | |
974 | ++ | |
975 | ++ head->read_step = step; | |
976 | ++ if (!profile) | |
977 | ++ continue; | |
978 | ++ if (j != TMY_COMMENT) | |
979 | ++ goto non_comment; | |
980 | ++ if (tmy_io_printf(head, "%u-%s=%s\n", | |
981 | ++ i, tmy_control[TMY_COMMENT].keyword, | |
982 | ++ profile->comment ? | |
983 | ++ profile->comment->name : "")) | |
984 | ++ break; | |
985 | ++ goto comment_ok; | |
986 | ++non_comment: ; | |
987 | ++ if (tmy_io_printf(head, "%u-%s=%u\n", | |
988 | ++ i, tmy_control[j].keyword, | |
989 | ++ profile->value[j])) | |
990 | ++ break; | |
991 | ++comment_ok: ; | |
992 | ++ } | |
993 | ++ | |
994 | ++ if (step == TMY_MAX_PROFILES * TMY_MAX_CONTROL_INDEX) { | |
995 | ++ head->read_var1 = (void *) ""; | |
996 | ++ head->read_step = 0; | |
997 | ++ goto capability; | |
998 | ++ } | |
999 | ++ return 0; | |
1000 | ++capability: ; | |
1001 | ++ if (tmy_read_capability_profile(head) == 0) | |
1002 | ++ head->read_eof = 1; | |
1003 | ++ return 0; | |
1004 | ++} | |
1005 | ++ | |
1006 | ++/************************* POLICY MANAGER HANDLER *************************/ | |
1007 | ++ | |
1008 | ++struct policy_manager_entry { | |
1009 | ++ struct list_head list; | |
1010 | ++ const struct path_info *manager; | |
1011 | ++ bool is_domain; | |
1012 | ++ bool is_deleted; | |
1013 | ++}; | |
1014 | ++ | |
1015 | ++static LIST_HEAD(policy_manager_list); | |
1016 | ++ | |
1017 | ++/* Update manager list. */ | |
1018 | ++static int tmy_add_manager_entry(const char *manager, const bool is_delete) | |
1019 | ++{ | |
1020 | ++ struct policy_manager_entry *new_entry; | |
1021 | ++ struct policy_manager_entry *ptr; | |
1022 | ++ static DEFINE_MUTEX(mutex); | |
1023 | ++ const struct path_info *saved_manager; | |
1024 | ++ int error = -ENOMEM; | |
1025 | ++ | |
1026 | ++ bool is_domain = 0; | |
1027 | ++ if (!tmy_is_root()) | |
1028 | ++ return -EPERM; | |
1029 | ++ if (tmy_is_domain_def(manager)) { | |
1030 | ++ if (!tmy_is_correct_domain(manager, __FUNCTION__)) | |
1031 | ++ return -EINVAL; | |
1032 | ++ is_domain = 1; | |
1033 | ++ } else { | |
1034 | ++ if (!tmy_correct_path(manager, 1, -1, -1, __FUNCTION__)) | |
1035 | ++ return -EINVAL; | |
1036 | ++ } | |
1037 | ++ | |
1038 | ++ saved_manager = tmy_save_name(manager); | |
1039 | ++ if (saved_manager == NULL) | |
1040 | ++ return -ENOMEM; | |
1041 | ++ | |
1042 | ++ mutex_lock(&mutex); | |
1043 | ++ | |
1044 | ++ list_for_each_entry(ptr, &policy_manager_list, list) { | |
1045 | ++ if (ptr->manager == saved_manager) { | |
1046 | ++ ptr->is_deleted = is_delete; | |
1047 | ++ error = 0; | |
1048 | ++ goto out; | |
1049 | ++ } | |
1050 | ++ } | |
1051 | ++ | |
1052 | ++ if (is_delete) { | |
1053 | ++ error = -ENOENT; | |
1054 | ++ goto out; | |
1055 | ++ } | |
1056 | ++ | |
1057 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
1058 | ++ if (new_entry == NULL) | |
1059 | ++ goto out; | |
1060 | ++ new_entry->manager = saved_manager; | |
1061 | ++ new_entry->is_domain = is_domain; | |
1062 | ++ list_add_tail_mb(&new_entry->list, &policy_manager_list); | |
1063 | ++ error = 0; | |
1064 | ++out: ; | |
1065 | ++ | |
1066 | ++ mutex_unlock(&mutex); | |
1067 | ++ | |
1068 | ++ if (!error) | |
1069 | ++ tmy_update_counter(TMY_UPDATE_MANAGER); | |
1070 | ++ return error; | |
1071 | ++} | |
1072 | ++ | |
1073 | ++/* Update manager list. */ | |
1074 | ++static int tmy_add_manager_policy(struct io_buffer *head) | |
1075 | ++{ | |
1076 | ++ char *data = head->write_buf; | |
1077 | ++ bool is_delete = 0; | |
1078 | ++ | |
1079 | ++ if (!tmy_is_root()) | |
1080 | ++ return -EPERM; | |
1081 | ++ if (tmy_strstarts(&data, TMY_DELETE)) | |
1082 | ++ is_delete = 1; | |
1083 | ++ return tmy_add_manager_entry(data, is_delete); | |
1084 | ++} | |
1085 | ++ | |
1086 | ++/* Read manager list.*/ | |
1087 | ++static int tmy_read_manager_policy(struct io_buffer *head) | |
1088 | ++{ | |
1089 | ++ struct list_head *pos; | |
1090 | ++ if (head->read_eof) | |
1091 | ++ return 0; | |
1092 | ++ if (!tmy_is_root()) | |
1093 | ++ return -EPERM; | |
1094 | ++ list_for_each_cookie(pos, head->read_var2, &policy_manager_list) { | |
1095 | ++ struct policy_manager_entry *ptr; | |
1096 | ++ ptr = list_entry(pos, struct policy_manager_entry, list); | |
1097 | ++ if (!ptr->is_deleted && | |
1098 | ++ tmy_io_printf(head, "%s\n", ptr->manager->name)) | |
1099 | ++ break; | |
1100 | ++ } | |
1101 | ++ if (!head->read_var2) | |
1102 | ++ head->read_eof = 1; | |
1103 | ++ return 0; | |
1104 | ++} | |
1105 | ++ | |
1106 | ++/** | |
1107 | ++ * tmy_is_policy_manager - check whether modifying policy is permitted. | |
1108 | ++ * | |
1109 | ++ * Returns nonzero if modifying policy is permitted. | |
1110 | ++ * Returns zero otherwise. | |
1111 | ++ * | |
1112 | ++ * Only programs or domains registers as manager are permitted to modify | |
1113 | ++ * policy via /sys/kernel/security/tomoyo/ interface. | |
1114 | ++ * This function checks whether the current process is a policy manager. | |
1115 | ++ * But policy manager is not the only processes that can modify policy; | |
1116 | ++ * other process are permitted to add policy | |
1117 | ++ * if their domains are assigned a profile for learning mode. | |
1118 | ++ */ | |
1119 | ++static int tmy_is_policy_manager(void) | |
1120 | ++{ | |
1121 | ++ struct policy_manager_entry *ptr; | |
1122 | ++ const char *exe; | |
1123 | ++ const struct path_info *domainname = | |
1124 | ++ TMY_SECURITY->domain->domainname; | |
1125 | ++ bool found = 0; | |
1126 | ++ | |
1127 | ++ /* Everyone can modify policy before /sbin/init starts. */ | |
1128 | ++ if (!sbin_init_started) | |
1129 | ++ return 1; | |
1130 | ++ | |
1131 | ++ list_for_each_entry(ptr, &policy_manager_list, list) { | |
1132 | ++ if (!ptr->is_deleted && | |
1133 | ++ ptr->is_domain && | |
1134 | ++ !tmy_pathcmp(domainname, ptr->manager)) | |
1135 | ++ return 1; | |
1136 | ++ } | |
1137 | ++ | |
1138 | ++ exe = tmy_get_exe(); | |
1139 | ++ if (!exe) | |
1140 | ++ return 0; | |
1141 | ++ | |
1142 | ++ list_for_each_entry(ptr, &policy_manager_list, list) { | |
1143 | ++ if (!ptr->is_deleted && | |
1144 | ++ !ptr->is_domain && | |
1145 | ++ !strcmp(exe, ptr->manager->name)) { | |
1146 | ++ found = 1; | |
1147 | ++ break; | |
1148 | ++ } | |
1149 | ++ } | |
1150 | ++ | |
1151 | ++ if (!found) { /* Reduce error messages. */ | |
1152 | ++ static pid_t last_pid; | |
1153 | ++ const pid_t pid = current->pid; | |
1154 | ++ if (last_pid != pid) { | |
1155 | ++ printk(KERN_INFO | |
1156 | ++ "%s is not permitted to update policies.\n", | |
1157 | ++ exe); | |
1158 | ++ last_pid = pid; | |
1159 | ++ } | |
1160 | ++ } | |
1161 | ++ | |
1162 | ++ tmy_free(exe); | |
1163 | ++ return found; | |
1164 | ++} | |
1165 | ++ | |
1166 | ++/************************* DOMAIN POLICY HANDLER *************************/ | |
1167 | ++ | |
1168 | ++/* Update domain policy. */ | |
1169 | ++static int tmy_add_domain_policy(struct io_buffer *head) | |
1170 | ++{ | |
1171 | ++ char *data = head->write_buf; | |
1172 | ++ char *cp; | |
1173 | ++ const struct condition_list *cond = NULL; | |
1174 | ++ struct domain_info *domain = head->write_var1; | |
1175 | ++ bool is_delete = 0; | |
1176 | ++ bool is_select = 0; | |
1177 | ++ bool is_undelete = 0; | |
1178 | ++ unsigned int profile; | |
1179 | ++ | |
1180 | ++ if (!tmy_is_root()) | |
1181 | ++ return -EPERM; | |
1182 | ++ | |
1183 | ++ if (tmy_strstarts(&data, TMY_DELETE)) | |
1184 | ++ is_delete = 1; | |
1185 | ++ else if (tmy_strstarts(&data, TMY_SELECT)) | |
1186 | ++ is_select = 1; | |
1187 | ++ else if (tmy_strstarts(&data, TMY_UNDELETE)) | |
1188 | ++ is_undelete = 1; | |
1189 | ++ | |
1190 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
1191 | ++ | |
1192 | ++ if (tmy_is_domain_def(data)) { | |
1193 | ++ if (is_delete) { | |
1194 | ++ tmy_delete_domain(data); | |
1195 | ++ domain = NULL; | |
1196 | ++ } else if (is_select) | |
1197 | ++ domain = tmy_find_domain(data); | |
1198 | ++ else if (is_undelete) | |
1199 | ++ domain = tmy_undelete_domain(data); | |
1200 | ++ else | |
1201 | ++ domain = tmy_new_domain(data, 0); | |
1202 | ++ head->write_var1 = domain; | |
1203 | ++ return 0; | |
1204 | ++ } | |
1205 | ++ | |
1206 | ++ if (!domain) | |
1207 | ++ return -EINVAL; | |
1208 | ++ | |
1209 | ++ if (sscanf(data, TMY_USE_PROFILE "%u", &profile) == 1 && | |
1210 | ++ profile < TMY_MAX_PROFILES) { | |
1211 | ++ if (profile_ptr[profile] || !sbin_init_started) | |
1212 | ++ domain->profile = (u8) profile; | |
1213 | ++ return 0; | |
1214 | ++ } | |
1215 | ++ cp = tmy_find_condition_part(data); | |
1216 | ++ if (cp) { | |
1217 | ++ cond = tmy_assign_condition(cp); | |
1218 | ++ if (!cond) | |
1219 | ++ return -EINVAL; | |
1220 | ++ } | |
1221 | ++ if (tmy_strstarts(&data, TMY_ALLOW_NETWORK)) | |
1222 | ++ return tmy_add_network_policy(data, domain, cond, is_delete); | |
1223 | ++ else if (tmy_strstarts(&data, TMY_ALLOW_ARGV0)) | |
1224 | ++ return tmy_add_argv0_policy(data, domain, cond, is_delete); | |
1225 | ++ else if (tmy_strstarts(&data, TMY_ALLOW_SIGNAL)) | |
1226 | ++ return tmy_add_signal_policy(data, domain, cond, is_delete); | |
1227 | ++ else if (tmy_strstarts(&data, TMY_ALLOW_CAPABILITY)) | |
1228 | ++ return tmy_add_capability_policy(data, domain, cond, is_delete); | |
1229 | ++ else | |
1230 | ++ return tmy_add_file_policy(data, domain, cond, is_delete); | |
1231 | ++ | |
1232 | ++ return -EINVAL; | |
1233 | ++} | |
1234 | ++ | |
1235 | ++/* Dump file's open ACL. */ | |
1236 | ++static inline int print_file_rwx_acl(struct io_buffer *head, | |
1237 | ++ struct acl_info *ptr) | |
1238 | ++{ | |
1239 | ++ struct file_acl *ptr2 = (struct file_acl *) ptr; | |
1240 | ++ const unsigned char b = ptr2->u_is_group; | |
1241 | ++ | |
1242 | ++ if (tmy_io_printf(head, "%d %s%s", | |
1243 | ++ ptr2->perm, b ? "@" : "", | |
1244 | ++ b ? ptr2->u.group->group_name->name : | |
1245 | ++ ptr2->u.filename->name)) | |
1246 | ++ return -ENOMEM; | |
1247 | ++ return 0; | |
1248 | ++} | |
1249 | ++ | |
1250 | ++/* Dump argv[0]'s ACL. */ | |
1251 | ++static inline int print_argv0_acl(struct io_buffer *head, | |
1252 | ++ struct acl_info *ptr) | |
1253 | ++{ | |
1254 | ++ struct argv0_acl *ptr2 = (struct argv0_acl *) ptr; | |
1255 | ++ | |
1256 | ++ if (tmy_io_printf(head, TMY_ALLOW_ARGV0 "%s %s", | |
1257 | ++ ptr2->filename->name, ptr2->argv0->name)) | |
1258 | ++ return -ENOMEM; | |
1259 | ++ return 0; | |
1260 | ++} | |
1261 | ++ | |
1262 | ++/* Dump network's ACL. */ | |
1263 | ++static inline int print_network_acl(struct io_buffer *head, | |
1264 | ++ struct acl_info *ptr) | |
1265 | ++{ | |
1266 | ++ struct net_acl *ptr2 = (struct net_acl *) ptr; | |
1267 | ++ u8 record_type = ptr2->record_type; | |
1268 | ++ u32 min_address; | |
1269 | ++ u32 max_address; | |
1270 | ++ char buf[64]; | |
1271 | ++ const u16 *min_address_ptr; | |
1272 | ++ const u16 *max_address_ptr; | |
1273 | ++ u16 min_port; | |
1274 | ++ u16 max_port; | |
1275 | ++ | |
1276 | ++ if (tmy_io_printf(head, TMY_ALLOW_NETWORK "%s ", | |
1277 | ++ tmy_network2keyword(ptr2->operation_type))) | |
1278 | ++ goto out; | |
1279 | ++ if (record_type != TMY_TYPE_ADDRESS_GROUP) | |
1280 | ++ goto non_address_group; | |
1281 | ++ | |
1282 | ++ if (tmy_io_printf(head, "@%s", ptr2->u.group->group_name->name)) | |
1283 | ++ goto out; | |
1284 | ++ goto print_port; | |
1285 | ++ | |
1286 | ++non_address_group: ; | |
1287 | ++ if (record_type != TMY_TYPE_IPv4) | |
1288 | ++ goto ipv6_address; | |
1289 | ++ | |
1290 | ++ min_address = ptr2->u.ipv4.min; | |
1291 | ++ max_address = ptr2->u.ipv4.max; | |
1292 | ++ if (tmy_io_printf(head, NIPQUAD_FMT, HIPQUAD(min_address))) | |
1293 | ++ goto out; | |
1294 | ++ if (min_address != max_address && | |
1295 | ++ tmy_io_printf(head, "-" NIPQUAD_FMT, HIPQUAD(max_address))) | |
1296 | ++ goto out; | |
1297 | ++ goto print_port; | |
1298 | ++ | |
1299 | ++ipv6_address: ; | |
1300 | ++ min_address_ptr = ptr2->u.ipv6.min; | |
1301 | ++ max_address_ptr = ptr2->u.ipv6.max; | |
1302 | ++ tmy_print_ipv6(buf, sizeof(buf), min_address_ptr); | |
1303 | ++ if (tmy_io_printf(head, "%s", buf)) | |
1304 | ++ goto out; | |
1305 | ++ if (memcmp(min_address_ptr, max_address_ptr, 16)) { | |
1306 | ++ tmy_print_ipv6(buf, sizeof(buf), max_address_ptr); | |
1307 | ++ if (tmy_io_printf(head, "-%s", buf)) | |
1308 | ++ goto out; | |
1309 | ++ } | |
1310 | ++ | |
1311 | ++print_port: ; | |
1312 | ++ min_port = ptr2->min_port; | |
1313 | ++ max_port = ptr2->max_port; | |
1314 | ++ if (tmy_io_printf(head, " %u", min_port)) | |
1315 | ++ goto out; | |
1316 | ++ if (min_port != max_port && tmy_io_printf(head, "-%u", max_port)) | |
1317 | ++ goto out; | |
1318 | ++ return 0; | |
1319 | ++out: ; | |
1320 | ++ return -ENOMEM; | |
1321 | ++} | |
1322 | ++ | |
1323 | ++/* Dump signal's ACL. */ | |
1324 | ++static inline int print_signal_acl(struct io_buffer *head, | |
1325 | ++ struct acl_info *ptr) | |
1326 | ++{ | |
1327 | ++ struct signal_acl *ptr2 = (struct signal_acl *) ptr; | |
1328 | ++ | |
1329 | ++ if (tmy_io_printf(head, TMY_ALLOW_SIGNAL "%u %s", | |
1330 | ++ ptr2->sig, ptr2->domainname->name)) | |
1331 | ++ return -ENOMEM; | |
1332 | ++ return 0; | |
1333 | ++} | |
1334 | ++ | |
1335 | ++/* Dump capability's ACL. */ | |
1336 | ++static inline int print_capability_acl(struct io_buffer *head, | |
1337 | ++ struct acl_info *ptr) | |
1338 | ++{ | |
1339 | ++ struct capability_acl *ptr2 = (struct capability_acl *) ptr; | |
1340 | ++ | |
1341 | ++ if (tmy_io_printf(head, TMY_ALLOW_CAPABILITY "%s", | |
1342 | ++ tmy_capability2keyword(ptr2->capability))) | |
1343 | ++ return -ENOMEM; | |
1344 | ++ return 0; | |
1345 | ++} | |
1346 | ++ | |
1347 | ++/* Dump file's non-open ACL. */ | |
1348 | ++static inline int print_file_other_acl(struct io_buffer *head, | |
1349 | ++ struct acl_info *ptr) | |
1350 | ++{ | |
1351 | ++ const u8 acl_type = ptr->type; | |
1352 | ++ const char *keyword = tmy_acltype2keyword(acl_type); | |
1353 | ++ | |
1354 | ++ if (!keyword) | |
1355 | ++ return 0; /* This must not happen. */ | |
1356 | ++ | |
1357 | ++ if (tmy_acltype2paths(acl_type) == 2) { | |
1358 | ++ struct double_acl *ptr2 = (struct double_acl *) ptr; | |
1359 | ++ const bool b1 = ptr2->u1_is_group; | |
1360 | ++ const bool b2 = ptr2->u2_is_group; | |
1361 | ++ | |
1362 | ++ if (tmy_io_printf(head, | |
1363 | ++ "allow_%s %s%s %s%s", | |
1364 | ++ keyword, | |
1365 | ++ b1 ? "@" : "", | |
1366 | ++ b1 ? ptr2->u1.group1->group_name->name : | |
1367 | ++ ptr2->u1.filename1->name, | |
1368 | ++ b2 ? "@" : "", | |
1369 | ++ b2 ? ptr2->u2.group2->group_name->name : | |
1370 | ++ ptr2->u2.filename2->name)) | |
1371 | ++ return -ENOMEM; | |
1372 | ++ } else { | |
1373 | ++ struct single_acl *ptr2 = (struct single_acl *) ptr; | |
1374 | ++ const bool b = ptr2->u_is_group; | |
1375 | ++ | |
1376 | ++ if (tmy_io_printf(head, | |
1377 | ++ "allow_%s %s%s", | |
1378 | ++ keyword, | |
1379 | ++ b ? "@" : "", | |
1380 | ++ b ? ptr2->u.group->group_name->name : | |
1381 | ++ ptr2->u.filename->name)) | |
1382 | ++ return -ENOMEM; | |
1383 | ++ } | |
1384 | ++ return 0; | |
1385 | ++} | |
1386 | ++ | |
1387 | ++/* Read domain policy. */ | |
1388 | ++static int tmy_read_domain_policy(struct io_buffer *head) | |
1389 | ++{ | |
1390 | ++ struct list_head *dpos; | |
1391 | ++ struct list_head *apos; | |
1392 | ++ | |
1393 | ++ if (head->read_eof) | |
1394 | ++ return 0; | |
1395 | ++ | |
1396 | ++ if (head->read_step == 0) { | |
1397 | ++ if (!tmy_is_root()) | |
1398 | ++ return -EPERM; | |
1399 | ++ head->read_step = 1; | |
1400 | ++ } | |
1401 | ++ list_for_each_cookie(dpos, head->read_var1, &domain_list) { | |
1402 | ++ struct domain_info *domain; | |
1403 | ++ domain = list_entry(dpos, struct domain_info, list); | |
1404 | ++ if (head->read_step != 1) | |
1405 | ++ goto acl_loop; | |
1406 | ++ if (domain->is_deleted) | |
1407 | ++ continue; | |
1408 | ++ if (tmy_io_printf(head, | |
1409 | ++ "%s\n" TMY_USE_PROFILE "%u\n%s\n", | |
1410 | ++ domain->domainname->name, | |
1411 | ++ domain->profile, | |
1412 | ++ domain->quota_warned ? | |
1413 | ++ "quota_exceeded\n" : "")) | |
1414 | ++ return 0; | |
1415 | ++ head->read_step = 2; | |
1416 | ++acl_loop: ; | |
1417 | ++ if (head->read_step == 3) | |
1418 | ++ goto tail_mark; | |
1419 | ++ list_for_each_cookie(apos, head->read_var2, | |
1420 | ++ &domain->acl_info_list) { | |
1421 | ++ struct acl_info *ptr; | |
1422 | ++ int pos; | |
1423 | ++ u8 acl_type; | |
1424 | ++ ptr = list_entry(apos, struct acl_info, list); | |
1425 | ++ if (ptr->is_deleted) | |
1426 | ++ continue; | |
1427 | ++ pos = head->read_avail; | |
1428 | ++ acl_type = ptr->type; | |
1429 | ++ if (acl_type == TMY_TYPE_FILE_ACL) { | |
1430 | ++ if (print_file_rwx_acl(head, ptr)) | |
1431 | ++ goto print_acl_rollback; | |
1432 | ++ } else if (acl_type == TMY_TYPE_ARGV0_ACL) { | |
1433 | ++ if (print_argv0_acl(head, ptr)) | |
1434 | ++ goto print_acl_rollback; | |
1435 | ++ } else if (acl_type == TMY_TYPE_IP_NETWORK_ACL) { | |
1436 | ++ if (print_network_acl(head, ptr)) | |
1437 | ++ goto print_acl_rollback; | |
1438 | ++ } else if (acl_type == TMY_TYPE_SIGNAL_ACL) { | |
1439 | ++ if (print_signal_acl(head, ptr)) | |
1440 | ++ goto print_acl_rollback; | |
1441 | ++ } else if (acl_type == TMY_TYPE_CAPABILITY_ACL) { | |
1442 | ++ if (print_capability_acl(head, ptr)) | |
1443 | ++ goto print_acl_rollback; | |
1444 | ++ } else | |
1445 | ++ if (print_file_other_acl(head, ptr)) | |
1446 | ++ goto print_acl_rollback; | |
1447 | ++ if (tmy_dump_condition(head, ptr->cond)) { | |
1448 | ++print_acl_rollback: ; | |
1449 | ++ head->read_avail = pos; | |
1450 | ++ return 0; | |
1451 | ++ } | |
1452 | ++ } | |
1453 | ++ head->read_step = 3; | |
1454 | ++tail_mark: ; | |
1455 | ++ if (tmy_io_printf(head, "\n")) | |
1456 | ++ return 0; | |
1457 | ++ head->read_step = 1; | |
1458 | ++ } | |
1459 | ++ head->read_eof = 1; | |
1460 | ++ return 0; | |
1461 | ++} | |
1462 | ++ | |
1463 | ++/* Read domainname and its profile values. */ | |
1464 | ++static int tmy_read_domain_profile(struct io_buffer *head) | |
1465 | ++{ | |
1466 | ++ struct list_head *pos; | |
1467 | ++ if (head->read_eof) | |
1468 | ++ return 0; | |
1469 | ++ if (!tmy_is_root()) | |
1470 | ++ return -EPERM; | |
1471 | ++ list_for_each_cookie(pos, head->read_var1, &domain_list) { | |
1472 | ++ struct domain_info *domain; | |
1473 | ++ domain = list_entry(pos, struct domain_info, list); | |
1474 | ++ if (domain->is_deleted) | |
1475 | ++ continue; | |
1476 | ++ if (tmy_io_printf(head, "%u %s\n", | |
1477 | ++ domain->profile, domain->domainname->name)) | |
1478 | ++ return 0; | |
1479 | ++ } | |
1480 | ++ head->read_eof = 1; | |
1481 | ++ return 0; | |
1482 | ++} | |
1483 | ++ | |
1484 | ++/* Set PID to report. Non manager process is permitted to call this function. */ | |
1485 | ++static int tmy_write_pid(struct io_buffer *head) | |
1486 | ++{ | |
1487 | ++ head->read_step = (int) simple_strtoul(head->write_buf, NULL, 10); | |
1488 | ++ head->read_eof = 0; | |
1489 | ++ return 0; | |
1490 | ++} | |
1491 | ++ | |
1492 | ++/* Read domainname and its profile values. */ | |
1493 | ++static int tmy_read_pid(struct io_buffer *head) | |
1494 | ++{ | |
1495 | ++ const int pid = head->read_step; | |
1496 | ++ struct task_struct *p; | |
1497 | ++ struct domain_info *domain = NULL; | |
1498 | ++ | |
1499 | ++ /* If PID is not set via tmy_write_pid(), do nothing. */ | |
1500 | ++ if (head->read_avail || head->read_eof) | |
1501 | ++ return 0; | |
1502 | ++ | |
1503 | ++ /***** CRITICAL SECTION START *****/ | |
1504 | ++ read_lock(&tasklist_lock); | |
1505 | ++ p = find_task_by_pid(pid); | |
1506 | ++ if (p) { | |
1507 | ++ /* "struct task_struct"->security is not NULL. */ | |
1508 | ++ domain = ((struct tmy_security *) p->security)->domain; | |
1509 | ++ } | |
1510 | ++ read_unlock(&tasklist_lock); | |
1511 | ++ /***** CRITICAL SECTION END *****/ | |
1512 | ++ | |
1513 | ++ if (domain) | |
1514 | ++ tmy_io_printf(head, "%d %u %s", | |
1515 | ++ pid, domain->profile, domain->domainname->name); | |
1516 | ++ head->read_eof = 1; | |
1517 | ++ | |
1518 | ++ return 0; | |
1519 | ++} | |
1520 | ++ | |
1521 | ++/* Update profile values for domains. */ | |
1522 | ++static int tmy_update_domain_profile(struct io_buffer *head) | |
1523 | ++{ | |
1524 | ++ char *data = head->write_buf; | |
1525 | ++ char *cp = strchr(data, ' '); | |
1526 | ++ struct domain_info *domain; | |
1527 | ++ unsigned int profile; | |
1528 | ++ | |
1529 | ++ if (!tmy_is_root()) | |
1530 | ++ return -EPERM; | |
1531 | ++ if (!cp) | |
1532 | ++ return -EINVAL; | |
1533 | ++ | |
1534 | ++ *cp = '\0'; | |
1535 | ++ domain = tmy_find_domain(cp + 1); | |
1536 | ++ profile = simple_strtoul(data, NULL, 10); | |
1537 | ++ if (domain && profile < TMY_MAX_PROFILES && | |
1538 | ++ (profile_ptr[profile] || !sbin_init_started)) | |
1539 | ++ domain->profile = (u8) profile; | |
1540 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
1541 | ++ | |
1542 | ++ return 0; | |
1543 | ++} | |
1544 | ++ | |
1545 | ++/************************* SYSTEM POLICY HANDLER *************************/ | |
1546 | ++ | |
1547 | ++/* Update system policy. */ | |
1548 | ++static int tmy_add_system_policy(struct io_buffer *head) | |
1549 | ++{ | |
1550 | ++ char *data = head->write_buf; | |
1551 | ++ bool is_delete = 0; | |
1552 | ++ | |
1553 | ++ if (!tmy_is_root()) | |
1554 | ++ return -EPERM; | |
1555 | ++ | |
1556 | ++ tmy_update_counter(TMY_UPDATE_SYSTEMPOLICY); | |
1557 | ++ | |
1558 | ++ if (tmy_strstarts(&data, TMY_DELETE)) | |
1559 | ++ is_delete = 1; | |
1560 | ++ if (tmy_strstarts(&data, TMY_ALLOW_MOUNT)) | |
1561 | ++ return tmy_add_mount_policy(data, is_delete); | |
1562 | ++ if (tmy_strstarts(&data, TMY_DENY_UNMOUNT)) | |
1563 | ++ return tmy_add_no_umount_policy(data, is_delete); | |
1564 | ++ if (tmy_strstarts(&data, TMY_ALLOW_PIVOT_ROOT)) | |
1565 | ++ return tmy_add_pivot_root_policy(data, is_delete); | |
1566 | ++ | |
1567 | ++ return -EINVAL; | |
1568 | ++} | |
1569 | ++ | |
1570 | ++/* Read system policy. */ | |
1571 | ++static int tmy_read_system_policy(struct io_buffer *head) | |
1572 | ++{ | |
1573 | ++ if (head->read_eof) | |
1574 | ++ return 0; | |
1575 | ++ switch (head->read_step) { | |
1576 | ++ case 0: | |
1577 | ++ if (!tmy_is_root()) | |
1578 | ++ return -EPERM; | |
1579 | ++ head->read_step = 1; | |
1580 | ++ case 1: | |
1581 | ++ if (tmy_read_mount_policy(head)) | |
1582 | ++ break; | |
1583 | ++ head->read_step = 2; | |
1584 | ++ case 2: | |
1585 | ++ if (tmy_read_no_umount_policy(head)) | |
1586 | ++ break; | |
1587 | ++ head->read_step = 3; | |
1588 | ++ case 3: | |
1589 | ++ if (tmy_read_pivot_root_policy(head)) | |
1590 | ++ break; | |
1591 | ++ head->read_eof = 1; | |
1592 | ++ break; | |
1593 | ++ default: | |
1594 | ++ return -EINVAL; | |
1595 | ++ } | |
1596 | ++ return 0; | |
1597 | ++} | |
1598 | ++ | |
1599 | ++/************************* EXCEPTION POLICY HANDLER *************************/ | |
1600 | ++/* Update exception policy. */ | |
1601 | ++static int tmy_add_exception_policy(struct io_buffer *head) | |
1602 | ++{ | |
1603 | ++ char *data = head->write_buf; | |
1604 | ++ bool is_delete = 0; | |
1605 | ++ | |
1606 | ++ if (!tmy_is_root()) | |
1607 | ++ return -EPERM; | |
1608 | ++ | |
1609 | ++ tmy_update_counter(TMY_UPDATE_EXCEPTIONPOLICY); | |
1610 | ++ | |
1611 | ++ if (tmy_strstarts(&data, TMY_DELETE)) | |
1612 | ++ is_delete = 1; | |
1613 | ++ if (tmy_strstarts(&data, TMY_KEEP_DOMAIN)) | |
1614 | ++ return tmy_add_domain_keeper_policy(data, 0, is_delete); | |
1615 | ++ if (tmy_strstarts(&data, TMY_NO_KEEP_DOMAIN)) | |
1616 | ++ return tmy_add_domain_keeper_policy(data, 1, is_delete); | |
1617 | ++ if (tmy_strstarts(&data, TMY_INITIALIZE_DOMAIN)) | |
1618 | ++ return tmy_add_domain_initializer_policy(data, 0, is_delete); | |
1619 | ++ if (tmy_strstarts(&data, TMY_NO_INITIALIZE_DOMAIN)) | |
1620 | ++ return tmy_add_domain_initializer_policy(data, 1, is_delete); | |
1621 | ++ if (tmy_strstarts(&data, TMY_ALIAS)) | |
1622 | ++ return tmy_add_alias_policy(data, is_delete); | |
1623 | ++ if (tmy_strstarts(&data, TMY_AGGREGATOR)) | |
1624 | ++ return tmy_add_aggregator_policy(data, is_delete); | |
1625 | ++ if (tmy_strstarts(&data, TMY_ALLOW_READ)) | |
1626 | ++ return tmy_add_globally_readable_policy(data, is_delete); | |
1627 | ++ if (tmy_strstarts(&data, TMY_FILE_PATTERN)) | |
1628 | ++ return tmy_add_pattern_policy(data, is_delete); | |
1629 | ++ if (tmy_strstarts(&data, TMY_PATH_GROUP)) | |
1630 | ++ return tmy_add_group_policy(data, is_delete); | |
1631 | ++ if (tmy_strstarts(&data, TMY_DENY_REWRITE)) | |
1632 | ++ return tmy_add_no_rewrite_policy(data, is_delete); | |
1633 | ++ if (tmy_strstarts(&data, TMY_ADDRESS_GROUP)) | |
1634 | ++ return tmy_add_address_group_policy(data, is_delete); | |
1635 | ++ | |
1636 | ++ return -EINVAL; | |
1637 | ++} | |
1638 | ++ | |
1639 | ++/* Read exception policy. */ | |
1640 | ++static int tmy_read_exception_policy(struct io_buffer *head) | |
1641 | ++{ | |
1642 | ++ if (head->read_eof) | |
1643 | ++ return 0; | |
1644 | ++ switch (head->read_step) { | |
1645 | ++ case 0: | |
1646 | ++ if (!tmy_is_root()) | |
1647 | ++ return -EPERM; | |
1648 | ++ head->read_step = 1; | |
1649 | ++ case 1: | |
1650 | ++ if (tmy_read_domain_keeper_policy(head)) | |
1651 | ++ break; | |
1652 | ++ head->read_step = 2; | |
1653 | ++ case 2: | |
1654 | ++ if (tmy_read_globally_readable_policy(head)) | |
1655 | ++ break; | |
1656 | ++ head->read_step = 3; | |
1657 | ++ case 3: | |
1658 | ++ if (tmy_read_domain_initializer_policy(head)) | |
1659 | ++ break; | |
1660 | ++ head->read_step = 4; | |
1661 | ++ case 4: | |
1662 | ++ if (tmy_read_alias_policy(head)) | |
1663 | ++ break; | |
1664 | ++ head->read_step = 5; | |
1665 | ++ case 5: | |
1666 | ++ if (tmy_read_aggregator_policy(head)) | |
1667 | ++ break; | |
1668 | ++ head->read_step = 6; | |
1669 | ++ case 6: | |
1670 | ++ if (tmy_read_pattern_policy(head)) | |
1671 | ++ break; | |
1672 | ++ head->read_step = 7; | |
1673 | ++ case 7: | |
1674 | ++ if (tmy_read_no_rewrite_policy(head)) | |
1675 | ++ break; | |
1676 | ++ head->read_step = 8; | |
1677 | ++ case 8: | |
1678 | ++ if (tmy_read_path_group_policy(head)) | |
1679 | ++ break; | |
1680 | ++ head->read_step = 9; | |
1681 | ++ case 9: | |
1682 | ++ if (tmy_read_address_group_policy(head)) | |
1683 | ++ break; | |
1684 | ++ head->read_eof = 1; | |
1685 | ++ break; | |
1686 | ++ default: | |
1687 | ++ return -EINVAL; | |
1688 | ++ } | |
1689 | ++ return 0; | |
1690 | ++} | |
1691 | ++ | |
1692 | ++/************************* POLICY LOADER *************************/ | |
1693 | ++ | |
1694 | ++/** | |
1695 | ++ * tmy_load_policy - load policy and activate access control. | |
1696 | ++ * @filename: program to be executed. | |
1697 | ++ * | |
1698 | ++ * This function calls /sbin/tomoyo-init using call_usermodehelper() | |
1699 | ++ * to load policy | |
1700 | ++ * if "execution of /sbin/init is requested" and "/sbin/tomoyo-init exists". | |
1701 | ++ */ | |
1702 | ++void tmy_load_policy(const char *filename) | |
1703 | ++{ | |
1704 | ++ if (sbin_init_started) | |
1705 | ++ return; | |
1706 | ++ if (strcmp(filename, "/sbin/init") != 0) | |
1707 | ++ return; | |
1708 | ++ | |
1709 | ++ /* | |
1710 | ++ * Don't activate MAC if the path '/sbin/tomoyo-init' doesn't exist. | |
1711 | ++ * If initrd.img includes /sbin/init but real-root-dev has not | |
1712 | ++ * mounted on / yet, activating MAC will block the system | |
1713 | ++ * since policies are not loaded yet. | |
1714 | ++ * So let do_execve() call this function everytime. | |
1715 | ++ */ | |
1716 | ++ if (!profile_loaded) { | |
1717 | ++ const char *tmy_loader = "/sbin/tomoyo-init"; | |
1718 | ++ struct nameidata nd; | |
1719 | ++ char *argv[2]; | |
1720 | ++ char *envp[3]; | |
1721 | ++ | |
1722 | ++ if (path_lookup(tmy_loader, LOOKUP_FOLLOW, &nd)) { | |
1723 | ++ printk("TOMOYO: Not activating Mandatory Access Control" | |
1724 | ++ " now since %s doesn't exist.\n", tmy_loader); | |
1725 | ++ return; | |
1726 | ++ } | |
1727 | ++ path_release(&nd); | |
1728 | ++ argv[0] = (char *) tmy_loader; | |
1729 | ++ argv[1] = NULL; | |
1730 | ++ envp[0] = "HOME=/"; | |
1731 | ++ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
1732 | ++ envp[2] = NULL; | |
1733 | ++ call_usermodehelper(argv[0], argv, envp, 1); | |
1734 | ++ } | |
1735 | ++ | |
1736 | ++ printk(KERN_INFO "TOMOYO: %s 2007/11/23\n", TOMOYO_VERSION_CODE); | |
1737 | ++ | |
1738 | ++ if (!profile_loaded) | |
1739 | ++ panic("TOMOYO: No profiles loaded.\n"); | |
1740 | ++ | |
1741 | ++ printk(KERN_INFO "TOMOYO: Mandatory Access Control activated.\n"); | |
1742 | ++ sbin_init_started = 1; | |
1743 | ++ | |
1744 | ++ { /* Check all profiles currently assigned to domains are defined. */ | |
1745 | ++ struct domain_info *domain; | |
1746 | ++ list_for_each_entry(domain, &domain_list, list) { | |
1747 | ++ const u8 profile = domain->profile; | |
1748 | ++ if (profile_ptr[profile]) | |
1749 | ++ continue; | |
1750 | ++ panic("TOMOYO: Profile %u (used by '%s') " | |
1751 | ++ "not defined.\n", | |
1752 | ++ profile, | |
1753 | ++ domain->domainname->name); | |
1754 | ++ } | |
1755 | ++ } | |
1756 | ++} | |
1757 | ++ | |
1758 | ++ | |
1759 | ++/************************* MAC Decision Delayer *************************/ | |
1760 | ++ | |
1761 | ++static DECLARE_WAIT_QUEUE_HEAD(query_wait); | |
1762 | ++ | |
1763 | ++static DEFINE_SPINLOCK(query_lock); | |
1764 | ++ | |
1765 | ++struct query_entry { | |
1766 | ++ struct list_head list; | |
1767 | ++ char *query; | |
1768 | ++ int query_len; | |
1769 | ++ unsigned int serial; | |
1770 | ++ int timer; | |
1771 | ++ int answer; | |
1772 | ++}; | |
1773 | ++ | |
1774 | ++static LIST_HEAD(query_list); | |
1775 | ++static atomic_t queryd_watcher = ATOMIC_INIT(0); | |
1776 | ++ | |
1777 | ++/** | |
1778 | ++ * tmy_supervisor - ask for supervisors decision. | |
1779 | ++ * @fmt: format strings for printf(). | |
1780 | ++ * | |
1781 | ++ * Returns zero if administrator allowed. | |
1782 | ++ * Returns nonzero otherwise. | |
1783 | ++ * | |
1784 | ++ * This is one of TOMOYO Linux's feature. | |
1785 | ++ * Normally, access requests that violates policy is rejected immediately. | |
1786 | ++ * But this behavior is inconvenient when developing policy. | |
1787 | ++ * TOMOYO Linux allows administrators handle access requests that violated | |
1788 | ++ * policy in enforce mode to adjust policy. | |
1789 | ++ * | |
1790 | ++ * This function blocks a process who is requesting access that violated policy | |
1791 | ++ * and tell it via /sys/kernel/security/tomoyo/query and wait for supervisor's | |
1792 | ++ * decision. | |
1793 | ++ */ | |
1794 | ++int tmy_supervisor(const char *fmt, ...) | |
1795 | ++{ | |
1796 | ++ va_list args; | |
1797 | ++ int error = -EPERM; | |
1798 | ++ int pos; | |
1799 | ++ int len; | |
1800 | ++ static unsigned int serial; | |
1801 | ++ struct query_entry *query_entry; | |
1802 | ++ | |
1803 | ++ if (!tmy_flags(TMY_ALLOW_ENFORCE_GRACE)) | |
1804 | ++ return -EPERM; | |
1805 | ++ if (!atomic_read(&queryd_watcher)) | |
1806 | ++ return -EPERM; | |
1807 | ++ | |
1808 | ++ va_start(args, fmt); | |
1809 | ++ len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; | |
1810 | ++ va_end(args); | |
1811 | ++ | |
1812 | ++ query_entry = tmy_alloc(sizeof(*query_entry)); | |
1813 | ++ if (!query_entry) | |
1814 | ++ goto out; | |
1815 | ++ query_entry->query = tmy_alloc(len); | |
1816 | ++ if (!query_entry->query) | |
1817 | ++ goto out; | |
1818 | ++ | |
1819 | ++ INIT_LIST_HEAD(&query_entry->list); | |
1820 | ++ | |
1821 | ++ /***** CRITICAL SECTION START *****/ | |
1822 | ++ spin_lock(&query_lock); | |
1823 | ++ query_entry->serial = serial++; | |
1824 | ++ spin_unlock(&query_lock); | |
1825 | ++ /***** CRITICAL SECTION END *****/ | |
1826 | ++ | |
1827 | ++ pos = snprintf(query_entry->query, len - 1, | |
1828 | ++ "Q%u\n", query_entry->serial); | |
1829 | ++ va_start(args, fmt); | |
1830 | ++ vsnprintf(query_entry->query + pos, len - 1 - pos, fmt, args); | |
1831 | ++ query_entry->query_len = strlen(query_entry->query) + 1; | |
1832 | ++ va_end(args); | |
1833 | ++ | |
1834 | ++ /***** CRITICAL SECTION START *****/ | |
1835 | ++ spin_lock(&query_lock); | |
1836 | ++ list_add_tail(&query_entry->list, &query_list); | |
1837 | ++ spin_unlock(&query_lock); | |
1838 | ++ /***** CRITICAL SECTION END *****/ | |
1839 | ++ | |
1840 | ++ tmy_update_counter(TMY_UPDATE_QUERY); | |
1841 | ++ | |
1842 | ++ /* Give 10 seconds for supervisor's opinion. */ | |
1843 | ++ for (query_entry->timer = 0; | |
1844 | ++ atomic_read(&queryd_watcher) && | |
1845 | ++ tmy_flags(TMY_ALLOW_ENFORCE_GRACE) && | |
1846 | ++ query_entry->timer < 100; | |
1847 | ++ query_entry->timer++) { | |
1848 | ++ wake_up(&query_wait); | |
1849 | ++ set_current_state(TASK_INTERRUPTIBLE); | |
1850 | ++ schedule_timeout(HZ / 10); | |
1851 | ++ if (query_entry->answer) | |
1852 | ++ break; | |
1853 | ++ } | |
1854 | ++ | |
1855 | ++ tmy_update_counter(TMY_UPDATE_QUERY); | |
1856 | ++ | |
1857 | ++ /***** CRITICAL SECTION START *****/ | |
1858 | ++ spin_lock(&query_lock); | |
1859 | ++ list_del(&query_entry->list); | |
1860 | ++ spin_unlock(&query_lock); | |
1861 | ++ /***** CRITICAL SECTION END *****/ | |
1862 | ++ | |
1863 | ++ switch (query_entry->answer) { | |
1864 | ++ case 1: | |
1865 | ++ /* Granted by administrator. */ | |
1866 | ++ error = 0; | |
1867 | ++ break; | |
1868 | ++ case 0: | |
1869 | ++ /* Timed out. */ | |
1870 | ++ break; | |
1871 | ++ default: | |
1872 | ++ /* Rejected by administrator. */ | |
1873 | ++ break; | |
1874 | ++ } | |
1875 | ++ | |
1876 | ++out: ; | |
1877 | ++ if (query_entry) | |
1878 | ++ tmy_free(query_entry->query); | |
1879 | ++ tmy_free(query_entry); | |
1880 | ++ return error; | |
1881 | ++} | |
1882 | ++ | |
1883 | ++/* Check for pending access requests. */ | |
1884 | ++static int tmy_poll_query(struct file *file, poll_table *wait) | |
1885 | ++{ | |
1886 | ++ int found; | |
1887 | ++ | |
1888 | ++ /***** CRITICAL SECTION START *****/ | |
1889 | ++ spin_lock(&query_lock); | |
1890 | ++ found = !list_empty(&query_list); | |
1891 | ++ spin_unlock(&query_lock); | |
1892 | ++ /***** CRITICAL SECTION END *****/ | |
1893 | ++ | |
1894 | ++ if (found) | |
1895 | ++ return POLLIN | POLLRDNORM; | |
1896 | ++ poll_wait(file, &query_wait, wait); | |
1897 | ++ | |
1898 | ++ /***** CRITICAL SECTION START *****/ | |
1899 | ++ spin_lock(&query_lock); | |
1900 | ++ found = !list_empty(&query_list); | |
1901 | ++ spin_unlock(&query_lock); | |
1902 | ++ /***** CRITICAL SECTION END *****/ | |
1903 | ++ | |
1904 | ++ if (found) | |
1905 | ++ return POLLIN | POLLRDNORM; | |
1906 | ++ return 0; | |
1907 | ++} | |
1908 | ++ | |
1909 | ++/* Read pending access requests. */ | |
1910 | ++static int tmy_read_query(struct io_buffer *head) | |
1911 | ++{ | |
1912 | ++ struct list_head *tmp; | |
1913 | ++ int pos = 0; | |
1914 | ++ int len = 0; | |
1915 | ++ char *buf; | |
1916 | ++ | |
1917 | ++ if (head->read_avail) | |
1918 | ++ return 0; | |
1919 | ++ if (head->read_buf) { | |
1920 | ++ tmy_free(head->read_buf); | |
1921 | ++ head->read_buf = NULL; | |
1922 | ++ head->readbuf_size = 0; | |
1923 | ++ } | |
1924 | ++ | |
1925 | ++ /***** CRITICAL SECTION START *****/ | |
1926 | ++ spin_lock(&query_lock); | |
1927 | ++ list_for_each(tmp, &query_list) { | |
1928 | ++ struct query_entry *ptr | |
1929 | ++ = list_entry(tmp, struct query_entry, list); | |
1930 | ++ if (pos++ == head->read_step) { | |
1931 | ++ len = ptr->query_len; | |
1932 | ++ break; | |
1933 | ++ } | |
1934 | ++ } | |
1935 | ++ spin_unlock(&query_lock); | |
1936 | ++ /***** CRITICAL SECTION END *****/ | |
1937 | ++ | |
1938 | ++ if (!len) { | |
1939 | ++ head->read_step = 0; | |
1940 | ++ return 0; | |
1941 | ++ } | |
1942 | ++ buf = tmy_alloc(len); | |
1943 | ++ if (buf) { | |
1944 | ++ pos = 0; | |
1945 | ++ | |
1946 | ++ /***** CRITICAL SECTION START *****/ | |
1947 | ++ spin_lock(&query_lock); | |
1948 | ++ list_for_each(tmp, &query_list) { | |
1949 | ++ struct query_entry *ptr | |
1950 | ++ = list_entry(tmp, struct query_entry, list); | |
1951 | ++ if (pos++ == head->read_step) { | |
1952 | ++ /* Some query can be skiipped because | |
1953 | ++ * query_list can change, but I don't care. | |
1954 | ++ */ | |
1955 | ++ if (len == ptr->query_len) | |
1956 | ++ memmove(buf, ptr->query, len); | |
1957 | ++ break; | |
1958 | ++ } | |
1959 | ++ } | |
1960 | ++ spin_unlock(&query_lock); | |
1961 | ++ /***** CRITICAL SECTION END *****/ | |
1962 | ++ | |
1963 | ++ if (buf[0]) { | |
1964 | ++ head->readbuf_size = len; | |
1965 | ++ head->read_avail = len; | |
1966 | ++ head->read_buf = buf; | |
1967 | ++ head->read_step++; | |
1968 | ++ } else | |
1969 | ++ tmy_free(buf); | |
1970 | ++ } | |
1971 | ++ | |
1972 | ++ return 0; | |
1973 | ++} | |
1974 | ++ | |
1975 | ++/* Reply to pending access requests. */ | |
1976 | ++static int tmy_write_answer(struct io_buffer *head) | |
1977 | ++{ | |
1978 | ++ char *data = head->write_buf; | |
1979 | ++ struct list_head *tmp; | |
1980 | ++ unsigned int serial; | |
1981 | ++ unsigned int answer; | |
1982 | ++ | |
1983 | ++ /***** CRITICAL SECTION START *****/ | |
1984 | ++ spin_lock(&query_lock); | |
1985 | ++ list_for_each(tmp, &query_list) { | |
1986 | ++ struct query_entry *ptr | |
1987 | ++ = list_entry(tmp, struct query_entry, list); | |
1988 | ++ ptr->timer = 0; | |
1989 | ++ } | |
1990 | ++ spin_unlock(&query_lock); | |
1991 | ++ /***** CRITICAL SECTION END *****/ | |
1992 | ++ | |
1993 | ++ if (sscanf(data, "A%u=%u", &serial, &answer) != 2) | |
1994 | ++ return -EINVAL; | |
1995 | ++ | |
1996 | ++ /***** CRITICAL SECTION START *****/ | |
1997 | ++ spin_lock(&query_lock); | |
1998 | ++ list_for_each(tmp, &query_list) { | |
1999 | ++ struct query_entry *ptr | |
2000 | ++ = list_entry(tmp, struct query_entry, list); | |
2001 | ++ if (ptr->serial != serial) | |
2002 | ++ continue; | |
2003 | ++ if (!ptr->answer) | |
2004 | ++ ptr->answer = answer; | |
2005 | ++ break; | |
2006 | ++ } | |
2007 | ++ spin_unlock(&query_lock); | |
2008 | ++ /***** CRITICAL SECTION END *****/ | |
2009 | ++ | |
2010 | ++ return 0; | |
2011 | ++} | |
2012 | ++ | |
2013 | ++/****************** /sys/kernel/security INTERFACE HANDLER ******************/ | |
2014 | ++ | |
2015 | ++/* Policy updates counter. */ | |
2016 | ++static unsigned int updates_counter[TMY_MAX_UPDATES_COUNTER]; | |
2017 | ++static DEFINE_SPINLOCK(updates_counter_lock); | |
2018 | ++ | |
2019 | ++/** | |
2020 | ++ * tmy_update_counter - notify userland that policy is changed. | |
2021 | ++ * @index: index to update counter. | |
2022 | ++ * | |
2023 | ++ * This is for userland process who is monitoring policy changes. | |
2024 | ++ */ | |
2025 | ++void tmy_update_counter(const unsigned char index) | |
2026 | ++{ | |
2027 | ++ /***** CRITICAL SECTION START *****/ | |
2028 | ++ spin_lock(&updates_counter_lock); | |
2029 | ++ if (index < TMY_MAX_UPDATES_COUNTER) | |
2030 | ++ updates_counter[index]++; | |
2031 | ++ spin_unlock(&updates_counter_lock); | |
2032 | ++ /***** CRITICAL SECTION END *****/ | |
2033 | ++} | |
2034 | ++ | |
2035 | ++/* Read policy update counter. */ | |
2036 | ++static int tmy_read_updates_counter(struct io_buffer *head) | |
2037 | ++{ | |
2038 | ++ unsigned int counter[TMY_MAX_UPDATES_COUNTER]; | |
2039 | ++ if (head->read_eof) | |
2040 | ++ return 0; | |
2041 | ++ | |
2042 | ++ /***** CRITICAL SECTION START *****/ | |
2043 | ++ spin_lock(&updates_counter_lock); | |
2044 | ++ memmove(counter, updates_counter, sizeof(updates_counter)); | |
2045 | ++ memset(updates_counter, 0, sizeof(updates_counter)); | |
2046 | ++ spin_unlock(&updates_counter_lock); | |
2047 | ++ /***** CRITICAL SECTION END *****/ | |
2048 | ++ | |
2049 | ++ tmy_io_printf(head, | |
2050 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
2051 | ++ "/proc/tomoyo/system_policy: %10u\n" | |
2052 | ++ "/proc/tomoyo/domain_policy: %10u\n" | |
2053 | ++ "/proc/tomoyo/exception_policy: %10u\n" | |
2054 | ++ "/proc/tomoyo/profile: %10u\n" | |
2055 | ++ "/proc/tomoyo/query: %10u\n" | |
2056 | ++ "/proc/tomoyo/manager: %10u\n" | |
2057 | ++ "/proc/tomoyo/grant_log: %10u\n" | |
2058 | ++ "/proc/tomoyo/reject_log: %10u\n", | |
2059 | ++#else | |
2060 | ++ "/sys/kernel/security/tomoyo/system_policy: %10u\n" | |
2061 | ++ "/sys/kernel/security/tomoyo/domain_policy: %10u\n" | |
2062 | ++ "/sys/kernel/security/tomoyo/exception_policy: %10u\n" | |
2063 | ++ "/sys/kernel/security/tomoyo/profile: %10u\n" | |
2064 | ++ "/sys/kernel/security/tomoyo/query: %10u\n" | |
2065 | ++ "/sys/kernel/security/tomoyo/manager: %10u\n" | |
2066 | ++ "/sys/kernel/security/tomoyo/grant_log: %10u\n" | |
2067 | ++ "/sys/kernel/security/tomoyo/reject_log: %10u\n", | |
2068 | ++#endif | |
2069 | ++ counter[TMY_UPDATE_SYSTEMPOLICY], | |
2070 | ++ counter[TMY_UPDATE_DOMAINPOLICY], | |
2071 | ++ counter[TMY_UPDATE_EXCEPTIONPOLICY], | |
2072 | ++ counter[TMY_UPDATE_PROFILE], | |
2073 | ++ counter[TMY_UPDATE_QUERY], | |
2074 | ++ counter[TMY_UPDATE_MANAGER], | |
2075 | ++ counter[TMY_UPDATE_GRANT_LOG], | |
2076 | ++ counter[TMY_UPDATE_REJECT_LOG]); | |
2077 | ++ | |
2078 | ++ head->read_eof = 1; | |
2079 | ++ | |
2080 | ++ return 0; | |
2081 | ++} | |
2082 | ++ | |
2083 | ++/* Read how much memory is used. */ | |
2084 | ++static int tmy_read_memory_counter(struct io_buffer *head) | |
2085 | ++{ | |
2086 | ++ int shared; | |
2087 | ++ int private; | |
2088 | ++ int dynamic; | |
2089 | ++ | |
2090 | ++ if (head->read_eof) | |
2091 | ++ return 0; | |
2092 | ++ shared = tmy_get_memory_used_for_save_name(); | |
2093 | ++ private = tmy_get_memory_used_for_elements(); | |
2094 | ++ dynamic = tmy_get_memory_used_for_dynamic(); | |
2095 | ++ if (tmy_io_printf(head, | |
2096 | ++ "Shared: %10u\n" | |
2097 | ++ "Private: %10u\n" | |
2098 | ++ "Dynamic: %10u\n" | |
2099 | ++ "Total: %10u\n", | |
2100 | ++ shared, | |
2101 | ++ private, | |
2102 | ++ dynamic, | |
2103 | ++ shared + private + dynamic) == 0) | |
2104 | ++ head->read_eof = 1; | |
2105 | ++ return 0; | |
2106 | ++} | |
2107 | ++ | |
2108 | ++/* Read TOMOYO Linux's version. */ | |
2109 | ++static int tmy_read_version(struct io_buffer *head) | |
2110 | ++{ | |
2111 | ++ if (!head->read_eof) { | |
2112 | ++ tmy_io_printf(head, TOMOYO_VERSION_CODE "\n"); | |
2113 | ++ head->read_eof = 1; | |
2114 | ++ } | |
2115 | ++ return 0; | |
2116 | ++} | |
2117 | ++ | |
2118 | ++/* Read current process's domainname. */ | |
2119 | ++static int tmy_read_self_domain(struct io_buffer *head) | |
2120 | ++{ | |
2121 | ++ if (!head->read_eof) { | |
2122 | ++ tmy_io_printf(head, | |
2123 | ++ "%s", | |
2124 | ++ TMY_SECURITY->domain->domainname->name); | |
2125 | ++ head->read_eof = 1; | |
2126 | ++ } | |
2127 | ++ | |
2128 | ++ return 0; | |
2129 | ++} | |
2130 | ++ | |
2131 | ++/* This is /sys/kernel/security/tomoyo/ interface. */ | |
2132 | ++static int tmy_open_control(const int type, struct file *file) | |
2133 | ++{ | |
2134 | ++ struct io_buffer *head = tmy_alloc(sizeof(*head)); | |
2135 | ++ | |
2136 | ++ if (!head) | |
2137 | ++ return -ENOMEM; | |
2138 | ++ mutex_init(&head->read_mutex); | |
2139 | ++ mutex_init(&head->write_mutex); | |
2140 | ++ | |
2141 | ++ switch (type) { | |
2142 | ++ case TMY_DOMAINPOLICY: | |
2143 | ++ head->write = tmy_add_domain_policy; | |
2144 | ++ head->read = tmy_read_domain_policy; | |
2145 | ++ break; | |
2146 | ++ case TMY_SYSTEMPOLICY: | |
2147 | ++ head->write = tmy_add_system_policy; | |
2148 | ++ head->read = tmy_read_system_policy; | |
2149 | ++ break; | |
2150 | ++ case TMY_EXCEPTIONPOLICY: | |
2151 | ++ head->write = tmy_add_exception_policy; | |
2152 | ++ head->read = tmy_read_exception_policy; | |
2153 | ++ break; | |
2154 | ++ case TMY_DOMAIN_STATUS: | |
2155 | ++ head->write = tmy_update_domain_profile; | |
2156 | ++ head->read = tmy_read_domain_profile; | |
2157 | ++ break; | |
2158 | ++ case TMY_PROCESS_STATUS: | |
2159 | ++ head->write = tmy_write_pid; | |
2160 | ++ head->read = tmy_read_pid; | |
2161 | ++ break; | |
2162 | ++ case TMY_SELFDOMAIN: | |
2163 | ++ head->read = tmy_read_self_domain; | |
2164 | ++ break; | |
2165 | ++ case TMY_MEMINFO: | |
2166 | ++ head->read = tmy_read_memory_counter; | |
2167 | ++ head->readbuf_size = 128; | |
2168 | ++ break; | |
2169 | ++ case TMY_PROFILE: | |
2170 | ++ head->write = tmy_set_profile; | |
2171 | ++ head->read = tmy_read_profile; | |
2172 | ++ break; | |
2173 | ++ case TMY_QUERY: | |
2174 | ++ head->poll = tmy_poll_query; | |
2175 | ++ head->write = tmy_write_answer; | |
2176 | ++ head->read = tmy_read_query; | |
2177 | ++ break; | |
2178 | ++ case TMY_MANAGER: | |
2179 | ++ head->write = tmy_add_manager_policy; | |
2180 | ++ head->read = tmy_read_manager_policy; | |
2181 | ++ break; | |
2182 | ++ case TMY_UPDATESCOUNTER: | |
2183 | ++ head->read = tmy_read_updates_counter; | |
2184 | ++ break; | |
2185 | ++ case TMY_VERSION: | |
2186 | ++ head->read = tmy_read_version; | |
2187 | ++ break; | |
2188 | ++ case TMY_GRANT_LOG: | |
2189 | ++ head->read = tmy_read_grant_log; | |
2190 | ++ head->poll = tmy_poll_grant_log; | |
2191 | ++ break; | |
2192 | ++ case TMY_REJECT_LOG: | |
2193 | ++ head->read = tmy_read_reject_log; | |
2194 | ++ head->poll = tmy_poll_reject_log; | |
2195 | ++ break; | |
2196 | ++ } | |
2197 | ++ | |
2198 | ++ if (type != TMY_QUERY) { | |
2199 | ++ if (!head->readbuf_size) | |
2200 | ++ head->readbuf_size = PAGE_SIZE * 2; | |
2201 | ++ head->read_buf = tmy_alloc(head->readbuf_size); | |
2202 | ++ if (!head->read_buf) { | |
2203 | ++ tmy_free(head); | |
2204 | ++ return -ENOMEM; | |
2205 | ++ } | |
2206 | ++ } | |
2207 | ++ | |
2208 | ++ if (head->write) { | |
2209 | ++ head->writebuf_size = PAGE_SIZE * 2; | |
2210 | ++ head->write_buf = tmy_alloc(head->writebuf_size); | |
2211 | ++ if (!head->write_buf) { | |
2212 | ++ tmy_free(head->read_buf); | |
2213 | ++ tmy_free(head); | |
2214 | ++ return -ENOMEM; | |
2215 | ++ } | |
2216 | ++ } | |
2217 | ++ | |
2218 | ++ file->private_data = head; | |
2219 | ++ | |
2220 | ++ if (type == TMY_SELFDOMAIN) | |
2221 | ++ tmy_read_control(file, NULL, 0); | |
2222 | ++ else if (head->write == tmy_write_answer) | |
2223 | ++ atomic_inc(&queryd_watcher); | |
2224 | ++ | |
2225 | ++ return 0; | |
2226 | ++} | |
2227 | ++ | |
2228 | ++/* Copy read data to userland buffer. */ | |
2229 | ++static int tmy_copy_to_user(struct io_buffer *head, char __user *buffer, | |
2230 | ++ int buffer_len) | |
2231 | ++{ | |
2232 | ++ int len = head->read_avail; | |
2233 | ++ char *cp = head->read_buf; | |
2234 | ++ | |
2235 | ++ if (len > buffer_len) | |
2236 | ++ len = buffer_len; | |
2237 | ++ if (len) { | |
2238 | ++ if (copy_to_user(buffer, cp, len)) | |
2239 | ++ return -EFAULT; | |
2240 | ++ head->read_avail -= len; | |
2241 | ++ memmove(cp, cp + len, head->read_avail); | |
2242 | ++ } | |
2243 | ++ | |
2244 | ++ return len; | |
2245 | ++} | |
2246 | ++ | |
2247 | ++/* Check for pending requests. */ | |
2248 | ++static int tmy_poll_control(struct file *file, poll_table *wait) | |
2249 | ++{ | |
2250 | ++ struct io_buffer *head = (struct io_buffer *) file->private_data; | |
2251 | ++ if (!head->poll) | |
2252 | ++ return -ENOSYS; | |
2253 | ++ return head->poll(file, wait); | |
2254 | ++} | |
2255 | ++ | |
2256 | ++/* Read policy. */ | |
2257 | ++static int tmy_read_control(struct file *file, char __user *buffer, | |
2258 | ++ const int buffer_len) | |
2259 | ++{ | |
2260 | ++ int len = 0; | |
2261 | ++ struct io_buffer *head = (struct io_buffer *) file->private_data; | |
2262 | ++ | |
2263 | ++ if (!head->read) | |
2264 | ++ return -ENOSYS; | |
2265 | ++ if (!access_ok(VERIFY_WRITE, buffer, buffer_len)) | |
2266 | ++ return -EFAULT; | |
2267 | ++ if (mutex_lock_interruptible(&head->read_mutex)) | |
2268 | ++ return -EINTR; | |
2269 | ++ len = head->read(head); | |
2270 | ++ if (len >= 0) | |
2271 | ++ len = tmy_copy_to_user(head, buffer, buffer_len); | |
2272 | ++ mutex_unlock(&head->read_mutex); | |
2273 | ++ | |
2274 | ++ return len; | |
2275 | ++} | |
2276 | ++ | |
2277 | ++/* Update policy. */ | |
2278 | ++static int tmy_write_control(struct file *file, const char __user *buffer, | |
2279 | ++ const int buffer_len) | |
2280 | ++{ | |
2281 | ++ struct io_buffer *head = (struct io_buffer *) file->private_data; | |
2282 | ++ int error = buffer_len; | |
2283 | ++ int avail_len = buffer_len; | |
2284 | ++ char *cp0 = head->write_buf; | |
2285 | ++ | |
2286 | ++ if (!head->write) | |
2287 | ++ return -ENOSYS; | |
2288 | ++ if (!access_ok(VERIFY_READ, buffer, buffer_len)) | |
2289 | ++ return -EFAULT; | |
2290 | ++ if (!tmy_is_root()) | |
2291 | ++ return -EPERM; | |
2292 | ++ if (head->write != tmy_write_pid && !tmy_is_policy_manager()) | |
2293 | ++ /* Forbid updating policies for non manager programs. */ | |
2294 | ++ return -EPERM; | |
2295 | ++ | |
2296 | ++ if (mutex_lock_interruptible(&head->write_mutex)) | |
2297 | ++ return -EINTR; | |
2298 | ++ while (avail_len > 0) { | |
2299 | ++ char c; | |
2300 | ++ if (head->write_avail >= head->writebuf_size - 1) { | |
2301 | ++ error = -ENOMEM; | |
2302 | ++ break; | |
2303 | ++ } else if (get_user(c, buffer)) { | |
2304 | ++ error = -EFAULT; | |
2305 | ++ break; | |
2306 | ++ } | |
2307 | ++ buffer++; | |
2308 | ++ avail_len--; | |
2309 | ++ cp0[head->write_avail++] = c; | |
2310 | ++ if (c != '\n') | |
2311 | ++ continue; | |
2312 | ++ cp0[head->write_avail - 1] = '\0'; | |
2313 | ++ head->write_avail = 0; | |
2314 | ++ tmy_normalize_line(cp0); | |
2315 | ++ head->write(head); | |
2316 | ++ } | |
2317 | ++ mutex_unlock(&head->write_mutex); | |
2318 | ++ | |
2319 | ++ return error; | |
2320 | ++} | |
2321 | ++ | |
2322 | ++/* Close /sys/kernel/security/tomoyo/ interface. */ | |
2323 | ++static int tmy_close_control(struct file *file) | |
2324 | ++{ | |
2325 | ++ struct io_buffer *head = file->private_data; | |
2326 | ++ | |
2327 | ++ if (head->write == tmy_write_answer) | |
2328 | ++ atomic_dec(&queryd_watcher); | |
2329 | ++ | |
2330 | ++ tmy_free(head->read_buf); | |
2331 | ++ head->read_buf = NULL; | |
2332 | ++ tmy_free(head->write_buf); | |
2333 | ++ head->write_buf = NULL; | |
2334 | ++ tmy_free(head); | |
2335 | ++ head = NULL; | |
2336 | ++ file->private_data = NULL; | |
2337 | ++ return 0; | |
2338 | ++} | |
2339 | ++ | |
2340 | ++/* open() operation for /sys/kernel/security/tomoyo/ interface. */ | |
2341 | ++static int tmy_open(struct inode *inode, struct file *file) | |
2342 | ++{ | |
2343 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
2344 | ++ return tmy_open_control(((u8 *) PDE(inode)->data) - ((u8 *) NULL), | |
2345 | ++ file); | |
2346 | ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
2347 | ++ return tmy_open_control(((u8 *) file->f_dentry->d_inode->u.generic_ip) | |
2348 | ++ - ((u8 *) NULL), file); | |
2349 | ++#else | |
2350 | ++ return tmy_open_control(((u8 *) file->f_dentry->d_inode->i_private) | |
2351 | ++ - ((u8 *) NULL), file); | |
2352 | ++#endif | |
2353 | ++} | |
2354 | ++ | |
2355 | ++/* close() operation for /sys/kernel/security/tomoyo/ interface. */ | |
2356 | ++static int tmy_release(struct inode *inode, struct file *file) | |
2357 | ++{ | |
2358 | ++ return tmy_close_control(file); | |
2359 | ++} | |
2360 | ++ | |
2361 | ++/* poll() operation for /sys/kernel/security/tomoyo/ interface. */ | |
2362 | ++static unsigned int tmy_poll(struct file *file, poll_table *wait) | |
2363 | ++{ | |
2364 | ++ return tmy_poll_control(file, wait); | |
2365 | ++} | |
2366 | ++ | |
2367 | ++/* read() operation for /sys/kernel/security/tomoyo/ interface. */ | |
2368 | ++static ssize_t tmy_read(struct file *file, char __user *buf, | |
2369 | ++ size_t count, loff_t *ppos) | |
2370 | ++{ | |
2371 | ++ return tmy_read_control(file, buf, count); | |
2372 | ++} | |
2373 | ++ | |
2374 | ++/* write() operation for /sys/kernel/security/tomoyo/ interface. */ | |
2375 | ++static ssize_t tmy_write(struct file *file, const char __user *buf, | |
2376 | ++ size_t count, loff_t *ppos) | |
2377 | ++{ | |
2378 | ++ return tmy_write_control(file, buf, count); | |
2379 | ++} | |
2380 | ++ | |
2381 | ++static struct file_operations tmy_operations = { | |
2382 | ++ .open = tmy_open, | |
2383 | ++ .release = tmy_release, | |
2384 | ++ .poll = tmy_poll, | |
2385 | ++ .read = tmy_read, | |
2386 | ++ .write = tmy_write | |
2387 | ++}; | |
2388 | ++ | |
2389 | ++/* Associate /sys/kernel/security/tomoyo/ interface with key. */ | |
2390 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
2391 | ++static void __init tmy_create_entry(const char *name, | |
2392 | ++ const mode_t mode, | |
2393 | ++ struct proc_dir_entry *parent, | |
2394 | ++ const int key) | |
2395 | ++{ | |
2396 | ++ struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); | |
2397 | ++ if (entry) { | |
2398 | ++ entry->proc_fops = &tmy_operations; | |
2399 | ++ entry->data = ((u8 *) NULL) + key; | |
2400 | ++ } | |
2401 | ++} | |
2402 | ++#else | |
2403 | ++static void __init tmy_create_entry(const char *name, | |
2404 | ++ const mode_t mode, | |
2405 | ++ struct dentry *parent, | |
2406 | ++ const int key) | |
2407 | ++{ | |
2408 | ++ securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | |
2409 | ++ &tmy_operations); | |
2410 | ++} | |
2411 | ++#endif | |
2412 | ++ | |
2413 | ++/** | |
2414 | ++ * tmy_interface_init - initialize /sys/kernel/security/tomoyo/ interface. | |
2415 | ++ */ | |
2416 | ++static int __init tmy_interface_init(void) | |
2417 | ++{ | |
2418 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
2419 | ++ struct proc_dir_entry *tmy_dir; | |
2420 | ++ tmy_dir = proc_mkdir("tomoyo", NULL); | |
2421 | ++#else | |
2422 | ++ struct dentry *tmy_dir; | |
2423 | ++ tmy_dir = securityfs_create_dir("tomoyo", NULL); | |
2424 | ++#endif | |
2425 | ++ tmy_create_entry("query", 0600, tmy_dir, | |
2426 | ++ TMY_QUERY); | |
2427 | ++ tmy_create_entry("domain_policy", 0600, tmy_dir, | |
2428 | ++ TMY_DOMAINPOLICY); | |
2429 | ++ tmy_create_entry("system_policy", 0600, tmy_dir, | |
2430 | ++ TMY_SYSTEMPOLICY); | |
2431 | ++ tmy_create_entry("exception_policy", 0600, tmy_dir, | |
2432 | ++ TMY_EXCEPTIONPOLICY); | |
2433 | ++ tmy_create_entry(".domain_status", 0600, tmy_dir, | |
2434 | ++ TMY_DOMAIN_STATUS); | |
2435 | ++ tmy_create_entry(".process_status", 0400, tmy_dir, | |
2436 | ++ TMY_PROCESS_STATUS); | |
2437 | ++ tmy_create_entry("self_domain", 0400, tmy_dir, | |
2438 | ++ TMY_SELFDOMAIN); | |
2439 | ++ tmy_create_entry("meminfo", 0400, tmy_dir, | |
2440 | ++ TMY_MEMINFO); | |
2441 | ++ tmy_create_entry("profile", 0600, tmy_dir, | |
2442 | ++ TMY_PROFILE); | |
2443 | ++ tmy_create_entry("manager", 0600, tmy_dir, | |
2444 | ++ TMY_MANAGER); | |
2445 | ++ tmy_create_entry(".updates_counter", 0400, tmy_dir, | |
2446 | ++ TMY_UPDATESCOUNTER); | |
2447 | ++ tmy_create_entry("version", 0400, tmy_dir, | |
2448 | ++ TMY_VERSION); | |
2449 | ++ tmy_create_entry("grant_log", 0400, tmy_dir, | |
2450 | ++ TMY_GRANT_LOG); | |
2451 | ++ tmy_create_entry("reject_log", 0400, tmy_dir, | |
2452 | ++ TMY_REJECT_LOG); | |
2453 | ++ return 0; | |
2454 | ++} | |
2455 | ++ | |
2456 | ++postcore_initcall(tmy_interface_init); |
@@ -0,0 +1,378 @@ | ||
1 | +LSM expansion for TOMOYO Linux. | |
2 | + | |
3 | +LSM hooks for sending signal: | |
4 | + * task_kill_unlocked is added in sys_kill | |
5 | + * task_tkill_unlocked is added in sys_tkill | |
6 | + * task_tgkill_unlocked is added in sys_tgkill | |
7 | +LSM hooks for network accept and recv: | |
8 | + * socket_post_accept is modified to return int. | |
9 | + * post_recv_datagram is added in skb_recv_datagram. | |
10 | + | |
11 | +You can try TOMOYO Linux without this patch, but in that case, you | |
12 | +can't use access control functionality for restricting signal | |
13 | +transmission and incoming network data. | |
14 | + | |
15 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
16 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
17 | + include/linux/security.h | 91 +++++++++++++++++++++++++++++++++++++++++++---- | |
18 | + kernel/signal.c | 17 ++++++++ | |
19 | + net/core/datagram.c | 22 +++++++++++ | |
20 | + net/socket.c | 7 ++- | |
21 | + security/dummy.c | 32 ++++++++++++++-- | |
22 | + 5 files changed, 157 insertions(+), 12 deletions(-) | |
23 | + | |
24 | +--- linux-2.6.23.orig/include/linux/security.h 2007-10-10 05:31:38.000000000 +0900 | |
25 | ++++ linux-2.6.23/include/linux/security.h 2007-11-01 17:16:15.000000000 +0900 | |
26 | +@@ -628,6 +628,22 @@ struct request_sock; | |
27 | + * @sig contains the signal value. | |
28 | + * @secid contains the sid of the process where the signal originated | |
29 | + * Return 0 if permission is granted. | |
30 | ++ * @task_kill_unlocked: | |
31 | ++ * Check permission before sending signal @sig to the process of @pid | |
32 | ++ * with sys_kill. | |
33 | ++ * @pid contains the pid of target process. | |
34 | ++ * @sig contains the signal value. | |
35 | ++ * @task_tkill_unlocked: | |
36 | ++ * Check permission before sending signal @sig to the process of @pid | |
37 | ++ * with sys_tkill. | |
38 | ++ * @pid contains the pid of target process. | |
39 | ++ * @sig contains the signal value. | |
40 | ++ * @task_tgkill_unlocked: | |
41 | ++ * Check permission before sending signal @sig to the process of @pid | |
42 | ++ * with sys_tgkill. | |
43 | ++ * @tgid contains the thread group id. | |
44 | ++ * @pid contains the pid of target process. | |
45 | ++ * @sig contains the signal value. | |
46 | + * @task_wait: | |
47 | + * Check permission before allowing a process to reap a child process @p | |
48 | + * and collect its status information. | |
49 | +@@ -749,8 +765,12 @@ struct request_sock; | |
50 | + * @socket_post_accept: | |
51 | + * This hook allows a security module to copy security | |
52 | + * information into the newly created socket's inode. | |
53 | ++ * This hook also allows a security module to filter connections | |
54 | ++ * from unwanted peers. | |
55 | ++ * The connection will be aborted if this hook returns nonzero. | |
56 | + * @sock contains the listening socket structure. | |
57 | + * @newsock contains the newly created server socket for connection. | |
58 | ++ * Return 0 if permission is granted. | |
59 | + * @socket_sendmsg: | |
60 | + * Check permission before transmitting a message to another socket. | |
61 | + * @sock contains the socket structure. | |
62 | +@@ -764,6 +784,11 @@ struct request_sock; | |
63 | + * @size contains the size of message structure. | |
64 | + * @flags contains the operational flags. | |
65 | + * Return 0 if permission is granted. | |
66 | ++ * @post_recv_datagram: | |
67 | ++ * Check permission after receiving a datagram. | |
68 | ++ * @sk contains the socket. | |
69 | ++ * @skb contains the socket buffer (may be NULL). | |
70 | ++ * @flags contains the operational flags. | |
71 | + * @socket_getsockname: | |
72 | + * Check permission before the local address (name) of the socket object | |
73 | + * @sock is retrieved. | |
74 | +@@ -1279,6 +1304,9 @@ struct security_operations { | |
75 | + int (*task_movememory) (struct task_struct * p); | |
76 | + int (*task_kill) (struct task_struct * p, | |
77 | + struct siginfo * info, int sig, u32 secid); | |
78 | ++ int (*task_kill_unlocked) (int pid, int sig); | |
79 | ++ int (*task_tkill_unlocked) (int pid, int sig); | |
80 | ++ int (*task_tgkill_unlocked) (int tgid, int pid, int sig); | |
81 | + int (*task_wait) (struct task_struct * p); | |
82 | + int (*task_prctl) (int option, unsigned long arg2, | |
83 | + unsigned long arg3, unsigned long arg4, | |
84 | +@@ -1346,12 +1374,16 @@ struct security_operations { | |
85 | + struct sockaddr * address, int addrlen); | |
86 | + int (*socket_listen) (struct socket * sock, int backlog); | |
87 | + int (*socket_accept) (struct socket * sock, struct socket * newsock); | |
88 | +- void (*socket_post_accept) (struct socket * sock, | |
89 | +- struct socket * newsock); | |
90 | ++#define TMY_LSM_EXPANSION | |
91 | ++ int (*socket_post_accept) (struct socket *sock, | |
92 | ++ struct socket *newsock); | |
93 | + int (*socket_sendmsg) (struct socket * sock, | |
94 | + struct msghdr * msg, int size); | |
95 | + int (*socket_recvmsg) (struct socket * sock, | |
96 | + struct msghdr * msg, int size, int flags); | |
97 | ++ int (*post_recv_datagram) (struct sock *sk, | |
98 | ++ struct sk_buff *skb, | |
99 | ++ unsigned int flags); | |
100 | + int (*socket_getsockname) (struct socket * sock); | |
101 | + int (*socket_getpeername) (struct socket * sock); | |
102 | + int (*socket_getsockopt) (struct socket * sock, int level, int optname); | |
103 | +@@ -1967,6 +1999,21 @@ static inline int security_task_kill (st | |
104 | + return security_ops->task_kill (p, info, sig, secid); | |
105 | + } | |
106 | + | |
107 | ++static inline int security_task_kill_unlocked(int pid, int sig) | |
108 | ++{ | |
109 | ++ return security_ops->task_kill_unlocked(pid, sig); | |
110 | ++} | |
111 | ++ | |
112 | ++static inline int security_task_tkill_unlocked(int pid, int sig) | |
113 | ++{ | |
114 | ++ return security_ops->task_tkill_unlocked(pid, sig); | |
115 | ++} | |
116 | ++ | |
117 | ++static inline int security_task_tgkill_unlocked(int tgid, int pid, int sig) | |
118 | ++{ | |
119 | ++ return security_ops->task_tgkill_unlocked(tgid, pid, sig); | |
120 | ++} | |
121 | ++ | |
122 | + static inline int security_task_wait (struct task_struct *p) | |
123 | + { | |
124 | + return security_ops->task_wait (p); | |
125 | +@@ -2645,6 +2692,21 @@ static inline int security_task_kill (st | |
126 | + return 0; | |
127 | + } | |
128 | + | |
129 | ++static inline int security_task_kill_unlocked(int pid, int sig) | |
130 | ++{ | |
131 | ++ return 0; | |
132 | ++} | |
133 | ++ | |
134 | ++static inline int security_task_tkill_unlocked(int pid, int sig) | |
135 | ++{ | |
136 | ++ return 0; | |
137 | ++} | |
138 | ++ | |
139 | ++static inline int security_task_tgkill_unlocked(int tgid, int pid, int sig) | |
140 | ++{ | |
141 | ++ return 0; | |
142 | ++} | |
143 | ++ | |
144 | + static inline int security_task_wait (struct task_struct *p) | |
145 | + { | |
146 | + return 0; | |
147 | +@@ -2870,10 +2932,10 @@ static inline int security_socket_accept | |
148 | + return security_ops->socket_accept(sock, newsock); | |
149 | + } | |
150 | + | |
151 | +-static inline void security_socket_post_accept(struct socket * sock, | |
152 | +- struct socket * newsock) | |
153 | ++static inline int security_socket_post_accept(struct socket *sock, | |
154 | ++ struct socket *newsock) | |
155 | + { | |
156 | +- security_ops->socket_post_accept(sock, newsock); | |
157 | ++ return security_ops->socket_post_accept(sock, newsock); | |
158 | + } | |
159 | + | |
160 | + static inline int security_socket_sendmsg(struct socket * sock, | |
161 | +@@ -2889,6 +2951,13 @@ static inline int security_socket_recvms | |
162 | + return security_ops->socket_recvmsg(sock, msg, size, flags); | |
163 | + } | |
164 | + | |
165 | ++static inline int security_post_recv_datagram(struct sock *sk, | |
166 | ++ struct sk_buff *skb, | |
167 | ++ unsigned int flags) | |
168 | ++{ | |
169 | ++ return security_ops->post_recv_datagram(sk, skb, flags); | |
170 | ++} | |
171 | ++ | |
172 | + static inline int security_socket_getsockname(struct socket * sock) | |
173 | + { | |
174 | + return security_ops->socket_getsockname(sock); | |
175 | +@@ -3033,9 +3102,10 @@ static inline int security_socket_accept | |
176 | + return 0; | |
177 | + } | |
178 | + | |
179 | +-static inline void security_socket_post_accept(struct socket * sock, | |
180 | +- struct socket * newsock) | |
181 | ++static inline int security_socket_post_accept(struct socket *sock, | |
182 | ++ struct socket *newsock) | |
183 | + { | |
184 | ++ return 0; | |
185 | + } | |
186 | + | |
187 | + static inline int security_socket_sendmsg(struct socket * sock, | |
188 | +@@ -3051,6 +3121,13 @@ static inline int security_socket_recvms | |
189 | + return 0; | |
190 | + } | |
191 | + | |
192 | ++static inline int security_post_recv_datagram(struct sock *sk, | |
193 | ++ struct sk_buff *skb, | |
194 | ++ unsigned int flags); | |
195 | ++{ | |
196 | ++ return 0; | |
197 | ++} | |
198 | ++ | |
199 | + static inline int security_socket_getsockname(struct socket * sock) | |
200 | + { | |
201 | + return 0; | |
202 | +--- linux-2.6.23.orig/kernel/signal.c 2007-10-10 05:31:38.000000000 +0900 | |
203 | ++++ linux-2.6.23/kernel/signal.c 2007-11-01 17:16:15.000000000 +0900 | |
204 | +@@ -2196,6 +2196,11 @@ asmlinkage long | |
205 | + sys_kill(int pid, int sig) | |
206 | + { | |
207 | + struct siginfo info; | |
208 | ++ int ret; | |
209 | ++ | |
210 | ++ ret = security_task_kill_unlocked(pid, sig); | |
211 | ++ if (ret) | |
212 | ++ return ret; | |
213 | + | |
214 | + info.si_signo = sig; | |
215 | + info.si_errno = 0; | |
216 | +@@ -2251,10 +2256,16 @@ static int do_tkill(int tgid, int pid, i | |
217 | + */ | |
218 | + asmlinkage long sys_tgkill(int tgid, int pid, int sig) | |
219 | + { | |
220 | ++ int ret; | |
221 | ++ | |
222 | + /* This is only valid for single tasks */ | |
223 | + if (pid <= 0 || tgid <= 0) | |
224 | + return -EINVAL; | |
225 | + | |
226 | ++ ret = security_task_tgkill_unlocked(tgid, pid, sig); | |
227 | ++ if (ret) | |
228 | ++ return ret; | |
229 | ++ | |
230 | + return do_tkill(tgid, pid, sig); | |
231 | + } | |
232 | + | |
233 | +@@ -2264,10 +2275,16 @@ asmlinkage long sys_tgkill(int tgid, int | |
234 | + asmlinkage long | |
235 | + sys_tkill(int pid, int sig) | |
236 | + { | |
237 | ++ int ret; | |
238 | ++ | |
239 | + /* This is only valid for single tasks */ | |
240 | + if (pid <= 0) | |
241 | + return -EINVAL; | |
242 | + | |
243 | ++ ret = security_task_tkill_unlocked(pid, sig); | |
244 | ++ if (ret) | |
245 | ++ return ret; | |
246 | ++ | |
247 | + return do_tkill(0, pid, sig); | |
248 | + } | |
249 | + | |
250 | +--- linux-2.6.23.orig/net/socket.c 2007-10-10 05:31:38.000000000 +0900 | |
251 | ++++ linux-2.6.23/net/socket.c 2007-11-01 17:16:15.000000000 +0900 | |
252 | +@@ -1434,13 +1434,16 @@ asmlinkage long sys_accept(int fd, struc | |
253 | + goto out_fd; | |
254 | + } | |
255 | + | |
256 | ++ /* Filter connections from unwanted peers like TCP Wrapper. */ | |
257 | ++ err = security_socket_post_accept(sock, newsock); | |
258 | ++ if (err) | |
259 | ++ goto out_fd; | |
260 | ++ | |
261 | + /* File flags are not inherited via accept() unlike another OSes. */ | |
262 | + | |
263 | + fd_install(newfd, newfile); | |
264 | + err = newfd; | |
265 | + | |
266 | +- security_socket_post_accept(sock, newsock); | |
267 | +- | |
268 | + out_put: | |
269 | + fput_light(sock->file, fput_needed); | |
270 | + out: | |
271 | +--- linux-2.6.23.orig/security/dummy.c 2007-10-10 05:31:38.000000000 +0900 | |
272 | ++++ linux-2.6.23/security/dummy.c 2007-11-01 17:16:15.000000000 +0900 | |
273 | +@@ -564,6 +564,21 @@ static int dummy_task_kill (struct task_ | |
274 | + return 0; | |
275 | + } | |
276 | + | |
277 | ++static int dummy_task_kill_unlocked(int pid, int sig) | |
278 | ++{ | |
279 | ++ return 0; | |
280 | ++} | |
281 | ++ | |
282 | ++static int dummy_task_tkill_unlocked(int pid, int sig) | |
283 | ++{ | |
284 | ++ return 0; | |
285 | ++} | |
286 | ++ | |
287 | ++static int dummy_task_tgkill_unlocked(int tgid, int pid, int sig) | |
288 | ++{ | |
289 | ++ return 0; | |
290 | ++} | |
291 | ++ | |
292 | + static int dummy_task_prctl (int option, unsigned long arg2, unsigned long arg3, | |
293 | + unsigned long arg4, unsigned long arg5) | |
294 | + { | |
295 | +@@ -741,10 +756,10 @@ static int dummy_socket_accept (struct s | |
296 | + return 0; | |
297 | + } | |
298 | + | |
299 | +-static void dummy_socket_post_accept (struct socket *sock, | |
300 | +- struct socket *newsock) | |
301 | ++static int dummy_socket_post_accept(struct socket *sock, | |
302 | ++ struct socket *newsock) | |
303 | + { | |
304 | +- return; | |
305 | ++ return 0; | |
306 | + } | |
307 | + | |
308 | + static int dummy_socket_sendmsg (struct socket *sock, struct msghdr *msg, | |
309 | +@@ -759,6 +774,13 @@ static int dummy_socket_recvmsg (struct | |
310 | + return 0; | |
311 | + } | |
312 | + | |
313 | ++static int dummy_post_recv_datagram(struct sock *sk, | |
314 | ++ struct sk_buff *skb, | |
315 | ++ unsigned int flags) | |
316 | ++{ | |
317 | ++ return 0; | |
318 | ++} | |
319 | ++ | |
320 | + static int dummy_socket_getsockname (struct socket *sock) | |
321 | + { | |
322 | + return 0; | |
323 | +@@ -1053,6 +1075,9 @@ void security_fixup_ops (struct security | |
324 | + set_to_dummy_if_null(ops, task_movememory); | |
325 | + set_to_dummy_if_null(ops, task_wait); | |
326 | + set_to_dummy_if_null(ops, task_kill); | |
327 | ++ set_to_dummy_if_null(ops, task_kill_unlocked); | |
328 | ++ set_to_dummy_if_null(ops, task_tkill_unlocked); | |
329 | ++ set_to_dummy_if_null(ops, task_tgkill_unlocked); | |
330 | + set_to_dummy_if_null(ops, task_prctl); | |
331 | + set_to_dummy_if_null(ops, task_reparent_to_init); | |
332 | + set_to_dummy_if_null(ops, task_to_inode); | |
333 | +@@ -1096,6 +1121,7 @@ void security_fixup_ops (struct security | |
334 | + set_to_dummy_if_null(ops, socket_post_accept); | |
335 | + set_to_dummy_if_null(ops, socket_sendmsg); | |
336 | + set_to_dummy_if_null(ops, socket_recvmsg); | |
337 | ++ set_to_dummy_if_null(ops, post_recv_datagram); | |
338 | + set_to_dummy_if_null(ops, socket_getsockname); | |
339 | + set_to_dummy_if_null(ops, socket_getpeername); | |
340 | + set_to_dummy_if_null(ops, socket_setsockopt); | |
341 | +--- linux-2.6.23.orig/net/core/datagram.c 2007-10-10 05:31:38.000000000 +0900 | |
342 | ++++ linux-2.6.23/net/core/datagram.c 2007-11-01 17:16:15.000000000 +0900 | |
343 | +@@ -55,6 +55,7 @@ | |
344 | + #include <net/checksum.h> | |
345 | + #include <net/sock.h> | |
346 | + #include <net/tcp_states.h> | |
347 | ++#include <linux/security.h> | |
348 | + | |
349 | + /* | |
350 | + * Is a socket 'connection oriented' ? | |
351 | +@@ -178,6 +179,27 @@ struct sk_buff *skb_recv_datagram(struct | |
352 | + } else | |
353 | + skb = skb_dequeue(&sk->sk_receive_queue); | |
354 | + | |
355 | ++ error = security_post_recv_datagram(sk, skb, flags); | |
356 | ++ if (error) { | |
357 | ++ unsigned long cpu_flags; | |
358 | ++ | |
359 | ++ if (!(flags & MSG_PEEK)) | |
360 | ++ goto no_peek; | |
361 | ++ | |
362 | ++ spin_lock_irqsave(&sk->sk_receive_queue.lock, | |
363 | ++ cpu_flags); | |
364 | ++ if (skb == skb_peek(&sk->sk_receive_queue)) { | |
365 | ++ __skb_unlink(skb, | |
366 | ++ &sk->sk_receive_queue); | |
367 | ++ atomic_dec(&skb->users); | |
368 | ++ } | |
369 | ++ spin_unlock_irqrestore(&sk->sk_receive_queue.lock, | |
370 | ++ cpu_flags); | |
371 | ++no_peek: | |
372 | ++ skb_free_datagram(sk, skb); | |
373 | ++ goto no_packet; | |
374 | ++ } | |
375 | ++ | |
376 | + if (skb) | |
377 | + return skb; | |
378 | + |
@@ -0,0 +1,682 @@ | ||
1 | +Basic functions to get canonicalized absolute pathnames | |
2 | +for TOMOYO Linux. Even the requested pathname is symlink()ed | |
3 | +or chroot()ed, TOMOYO Linux uses the original pathname. | |
4 | + | |
5 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
6 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
7 | + security/tomoyo/realpath.c | 670 +++++++++++++++++++++++++++++++++++++++++++++ | |
8 | + 1 file changed, 670 insertions(+) | |
9 | + | |
10 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
11 | ++++ linux-2.6.23/security/tomoyo/realpath.c 2007-11-02 13:48:04.000000000 +0900 | |
12 | +@@ -0,0 +1,670 @@ | |
13 | ++/* | |
14 | ++ * security/tomoyo/realpath.c | |
15 | ++ * | |
16 | ++ * Get the canonicalized absolute pathnames. | |
17 | ++ * The basis for TOMOYO Linux. | |
18 | ++ */ | |
19 | ++ | |
20 | ++#include "tomoyo.h" | |
21 | ++#include "realpath.h" | |
22 | ++ | |
23 | ++/***** realpath handler *****/ | |
24 | ++ | |
25 | ++static int tmy_print_ascii(const char *sp, const char *cp, | |
26 | ++ int *buflen0, char **end0) | |
27 | ++{ | |
28 | ++ int buflen = *buflen0; | |
29 | ++ char *end = *end0; | |
30 | ++ | |
31 | ++ while (sp <= cp) { | |
32 | ++ unsigned char c; | |
33 | ++ | |
34 | ++ c = *(unsigned char *) cp; | |
35 | ++ if (c == '\\') { | |
36 | ++ buflen -= 2; | |
37 | ++ if (buflen < 0) | |
38 | ++ goto out; | |
39 | ++ *--end = '\\'; | |
40 | ++ *--end = '\\'; | |
41 | ++ } else if (c > ' ' && c < 127) { | |
42 | ++ if (--buflen < 0) | |
43 | ++ goto out; | |
44 | ++ *--end = (char) c; | |
45 | ++ } else { | |
46 | ++ buflen -= 4; | |
47 | ++ if (buflen < 0) | |
48 | ++ goto out; | |
49 | ++ *--end = (c & 7) + '0'; | |
50 | ++ *--end = ((c >> 3) & 7) + '0'; | |
51 | ++ *--end = (c >> 6) + '0'; | |
52 | ++ *--end = '\\'; | |
53 | ++ } | |
54 | ++ cp--; | |
55 | ++ } | |
56 | ++ | |
57 | ++ *buflen0 = buflen; | |
58 | ++ *end0 = end; | |
59 | ++ | |
60 | ++ return 0; | |
61 | ++out: ; | |
62 | ++ return -ENOMEM; | |
63 | ++} | |
64 | ++ | |
65 | ++/** | |
66 | ++ * tmy_get_absolute_path - return the realpath of a dentry. | |
67 | ++ * @dentry: pointer to "struct dentry". | |
68 | ++ * @vfsmnt: pointer to "struct vfsmount" to which the @dentry belongs. | |
69 | ++ * @buffer: size of buffer to save the result. | |
70 | ++ * @buflen: size of @buffer . | |
71 | ++ * | |
72 | ++ * Returns zero on success. | |
73 | ++ * Returns nonzero on failure. | |
74 | ++ * | |
75 | ++ * Caller holds the dcache_lock. | |
76 | ++ * Based on __d_path() in fs/dcache.c | |
77 | ++ * | |
78 | ++ * Unlike d_path(), this function traverses upto the root directory of | |
79 | ++ * process's namespace. | |
80 | ++ * | |
81 | ++ * If @dentry is a directory, trailing '/' is appended. | |
82 | ++ * Characters other than ' ' < c < 127 are converted to \ooo style octal string. | |
83 | ++ * Character \ is converted to \\ string. | |
84 | ++ */ | |
85 | ++static int tmy_get_absolute_path(struct dentry *dentry, | |
86 | ++ struct vfsmount *vfsmnt, | |
87 | ++ char *buffer, | |
88 | ++ int buflen) | |
89 | ++{ | |
90 | ++ char *start = buffer; | |
91 | ++ char *end = buffer + buflen; | |
92 | ++ bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)); | |
93 | ++ const char *sp; | |
94 | ++ const char *cp; | |
95 | ++ | |
96 | ++ if (buflen < 256) | |
97 | ++ goto out; | |
98 | ++ | |
99 | ++ *--end = '\0'; | |
100 | ++ buflen--; | |
101 | ++ | |
102 | ++ while (1) { | |
103 | ++ struct dentry *parent; | |
104 | ++ | |
105 | ++ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
106 | ++ /* Global root? */ | |
107 | ++ spin_lock(&vfsmount_lock); | |
108 | ++ if (vfsmnt->mnt_parent == vfsmnt) { | |
109 | ++ spin_unlock(&vfsmount_lock); | |
110 | ++ break; | |
111 | ++ } | |
112 | ++ dentry = vfsmnt->mnt_mountpoint; | |
113 | ++ vfsmnt = vfsmnt->mnt_parent; | |
114 | ++ spin_unlock(&vfsmount_lock); | |
115 | ++ continue; | |
116 | ++ } | |
117 | ++ | |
118 | ++ if (is_dir) { | |
119 | ++ is_dir = 0; | |
120 | ++ *--end = '/'; | |
121 | ++ buflen--; | |
122 | ++ } | |
123 | ++ | |
124 | ++ parent = dentry->d_parent; | |
125 | ++ sp = dentry->d_name.name; | |
126 | ++ cp = sp + dentry->d_name.len - 1; | |
127 | ++ | |
128 | ++ /* Exception: Use /proc/self/ rather than */ | |
129 | ++ /* /proc/\$/ for current process. */ | |
130 | ++ if (IS_ROOT(parent) && | |
131 | ++ *sp > '0' && *sp <= '9' && parent->d_sb && | |
132 | ++ parent->d_sb->s_magic == PROC_SUPER_MAGIC) { | |
133 | ++ | |
134 | ++ char *ep; | |
135 | ++ const pid_t pid = (pid_t) simple_strtoul(sp, &ep, 10); | |
136 | ++ | |
137 | ++ if (!*ep && pid == current->tgid) { | |
138 | ++ sp = "self"; | |
139 | ++ cp = sp + 3; | |
140 | ++ } | |
141 | ++ | |
142 | ++ } | |
143 | ++ | |
144 | ++ if (tmy_print_ascii(sp, cp, &buflen, &end)) | |
145 | ++ goto out; | |
146 | ++ | |
147 | ++ if (--buflen < 0) | |
148 | ++ goto out; | |
149 | ++ *--end = '/'; | |
150 | ++ | |
151 | ++ dentry = parent; | |
152 | ++ } | |
153 | ++ if (*end == '/') { | |
154 | ++ buflen++; | |
155 | ++ end++; | |
156 | ++ } | |
157 | ++ | |
158 | ++ sp = dentry->d_name.name; | |
159 | ++ cp = sp + dentry->d_name.len - 1; | |
160 | ++ | |
161 | ++ if (tmy_print_ascii(sp, cp, &buflen, &end)) | |
162 | ++ goto out; | |
163 | ++ | |
164 | ++ /* Move the pathname to the top of the buffer. */ | |
165 | ++ memmove(start, end, strlen(end) + 1); | |
166 | ++ return 0; | |
167 | ++out: ; | |
168 | ++ return -ENOMEM; | |
169 | ++} | |
170 | ++ | |
171 | ++/** | |
172 | ++ * tmy_realpath_dentry2 - return the realpath of a dentry. | |
173 | ++ * @dentry: pointer to "struct dentry". | |
174 | ++ * @mnt: pointer to "struct vfsmount" to which the @dentry belongs. | |
175 | ++ * @newname: buffer to save the result. | |
176 | ++ * @newname_len: size of @newname . | |
177 | ++ * | |
178 | ++ * Returns zero on success. | |
179 | ++ * Returns nonzero on failure. | |
180 | ++ */ | |
181 | ++int tmy_realpath_dentry2(struct dentry *dentry, | |
182 | ++ struct vfsmount *mnt, | |
183 | ++ char *newname, | |
184 | ++ int newname_len) | |
185 | ++{ | |
186 | ++ int error; | |
187 | ++ struct dentry *d_dentry; | |
188 | ++ struct vfsmount *d_mnt; | |
189 | ++ | |
190 | ++ if (!dentry || !mnt || !newname || newname_len <= 0) | |
191 | ++ return -EINVAL; | |
192 | ++ | |
193 | ++ d_dentry = dget(dentry); | |
194 | ++ d_mnt = mntget(mnt); | |
195 | ++ | |
196 | ++ /***** CRITICAL SECTION START *****/ | |
197 | ++ spin_lock(&dcache_lock); | |
198 | ++ error = tmy_get_absolute_path(d_dentry, d_mnt, newname, newname_len); | |
199 | ++ spin_unlock(&dcache_lock); | |
200 | ++ /***** CRITICAL SECTION END *****/ | |
201 | ++ | |
202 | ++ dput(d_dentry); | |
203 | ++ mntput(d_mnt); | |
204 | ++ return error; | |
205 | ++} | |
206 | ++ | |
207 | ++/** | |
208 | ++ * tmy_realpath_dentry - return the realpath of a dentry. | |
209 | ++ * @dentry: pointer to "struct dentry". | |
210 | ++ * @mnt: pointer to "struct vfsmount" to which the @dentry belongs. | |
211 | ++ * | |
212 | ++ * Returns realpath(3) of the @dentry on success. | |
213 | ++ * Returns NULL on failure. | |
214 | ++ * | |
215 | ++ * This function uses tmy_alloc(), so caller must call tmy_free() | |
216 | ++ * if this function didn't return NULL. | |
217 | ++ */ | |
218 | ++char *tmy_realpath_dentry(struct dentry *dentry, struct vfsmount *mnt) | |
219 | ++{ | |
220 | ++ char *buf = tmy_alloc(TMY_MAX_PATHNAME_LEN); | |
221 | ++ | |
222 | ++ if (buf && | |
223 | ++ tmy_realpath_dentry2(dentry, mnt, buf, | |
224 | ++ TMY_MAX_PATHNAME_LEN - 1) == 0) | |
225 | ++ return buf; | |
226 | ++ | |
227 | ++ tmy_free(buf); | |
228 | ++ return NULL; | |
229 | ++} | |
230 | ++ | |
231 | ++/** | |
232 | ++ * tmy_realpath - return the realpath of a pathname. | |
233 | ++ * @pathname: pathname to report. | |
234 | ++ * | |
235 | ++ * Returns realpath(3) of the @pathname on success. | |
236 | ++ * Returns NULL on failure. | |
237 | ++ * | |
238 | ++ * This function uses tmy_alloc(), so caller must call tmy_free() | |
239 | ++ * if this function didn't return NULL. | |
240 | ++ */ | |
241 | ++char *tmy_realpath(const char *pathname) | |
242 | ++{ | |
243 | ++ struct nameidata nd; | |
244 | ++ | |
245 | ++ if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) { | |
246 | ++ char *buf = tmy_realpath_dentry(nd.dentry, nd.mnt); | |
247 | ++ | |
248 | ++ path_release(&nd); | |
249 | ++ return buf; | |
250 | ++ } | |
251 | ++ | |
252 | ++ return NULL; | |
253 | ++} | |
254 | ++ | |
255 | ++/** | |
256 | ++ * tmy_realpath_nofollow - return the realpath of a pathname. | |
257 | ++ * @pathname: pathname to report. | |
258 | ++ * | |
259 | ++ * Returns realpath(3) of the @pathname on success. | |
260 | ++ * Returns NULL on failure. | |
261 | ++ * | |
262 | ++ * Unlike tmy_realpath(), this function doesn't follow if @pathname is | |
263 | ++ * a symbolic link. | |
264 | ++ * | |
265 | ++ * This function uses tmy_alloc(), so caller must call tmy_free() | |
266 | ++ * if this function didn't return NULL. | |
267 | ++ */ | |
268 | ++char *tmy_realpath_nofollow(const char *pathname) | |
269 | ++{ | |
270 | ++ struct nameidata nd; | |
271 | ++ | |
272 | ++ if (pathname && path_lookup(pathname, 0, &nd) == 0) { | |
273 | ++ char *buf = tmy_realpath_dentry(nd.dentry, nd.mnt); | |
274 | ++ | |
275 | ++ path_release(&nd); | |
276 | ++ return buf; | |
277 | ++ } | |
278 | ++ | |
279 | ++ return NULL; | |
280 | ++} | |
281 | ++ | |
282 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) | |
283 | ++/* tmy_get_absolute_path() for "struct ctl_table". */ | |
284 | ++static int tmy_sysctl_path(struct ctl_table *table, char *buffer, int buflen) | |
285 | ++{ | |
286 | ++ char *end = buffer + buflen; | |
287 | ++ | |
288 | ++ if (buflen < 256) | |
289 | ++ goto out; | |
290 | ++ | |
291 | ++ *--end = '\0'; | |
292 | ++ buflen--; | |
293 | ++ | |
294 | ++ buflen -= 9; /* for "/proc/sys" prefix */ | |
295 | ++ | |
296 | ++ while (table) { | |
297 | ++ char buf[32]; | |
298 | ++ const char *sp = table->procname; | |
299 | ++ const char *cp; | |
300 | ++ | |
301 | ++ if (!sp) { | |
302 | ++ memset(buf, 0, sizeof(buf)); | |
303 | ++ snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name); | |
304 | ++ sp = buf; | |
305 | ++ } | |
306 | ++ cp = strchr(sp, '\0') - 1; | |
307 | ++ | |
308 | ++ if (tmy_print_ascii(sp, cp, &buflen, &end)) | |
309 | ++ goto out; | |
310 | ++ | |
311 | ++ if (--buflen < 0) | |
312 | ++ goto out; | |
313 | ++ | |
314 | ++ *--end = '/'; | |
315 | ++ table = table->parent; | |
316 | ++ } | |
317 | ++ | |
318 | ++ /* Move the pathname to the top of the buffer. */ | |
319 | ++ memmove(buffer, "/proc/sys", 9); | |
320 | ++ memmove(buffer + 9, end, strlen(end) + 1); | |
321 | ++ return 0; | |
322 | ++out: ; | |
323 | ++ return -ENOMEM; | |
324 | ++} | |
325 | ++ | |
326 | ++/** | |
327 | ++ * sysctlpath_from_table - return the realpath of a ctl_table. | |
328 | ++ * @table: pointer to "struct ctl_table". | |
329 | ++ * | |
330 | ++ * Returns realpath(3) of the @table on success. | |
331 | ++ * Returns NULL on failure. | |
332 | ++ * | |
333 | ++ * This function uses tmy_alloc(), so caller must call tmy_free() | |
334 | ++ * if this function didn't return NULL. | |
335 | ++ */ | |
336 | ++char *sysctlpath_from_table(struct ctl_table *table) | |
337 | ++{ | |
338 | ++ char *buf = tmy_alloc(TMY_MAX_PATHNAME_LEN); | |
339 | ++ | |
340 | ++ if (buf && tmy_sysctl_path(table, buf, TMY_MAX_PATHNAME_LEN - 1) == 0) | |
341 | ++ return buf; | |
342 | ++ | |
343 | ++ tmy_free(buf); | |
344 | ++ return NULL; | |
345 | ++} | |
346 | ++#endif | |
347 | ++ | |
348 | ++/***** Private memory allocator. *****/ | |
349 | ++ | |
350 | ++/* | |
351 | ++ * Round up an integer so that the returned pointers are appropriately aligned. | |
352 | ++ * FIXME: Are there more requirements that is needed for assigning value | |
353 | ++ * atomically? | |
354 | ++ */ | |
355 | ++static inline unsigned int tmy_roundup(const unsigned int size) | |
356 | ++{ | |
357 | ++ if (sizeof(void *) >= sizeof(long)) | |
358 | ++ return ((size + sizeof(void *) - 1) / sizeof(void *)) | |
359 | ++ * sizeof(void *); | |
360 | ++ | |
361 | ++ return ((size + sizeof(long) - 1) / sizeof(long)) * sizeof(long); | |
362 | ++} | |
363 | ++ | |
364 | ++static unsigned int allocated_memory_for_elements; | |
365 | ++ | |
366 | ++/** | |
367 | ++ * tmy_get_memory_used_for_elements - get memory usage for private data. | |
368 | ++ * | |
369 | ++ * Returns size of memory allocated for keeping individual ACL elements. | |
370 | ++ */ | |
371 | ++unsigned int tmy_get_memory_used_for_elements(void) | |
372 | ++{ | |
373 | ++ return allocated_memory_for_elements; | |
374 | ++} | |
375 | ++ | |
376 | ++/** | |
377 | ++ * tmy_alloc_element - allocate memory for structures. | |
378 | ++ * @size: requested size in bytes. | |
379 | ++ * | |
380 | ++ * Returns '\0'-initialized memory region on success. | |
381 | ++ * Returns NULL on failure. | |
382 | ++ * | |
383 | ++ * This function allocates memory for keeping ACL entries. | |
384 | ++ * The RAM is chunked, so NEVER try to kfree() the returned pointer. | |
385 | ++ */ | |
386 | ++void *tmy_alloc_element(const unsigned int size) | |
387 | ++{ | |
388 | ++ static DEFINE_MUTEX(mutex); | |
389 | ++ static char *buf; | |
390 | ++ static unsigned int buf_used_len = PAGE_SIZE; | |
391 | ++ char *ptr = NULL; | |
392 | ++ const unsigned int word_aligned_size = tmy_roundup(size); | |
393 | ++ | |
394 | ++ if (word_aligned_size > PAGE_SIZE) | |
395 | ++ return NULL; | |
396 | ++ | |
397 | ++ mutex_lock(&mutex); | |
398 | ++ | |
399 | ++ if (buf_used_len + word_aligned_size > PAGE_SIZE) { | |
400 | ++ ptr = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
401 | ++ | |
402 | ++ if (!ptr) { | |
403 | ++ printk(KERN_INFO "ERROR: " | |
404 | ++ "Out of memory for tmy_alloc_element().\n"); | |
405 | ++ if (!sbin_init_started) | |
406 | ++ panic("MAC Initialization failed.\n"); | |
407 | ++ } else { | |
408 | ++ buf = ptr; | |
409 | ++ allocated_memory_for_elements += PAGE_SIZE; | |
410 | ++ buf_used_len = word_aligned_size; | |
411 | ++ ptr = buf; | |
412 | ++ } | |
413 | ++ | |
414 | ++ } else if (word_aligned_size) { | |
415 | ++ unsigned int i; | |
416 | ++ | |
417 | ++ ptr = buf + buf_used_len; | |
418 | ++ buf_used_len += word_aligned_size; | |
419 | ++ | |
420 | ++ for (i = 0; i < word_aligned_size; i++) { | |
421 | ++ if (ptr[i]) { /* This must not happen! */ | |
422 | ++ printk(KERN_ERR | |
423 | ++ "WARNING: Reserved memory was tainted! " | |
424 | ++ "The system might go wrong.\n"); | |
425 | ++ ptr[i] = '\0'; | |
426 | ++ } | |
427 | ++ } | |
428 | ++ | |
429 | ++ } | |
430 | ++ | |
431 | ++ mutex_unlock(&mutex); | |
432 | ++ return ptr; | |
433 | ++} | |
434 | ++ | |
435 | ++/***** Shared memory allocator. *****/ | |
436 | ++ | |
437 | ++static unsigned int allocated_memory_for_savename; | |
438 | ++ | |
439 | ++/** | |
440 | ++ * tmy_get_memory_used_for_save_name - get memory usage for shared data. | |
441 | ++ * | |
442 | ++ * Returns size of memory allocated for keeping string tokens. | |
443 | ++ */ | |
444 | ++unsigned int tmy_get_memory_used_for_save_name(void) | |
445 | ++{ | |
446 | ++ return allocated_memory_for_savename; | |
447 | ++} | |
448 | ++ | |
449 | ++#define MAX_HASH 256 | |
450 | ++ | |
451 | ++/* List of tokens. */ | |
452 | ++struct name_entry { | |
453 | ++ struct list_head list; | |
454 | ++ struct path_info entry; | |
455 | ++}; | |
456 | ++ | |
457 | ++/* List of free memory. */ | |
458 | ++struct free_memory_block { | |
459 | ++ struct list_head list; | |
460 | ++ char *ptr; /* Pointer to a free area. */ | |
461 | ++ int len; /* Length of the area. */ | |
462 | ++}; | |
463 | ++ | |
464 | ++/** | |
465 | ++ * tmy_save_name - keep the given token on the RAM. | |
466 | ++ * @name: the string token to remember. | |
467 | ++ * | |
468 | ++ * Returns pointer to memory region on success. | |
469 | ++ * Returns NULL on failure. | |
470 | ++ * | |
471 | ++ * This function allocates memory for keeping any string data. | |
472 | ++ * The RAM is shared, so NEVER try to modify or kfree() the returned name. | |
473 | ++ */ | |
474 | ++const struct path_info *tmy_save_name(const char *name) | |
475 | ++{ | |
476 | ++ static bool first_call = 1; | |
477 | ++ static DEFINE_MUTEX(mutex); | |
478 | ++ static LIST_HEAD(fmb_list); | |
479 | ++ struct free_memory_block *fmb; | |
480 | ++ static struct list_head name_list[MAX_HASH]; /* The list of names. */ | |
481 | ++ struct name_entry *ptr; | |
482 | ++ unsigned int hash; | |
483 | ++ int len; | |
484 | ++ bool found = 0; | |
485 | ++ if (!name) | |
486 | ++ return NULL; | |
487 | ++ len = strlen(name) + 1; | |
488 | ++ if (len > TMY_MAX_PATHNAME_LEN) { | |
489 | ++ printk(KERN_INFO "ERROR: Name too long for tmy_save_name().\n"); | |
490 | ++ return NULL; | |
491 | ++ } | |
492 | ++ hash = full_name_hash((const unsigned char *) name, len - 1); | |
493 | ++ /* List access in this function is protected by mutex. */ | |
494 | ++ mutex_lock(&mutex); | |
495 | ++ if (first_call) { | |
496 | ++ int i; | |
497 | ++ first_call = 0; | |
498 | ++ for (i = 0; i < MAX_HASH; i++) | |
499 | ++ INIT_LIST_HEAD(&name_list[i]); | |
500 | ++ if (TMY_MAX_PATHNAME_LEN > PAGE_SIZE) | |
501 | ++ panic("Bad size."); | |
502 | ++ } | |
503 | ++ list_for_each_entry(ptr, &name_list[hash % MAX_HASH], list) { | |
504 | ++ if (hash == ptr->entry.hash && | |
505 | ++ strcmp(name, ptr->entry.name) == 0) | |
506 | ++ goto out; | |
507 | ++ } | |
508 | ++ list_for_each_entry(fmb, &fmb_list, list) { | |
509 | ++ if (len <= fmb->len) { | |
510 | ++ found = 1; | |
511 | ++ break; | |
512 | ++ } | |
513 | ++ } | |
514 | ++ if (!found) { | |
515 | ++ char *cp; | |
516 | ++ cp = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
517 | ++ if (!cp) | |
518 | ++ goto out2; | |
519 | ++ fmb = kzalloc(sizeof(*fmb), GFP_KERNEL); | |
520 | ++ if (!fmb) { | |
521 | ++ kfree(cp); | |
522 | ++out2: ; | |
523 | ++ printk(KERN_INFO | |
524 | ++ "ERROR: Out of memory for tmy_save_name().\n"); | |
525 | ++ if (!sbin_init_started) | |
526 | ++ panic("MAC Initialization failed.\n"); | |
527 | ++ ptr = NULL; | |
528 | ++ goto out; | |
529 | ++ } | |
530 | ++ allocated_memory_for_savename += PAGE_SIZE; | |
531 | ++ fmb->ptr = cp; | |
532 | ++ fmb->len = PAGE_SIZE; | |
533 | ++ list_add_tail(&fmb->list, &fmb_list); | |
534 | ++ } | |
535 | ++ ptr = tmy_alloc_element(sizeof(*ptr)); | |
536 | ++ if (!ptr) | |
537 | ++ goto out; | |
538 | ++ ptr->entry.name = fmb->ptr; | |
539 | ++ memmove(fmb->ptr, name, len); | |
540 | ++ tmy_fill_path_info(&ptr->entry); | |
541 | ++ fmb->ptr += len; | |
542 | ++ fmb->len -= len; | |
543 | ++ list_add_tail(&ptr->list, &name_list[hash % MAX_HASH]); | |
544 | ++ if (fmb->len == 0) { | |
545 | ++ list_del(&fmb->list); /* Protected by mutex. */ | |
546 | ++ kfree(fmb); | |
547 | ++ } | |
548 | ++out: ; | |
549 | ++ mutex_unlock(&mutex); | |
550 | ++ return ptr ? &ptr->entry : NULL; | |
551 | ++} | |
552 | ++ | |
553 | ++/***** Dynamic memory allocator. *****/ | |
554 | ++ | |
555 | ++/* List of temporary memory blocks. */ | |
556 | ++struct cache_entry { | |
557 | ++ struct list_head list; | |
558 | ++ void *ptr; | |
559 | ++ int size; | |
560 | ++}; | |
561 | ++ | |
562 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | |
563 | ++static struct kmem_cache *tmy_cachep; | |
564 | ++#else | |
565 | ++static kmem_cache_t *tmy_cachep; | |
566 | ++#endif | |
567 | ++ | |
568 | ++/** | |
569 | ++ * tmy_realpath_init - initialize memory allocator. | |
570 | ++ */ | |
571 | ++static int __init tmy_realpath_init(void) | |
572 | ++{ | |
573 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) | |
574 | ++ tmy_cachep = kmem_cache_create("tomoyo_cache", | |
575 | ++ sizeof(struct cache_entry), | |
576 | ++ 0, SLAB_PANIC, NULL); | |
577 | ++#else | |
578 | ++ tmy_cachep = kmem_cache_create("tomoyo_cache", | |
579 | ++ sizeof(struct cache_entry), | |
580 | ++ 0, SLAB_PANIC, NULL, NULL); | |
581 | ++#endif | |
582 | ++ return 0; | |
583 | ++} | |
584 | ++postcore_initcall(tmy_realpath_init); | |
585 | ++ | |
586 | ++static LIST_HEAD(cache_list); | |
587 | ++static DEFINE_SPINLOCK(cache_list_lock); | |
588 | ++static unsigned int dynamic_memory_size; | |
589 | ++ | |
590 | ++/** | |
591 | ++ * tmy_get_memory_used_for_dynamic - get memory usage for temporary purpose. | |
592 | ++ * | |
593 | ++ * Returns size of memory allocated for keeping temporary purpose. | |
594 | ++ */ | |
595 | ++unsigned int tmy_get_memory_used_for_dynamic(void) | |
596 | ++{ | |
597 | ++ return dynamic_memory_size; | |
598 | ++} | |
599 | ++ | |
600 | ++/** | |
601 | ++ * tmy_alloc - allocate memory for temporary purpose. | |
602 | ++ * @size: requested size in bytes. | |
603 | ++ * | |
604 | ++ * Returns '\0'-initialized memory region on success. | |
605 | ++ * Returns NULL on failure. | |
606 | ++ * | |
607 | ++ * This function allocates memory for keeping ACL entries. | |
608 | ++ * The caller has to call tmy_free() the returned pointer | |
609 | ++ * when memory is no longer needed. | |
610 | ++ */ | |
611 | ++void *tmy_alloc(const size_t size) | |
612 | ++{ | |
613 | ++ void *ret = kzalloc(size, GFP_KERNEL); | |
614 | ++ struct cache_entry *new_entry; | |
615 | ++ | |
616 | ++ if (!ret) { | |
617 | ++ printk(KERN_INFO "ERROR: " | |
618 | ++ "kmalloc(): Out of memory for tmy_alloc().\n"); | |
619 | ++ return ret; | |
620 | ++ } | |
621 | ++ | |
622 | ++ new_entry = kmem_cache_alloc(tmy_cachep, GFP_KERNEL); | |
623 | ++ | |
624 | ++ if (!new_entry) { | |
625 | ++ printk(KERN_INFO "ERROR: " | |
626 | ++ "kmem_cache_alloc(): Out of memory for tmy_alloc().\n"); | |
627 | ++ kfree(ret); | |
628 | ++ ret = NULL; | |
629 | ++ } else { | |
630 | ++ INIT_LIST_HEAD(&new_entry->list); | |
631 | ++ new_entry->ptr = ret; | |
632 | ++ new_entry->size = ksize(ret); | |
633 | ++ | |
634 | ++ /***** CRITICAL SECTION START *****/ | |
635 | ++ spin_lock(&cache_list_lock); | |
636 | ++ list_add_tail(&new_entry->list, &cache_list); | |
637 | ++ dynamic_memory_size += new_entry->size; | |
638 | ++ spin_unlock(&cache_list_lock); | |
639 | ++ /***** CRITICAL SECTION END *****/ | |
640 | ++ } | |
641 | ++ | |
642 | ++ return ret; | |
643 | ++} | |
644 | ++ | |
645 | ++/** | |
646 | ++ * tmy_free - free memory allocated by tmy_alloc(). | |
647 | ++ * @p: pointer to memory block allocated by tmy_alloc(). | |
648 | ++ * | |
649 | ++ * If caller calls this function for multiple times (i.e. double-free() bug) or | |
650 | ++ * calls this function with invalid pointer, warning message will appear. | |
651 | ++ * If caller forgets to call this function, | |
652 | ++ * tmy_get_memory_used_for_dynamic() will indicate memory leaks. | |
653 | ++ */ | |
654 | ++void tmy_free(const void *p) | |
655 | ++{ | |
656 | ++ struct list_head *v; | |
657 | ++ struct cache_entry *entry = NULL; | |
658 | ++ if (!p) | |
659 | ++ return; | |
660 | ++ | |
661 | ++ /***** CRITICAL SECTION START *****/ | |
662 | ++ spin_lock(&cache_list_lock); | |
663 | ++ list_for_each_prev(v, &cache_list) { | |
664 | ++ entry = list_entry(v, struct cache_entry, list); | |
665 | ++ if (entry->ptr != p) { | |
666 | ++ entry = NULL; | |
667 | ++ continue; | |
668 | ++ } | |
669 | ++ list_del(&entry->list); | |
670 | ++ dynamic_memory_size -= entry->size; | |
671 | ++ break; | |
672 | ++ } | |
673 | ++ spin_unlock(&cache_list_lock); | |
674 | ++ /***** CRITICAL SECTION END *****/ | |
675 | ++ | |
676 | ++ if (entry) { | |
677 | ++ kfree(p); | |
678 | ++ kmem_cache_free(tmy_cachep, entry); | |
679 | ++ } else | |
680 | ++ printk(KERN_INFO "BUG: tmy_free() with invalid pointer.\n"); | |
681 | ++ | |
682 | ++} |
@@ -0,0 +1,944 @@ | ||
1 | +Mount access control functions for TOMOYO Linux. | |
2 | +TOMOYO Linux checks permission according to | |
3 | +device name, mount point, filesystem type and optional flags. | |
4 | +TOMOYO Linux also checks permission in umount and pivot_root. | |
5 | + | |
6 | +Each permission can be automatically accumulated into | |
7 | +the policy using 'learning mode'. | |
8 | + | |
9 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
10 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
11 | + security/tomoyo/mount.c | 928 ++++++++++++++++++++++++++++++++++++++++++++++++ | |
12 | + 1 file changed, 928 insertions(+) | |
13 | + | |
14 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
15 | ++++ linux-2.6.23/security/tomoyo/mount.c 2007-11-09 17:17:06.000000000 +0900 | |
16 | +@@ -0,0 +1,928 @@ | |
17 | ++/* | |
18 | ++ * security/tomoyo/mount.c | |
19 | ++ * | |
20 | ++ * Mount access control functions for TOMOYO Linux. | |
21 | ++ */ | |
22 | ++ | |
23 | ++#include "tomoyo.h" | |
24 | ++#include "realpath.h" | |
25 | ++ | |
26 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) | |
27 | ++static inline void put_filesystem(struct file_system_type *fs) | |
28 | ++{ | |
29 | ++ module_put(fs->owner); | |
30 | ++} | |
31 | ++#endif | |
32 | ++ | |
33 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
34 | ++#define MS_UNBINDABLE (1<<17) /* change to unbindable */ | |
35 | ++#define MS_PRIVATE (1<<18) /* change to private */ | |
36 | ++#define MS_SLAVE (1<<19) /* change to slave */ | |
37 | ++#define MS_SHARED (1<<20) /* change to shared */ | |
38 | ++#endif | |
39 | ++ | |
40 | ++/***** KEYWORDS for mount restrictions. *****/ | |
41 | ++ | |
42 | ++#define MOUNT_BIND_KEYWORD "--bind" | |
43 | ++#define MOUNT_MOVE_KEYWORD "--move" | |
44 | ++#define MOUNT_REMOUNT_KEYWORD "--remount" | |
45 | ++#define MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" | |
46 | ++#define MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" | |
47 | ++#define MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" | |
48 | ++#define MOUNT_MAKE_SHARED_KEYWORD "--make-shared" | |
49 | ++ | |
50 | ++/***** The structure for mount restrictions. *****/ | |
51 | ++ | |
52 | ++struct mount_entry { | |
53 | ++ struct list_head list; | |
54 | ++ const struct path_info *dev_name; | |
55 | ++ const struct path_info *dir_name; | |
56 | ++ const struct path_info *fs_type; | |
57 | ++ unsigned int flags; /* Mount flags. */ | |
58 | ++ bool is_deleted; | |
59 | ++}; | |
60 | ++ | |
61 | ++struct no_umount_entry { | |
62 | ++ struct list_head list; | |
63 | ++ const struct path_info *dir; | |
64 | ++ bool is_deleted; | |
65 | ++}; | |
66 | ++ | |
67 | ++/************************ MOUNT RESTRICTION HANDLER ************************/ | |
68 | ++ | |
69 | ++static LIST_HEAD(mount_list); | |
70 | ++ | |
71 | ++/* Add or remove a mount entry. */ | |
72 | ++static int tmy_add_mount_acl(const char *dev_name, | |
73 | ++ const char *dir_name, | |
74 | ++ const char *fs_type, | |
75 | ++ const unsigned int flags, | |
76 | ++ const bool is_delete) | |
77 | ++{ | |
78 | ++ struct mount_entry *new_entry; | |
79 | ++ struct mount_entry *ptr; | |
80 | ++ const struct path_info *fs; | |
81 | ++ const struct path_info *dev; | |
82 | ++ const struct path_info *dir; | |
83 | ++ static DEFINE_MUTEX(mutex); | |
84 | ++ int error = -ENOMEM; | |
85 | ++ | |
86 | ++ fs = tmy_save_name(fs_type); | |
87 | ++ if (!fs) | |
88 | ++ return -EINVAL; | |
89 | ++ | |
90 | ++ if (!dev_name) | |
91 | ++ /* Map dev_name to "<NULL>" for if no dev_name given. */ | |
92 | ++ dev_name = "<NULL>"; | |
93 | ++ if (strcmp(fs->name, MOUNT_REMOUNT_KEYWORD) == 0) | |
94 | ++ /* Fix dev_name to "any" for remount permission. */ | |
95 | ++ dev_name = "any"; | |
96 | ++ if (strcmp(fs->name, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || | |
97 | ++ strcmp(fs->name, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || | |
98 | ++ strcmp(fs->name, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || | |
99 | ++ strcmp(fs->name, MOUNT_MAKE_SHARED_KEYWORD) == 0) | |
100 | ++ dev_name = "any"; | |
101 | ++ | |
102 | ++ if (!tmy_correct_path(dev_name, 0, 0, 0, __FUNCTION__) || | |
103 | ++ !tmy_correct_path(dir_name, 1, 0, 1, __FUNCTION__)) | |
104 | ++ return -EINVAL; | |
105 | ++ | |
106 | ++ dev = tmy_save_name(dev_name); | |
107 | ++ if (!dev) | |
108 | ++ return -ENOMEM; | |
109 | ++ dir = tmy_save_name(dir_name); | |
110 | ++ if (!dir) | |
111 | ++ return -ENOMEM; | |
112 | ++ | |
113 | ++ mutex_lock(&mutex); | |
114 | ++ | |
115 | ++ list_for_each_entry(ptr, &mount_list, list) { | |
116 | ++ if (ptr->flags != flags || | |
117 | ++ tmy_pathcmp(ptr->dev_name, dev) || | |
118 | ++ tmy_pathcmp(ptr->dir_name, dir) || | |
119 | ++ tmy_pathcmp(ptr->fs_type, fs)) | |
120 | ++ continue; | |
121 | ++ ptr->is_deleted = is_delete; | |
122 | ++ error = 0; | |
123 | ++ goto out; | |
124 | ++ } | |
125 | ++ | |
126 | ++ if (is_delete) { | |
127 | ++ error = -ENOENT; | |
128 | ++ goto out; | |
129 | ++ } | |
130 | ++ | |
131 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
132 | ++ if (!new_entry) | |
133 | ++ goto out; | |
134 | ++ | |
135 | ++ new_entry->dev_name = dev; | |
136 | ++ new_entry->dir_name = dir; | |
137 | ++ new_entry->fs_type = fs; | |
138 | ++ new_entry->flags = flags; | |
139 | ++ list_add_tail_mb(&new_entry->list, &mount_list); | |
140 | ++ error = 0; | |
141 | ++out: ; | |
142 | ++ mutex_unlock(&mutex); | |
143 | ++ return error; | |
144 | ++} | |
145 | ++ | |
146 | ++/* Print error message for mount request. */ | |
147 | ++static inline int tmy_mount_perm_error(char *dev_name, | |
148 | ++ char *dir_name, | |
149 | ++ char *type, | |
150 | ++ unsigned long flags, | |
151 | ++ const u8 profile, | |
152 | ++ const unsigned int mode) | |
153 | ++{ | |
154 | ++ int error = -EPERM; | |
155 | ++ const char *realname1 = tmy_realpath(dev_name); | |
156 | ++ const char *realname2 = tmy_realpath(dir_name); | |
157 | ++ const char *exename = tmy_get_exe(); | |
158 | ++ const bool is_enforce = (mode == 3); | |
159 | ++ | |
160 | ++ if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) { | |
161 | ++ tmy_audit(KERN_WARNING "TOMOYO-%s: mount -o remount %s 0x%lX " | |
162 | ++ "(pid=%d:exe=%s): Permission denied.\n", | |
163 | ++ tmy_getmsg(is_enforce), | |
164 | ++ realname2 ? realname2 : dir_name, flags, | |
165 | ++ current->pid, exename); | |
166 | ++ if (is_enforce && | |
167 | ++ !tmy_supervisor("# %s is requesting\nmount -o remount %s\n", | |
168 | ++ exename, realname2 ? realname2 : dir_name)) | |
169 | ++ error = 0; | |
170 | ++ | |
171 | ++ } else if (!strcmp(type, MOUNT_BIND_KEYWORD) || | |
172 | ++ !strcmp(type, MOUNT_MOVE_KEYWORD)) { | |
173 | ++ tmy_audit(KERN_WARNING "TOMOYO-%s: mount %s %s %s 0x%lX " | |
174 | ++ "(pid=%d:exe=%s): Permission denied.\n", | |
175 | ++ tmy_getmsg(is_enforce), type, | |
176 | ++ realname1 ? realname1 : dev_name, | |
177 | ++ realname2 ? realname2 : dir_name, | |
178 | ++ flags, current->pid, exename); | |
179 | ++ if (is_enforce && | |
180 | ++ tmy_supervisor("# %s is requesting\nmount %s %s %s 0x%lX\n", | |
181 | ++ exename, type, | |
182 | ++ realname1 ? realname1 : dev_name, | |
183 | ++ realname2 ? realname2 : dir_name, | |
184 | ++ flags) == 0) | |
185 | ++ error = 0; | |
186 | ++ | |
187 | ++ } else if (!strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) || | |
188 | ++ !strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) || | |
189 | ++ !strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) || | |
190 | ++ !strcmp(type, MOUNT_MAKE_SHARED_KEYWORD)) { | |
191 | ++ tmy_audit(KERN_WARNING "TOMOYO-%s: mount %s %s 0x%lX " | |
192 | ++ "(pid=%d:exe=%s): Permission denied.\n", | |
193 | ++ tmy_getmsg(is_enforce), type, | |
194 | ++ realname2 ? realname2 : dir_name, | |
195 | ++ flags, current->pid, exename); | |
196 | ++ if (is_enforce && | |
197 | ++ tmy_supervisor("# %s is requesting\nmount %s %s 0x%lX", | |
198 | ++ exename, type, | |
199 | ++ realname2 ? realname2 : dir_name, | |
200 | ++ flags) == 0) | |
201 | ++ error = 0; | |
202 | ++ | |
203 | ++ } else { | |
204 | ++ tmy_audit(KERN_WARNING "TOMOYO-%s: mount -t %s %s %s 0x%lX " | |
205 | ++ "(pid=%d:exe=%s): Permission denied.\n", | |
206 | ++ tmy_getmsg(is_enforce), type, | |
207 | ++ realname1 ? realname1 : dev_name, | |
208 | ++ realname2 ? realname2 : dir_name, | |
209 | ++ flags, current->pid, exename); | |
210 | ++ if (is_enforce && | |
211 | ++ tmy_supervisor("# %s is requesting\n" | |
212 | ++ "mount -t %s %s %s 0x%lX\n", | |
213 | ++ exename, type, | |
214 | ++ realname1 ? realname1 : dev_name, | |
215 | ++ realname2 ? realname2 : dir_name, | |
216 | ++ flags) == 0) | |
217 | ++ error = 0; | |
218 | ++ | |
219 | ++ } | |
220 | ++ | |
221 | ++ tmy_free(exename); | |
222 | ++ tmy_free(realname2); | |
223 | ++ tmy_free(realname1); | |
224 | ++ return error; | |
225 | ++} | |
226 | ++ | |
227 | ++/** | |
228 | ++ * tmy_mount_perm - check for mount permission. | |
229 | ++ * @dev_name: pointer to device name. May be NULL. | |
230 | ++ * @dir_name: pointer to mount point. | |
231 | ++ * @type: pointer to filesystem. May be NULL. | |
232 | ++ * @flags: mount flags. | |
233 | ++ * | |
234 | ++ * Returns zero if permission granted. | |
235 | ++ * Returns nonzero if permission denied. | |
236 | ++ */ | |
237 | ++int tmy_mount_perm(char *dev_name, | |
238 | ++ char *dir_name, | |
239 | ++ char *type, | |
240 | ++ unsigned long flags) | |
241 | ++{ | |
242 | ++ const u8 profile = TMY_SECURITY->domain->profile; | |
243 | ++ const unsigned int mode = tmy_flags(TMY_RESTRICT_MOUNT); | |
244 | ++ const bool is_enforce = (mode == 3); | |
245 | ++ int error = -EPERM; | |
246 | ++ | |
247 | ++ if (!mode) | |
248 | ++ return 0; | |
249 | ++ if (!type) | |
250 | ++ type = "<NULL>"; | |
251 | ++ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
252 | ++ flags &= ~MS_MGC_MSK; | |
253 | ++ | |
254 | ++ switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { | |
255 | ++ case MS_REMOUNT: | |
256 | ++ case MS_MOVE: | |
257 | ++ case MS_BIND: | |
258 | ++ case 0: | |
259 | ++ break; | |
260 | ++ default: | |
261 | ++ tmy_audit(KERN_WARNING "TOMOYO-ERROR: %s%s%sare given " | |
262 | ++ "for single mount operation.\n", | |
263 | ++ flags & MS_REMOUNT ? "'remount' " : "", | |
264 | ++ flags & MS_MOVE ? "'move' " : "", | |
265 | ++ flags & MS_BIND ? "'bind' " : ""); | |
266 | ++ return -EINVAL; | |
267 | ++ } | |
268 | ++ | |
269 | ++ switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { | |
270 | ++ case MS_UNBINDABLE: | |
271 | ++ case MS_PRIVATE: | |
272 | ++ case MS_SLAVE: | |
273 | ++ case MS_SHARED: | |
274 | ++ case 0: | |
275 | ++ break; | |
276 | ++ default: | |
277 | ++ tmy_audit(KERN_WARNING "TOMOYO-ERROR: %s%s%s%sare given " | |
278 | ++ "for single mount operation.\n", | |
279 | ++ flags & MS_UNBINDABLE ? "'unbindable' " : "", | |
280 | ++ flags & MS_PRIVATE ? "'private' " : "", | |
281 | ++ flags & MS_SLAVE ? "'slave' " : "", | |
282 | ++ flags & MS_SHARED ? "'shared' " : ""); | |
283 | ++ return -EINVAL; | |
284 | ++ } | |
285 | ++ | |
286 | ++ if (flags & MS_REMOUNT) | |
287 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
288 | ++ MOUNT_REMOUNT_KEYWORD, | |
289 | ++ flags & ~MS_REMOUNT); | |
290 | ++ else if (flags & MS_MOVE) | |
291 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
292 | ++ MOUNT_MOVE_KEYWORD, | |
293 | ++ flags & ~MS_MOVE); | |
294 | ++ else if (flags & MS_BIND) | |
295 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
296 | ++ MOUNT_BIND_KEYWORD, | |
297 | ++ flags & ~MS_BIND); | |
298 | ++ else if (flags & MS_UNBINDABLE) | |
299 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
300 | ++ MOUNT_MAKE_UNBINDABLE_KEYWORD, | |
301 | ++ flags & ~MS_UNBINDABLE); | |
302 | ++ else if (flags & MS_PRIVATE) | |
303 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
304 | ++ MOUNT_MAKE_PRIVATE_KEYWORD, | |
305 | ++ flags & ~MS_PRIVATE); | |
306 | ++ else if (flags & MS_SLAVE) | |
307 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
308 | ++ MOUNT_MAKE_SLAVE_KEYWORD, | |
309 | ++ flags & ~MS_SLAVE); | |
310 | ++ else if (flags & MS_SHARED) | |
311 | ++ error = tmy_mount_perm(dev_name, dir_name, | |
312 | ++ MOUNT_MAKE_SHARED_KEYWORD, | |
313 | ++ flags & ~MS_SHARED); | |
314 | ++ else { | |
315 | ++ struct mount_entry *ptr; | |
316 | ++ struct file_system_type *fstype = NULL; | |
317 | ++ const char *requested_dir_name = NULL; | |
318 | ++ const char *requested_dev_name = NULL; | |
319 | ++ struct path_info rdev; | |
320 | ++ struct path_info rdir; | |
321 | ++ int need_dev = 0; | |
322 | ++ | |
323 | ++ requested_dir_name = tmy_realpath(dir_name); | |
324 | ++ if (!requested_dir_name) { | |
325 | ++ error = -ENOENT; | |
326 | ++ goto cleanup; | |
327 | ++ } | |
328 | ++ rdir.name = requested_dir_name; | |
329 | ++ tmy_fill_path_info(&rdir); | |
330 | ++ | |
331 | ++ /* Compare fs name. */ | |
332 | ++ fstype = get_fs_type(type); | |
333 | ++ if (strcmp(type, MOUNT_REMOUNT_KEYWORD) == 0) | |
334 | ++ /* Needn't to resolve dev_name */; | |
335 | ++ else if (strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) == 0 || | |
336 | ++ strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) == 0 || | |
337 | ++ strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) == 0 || | |
338 | ++ strcmp(type, MOUNT_MAKE_SHARED_KEYWORD) == 0) | |
339 | ++ /* Needn't to resolve dev_name */; | |
340 | ++ else if (strcmp(type, MOUNT_BIND_KEYWORD) == 0 || | |
341 | ++ strcmp(type, MOUNT_MOVE_KEYWORD) == 0) { | |
342 | ++ requested_dev_name = tmy_realpath(dev_name); | |
343 | ++ if (!requested_dev_name) { | |
344 | ++ error = -ENOENT; | |
345 | ++ goto cleanup; | |
346 | ++ } | |
347 | ++ rdev.name = requested_dev_name; | |
348 | ++ tmy_fill_path_info(&rdev); | |
349 | ++ need_dev = -1; | |
350 | ++ } else if (fstype) { | |
351 | ++ if (fstype->fs_flags & FS_REQUIRES_DEV) { | |
352 | ++ requested_dev_name = tmy_realpath(dev_name); | |
353 | ++ if (!requested_dev_name) { | |
354 | ++ error = -ENOENT; | |
355 | ++ goto cleanup; | |
356 | ++ } | |
357 | ++ rdev.name = requested_dev_name; | |
358 | ++ tmy_fill_path_info(&rdev); | |
359 | ++ need_dev = 1; | |
360 | ++ } | |
361 | ++ } else { | |
362 | ++ error = -ENODEV; | |
363 | ++ goto cleanup; | |
364 | ++ } | |
365 | ++ | |
366 | ++ list_for_each_entry(ptr, &mount_list, list) { | |
367 | ++ if (ptr->is_deleted) | |
368 | ++ continue; | |
369 | ++ | |
370 | ++ /* Compare options */ | |
371 | ++ if (ptr->flags != flags) | |
372 | ++ continue; | |
373 | ++ | |
374 | ++ /* Compare fs name. */ | |
375 | ++ if (strcmp(type, ptr->fs_type->name)) | |
376 | ++ continue; | |
377 | ++ | |
378 | ++ /* Compare mount point. */ | |
379 | ++ if (tmy_path_match(&rdir, ptr->dir_name) == 0) | |
380 | ++ continue; | |
381 | ++ | |
382 | ++ /* Compare device name. */ | |
383 | ++ if (requested_dev_name && | |
384 | ++ tmy_path_match(&rdev, ptr->dev_name) == 0) | |
385 | ++ continue; | |
386 | ++ | |
387 | ++ /* OK. */ | |
388 | ++ error = 0; | |
389 | ++ | |
390 | ++ if (need_dev > 0) | |
391 | ++ tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: " | |
392 | ++ "'mount -t %s %s %s 0x%lX' " | |
393 | ++ "accepted.\n", | |
394 | ++ type, requested_dev_name, | |
395 | ++ requested_dir_name, flags); | |
396 | ++ else if (need_dev < 0) | |
397 | ++ tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: " | |
398 | ++ "'mount %s %s %s 0x%lX' accepted.\n", | |
399 | ++ type, requested_dev_name, | |
400 | ++ requested_dir_name, flags); | |
401 | ++ else if (!strcmp(type, MOUNT_REMOUNT_KEYWORD)) | |
402 | ++ tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: " | |
403 | ++ "'mount -o remount %s 0x%lX' " | |
404 | ++ "accepted.\n", | |
405 | ++ requested_dir_name, flags); | |
406 | ++ else if (!strcmp(type, MOUNT_MAKE_UNBINDABLE_KEYWORD) || | |
407 | ++ !strcmp(type, MOUNT_MAKE_PRIVATE_KEYWORD) || | |
408 | ++ !strcmp(type, MOUNT_MAKE_SLAVE_KEYWORD) || | |
409 | ++ !strcmp(type, MOUNT_MAKE_SHARED_KEYWORD)) | |
410 | ++ tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: " | |
411 | ++ "'mount %s %s 0x%lX' accepted.\n", | |
412 | ++ type, requested_dir_name, flags); | |
413 | ++ else | |
414 | ++ tmy_audit(KERN_DEBUG "TOMOYO-NOTICE: " | |
415 | ++ "'mount %s on %s 0x%lX' accepted.\n", | |
416 | ++ type, requested_dir_name, flags); | |
417 | ++ break; | |
418 | ++ } | |
419 | ++ | |
420 | ++ if (error) | |
421 | ++ error = tmy_mount_perm_error(dev_name, dir_name, type, | |
422 | ++ flags, profile, mode); | |
423 | ++ | |
424 | ++ if (error && mode == 1) { | |
425 | ++ tmy_add_mount_acl(need_dev ? | |
426 | ++ requested_dev_name : dev_name, | |
427 | ++ requested_dir_name, type, flags, 0); | |
428 | ++ tmy_update_counter(TMY_UPDATE_SYSTEMPOLICY); | |
429 | ++ } | |
430 | ++ | |
431 | ++cleanup: | |
432 | ++ tmy_free(requested_dev_name); | |
433 | ++ tmy_free(requested_dir_name); | |
434 | ++ if (fstype) | |
435 | ++ put_filesystem(fstype); | |
436 | ++ | |
437 | ++ } | |
438 | ++ | |
439 | ++ if (!is_enforce) | |
440 | ++ error = 0; | |
441 | ++ return error; | |
442 | ++ | |
443 | ++} | |
444 | ++ | |
445 | ++/** | |
446 | ++ * tmy_add_mount_policy - add or delete mount policy. | |
447 | ++ * @data: a line to parse. | |
448 | ++ * @is_delete: is this delete request? | |
449 | ++ * | |
450 | ++ * Returns zero on success. | |
451 | ++ * Returns nonzero on failure. | |
452 | ++ */ | |
453 | ++int tmy_add_mount_policy(char *data, const bool is_delete) | |
454 | ++{ | |
455 | ++ char *cp; | |
456 | ++ char *cp2; | |
457 | ++ const char *fs; | |
458 | ++ const char *dev; | |
459 | ++ const char *dir; | |
460 | ++ unsigned int flags = 0; | |
461 | ++ | |
462 | ++ cp2 = data; | |
463 | ++ cp = strchr(cp2, ' '); | |
464 | ++ if (!cp) | |
465 | ++ return -EINVAL; | |
466 | ++ *cp = '\0'; | |
467 | ++ dev = cp2; | |
468 | ++ | |
469 | ++ cp2 = cp + 1; | |
470 | ++ cp = strchr(cp2, ' '); | |
471 | ++ if (!cp) | |
472 | ++ return -EINVAL; | |
473 | ++ *cp = '\0'; | |
474 | ++ dir = cp2; | |
475 | ++ | |
476 | ++ cp2 = cp + 1; | |
477 | ++ cp = strchr(cp2, ' '); | |
478 | ++ if (!cp) | |
479 | ++ return -EINVAL; | |
480 | ++ *cp = '\0'; | |
481 | ++ fs = cp2; | |
482 | ++ | |
483 | ++ flags = simple_strtoul(cp + 1, NULL, 0); | |
484 | ++ return tmy_add_mount_acl(dev, dir, fs, flags, is_delete); | |
485 | ++} | |
486 | ++ | |
487 | ++/** | |
488 | ++ * tmy_read_mount_policy - read mount policy. | |
489 | ++ * @head: pointer to "struct io_buffer". | |
490 | ++ * | |
491 | ++ * Returns nonzero if reading incomplete. | |
492 | ++ * Returns zero otherwise. | |
493 | ++ */ | |
494 | ++int tmy_read_mount_policy(struct io_buffer *head) | |
495 | ++{ | |
496 | ++ struct list_head *pos; | |
497 | ++ list_for_each_cookie(pos, head->read_var2, &mount_list) { | |
498 | ++ struct mount_entry *ptr; | |
499 | ++ ptr = list_entry(pos, struct mount_entry, list); | |
500 | ++ if (ptr->is_deleted) | |
501 | ++ continue; | |
502 | ++ if (tmy_io_printf(head, TMY_ALLOW_MOUNT "%s %s %s 0x%x\n", | |
503 | ++ ptr->dev_name->name, ptr->dir_name->name, | |
504 | ++ ptr->fs_type->name, ptr->flags)) | |
505 | ++ return -ENOMEM; | |
506 | ++ } | |
507 | ++ return 0; | |
508 | ++} | |
509 | ++ | |
510 | ++static int tmy_find_conceal(struct nameidata *nd, | |
511 | ++ struct vfsmount *vfsmnt, | |
512 | ++ struct dentry *dentry) | |
513 | ++{ | |
514 | ++ int flag = 0; | |
515 | ++ | |
516 | ++ if (IS_ROOT(dentry) || !d_unhashed(dentry)) { | |
517 | ++ while (1) { | |
518 | ++ | |
519 | ++ if (nd->mnt->mnt_root == vfsmnt->mnt_root && | |
520 | ++ nd->dentry == dentry) { | |
521 | ++ flag = 1; | |
522 | ++ break; | |
523 | ++ } | |
524 | ++ | |
525 | ++ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
526 | ++ | |
527 | ++ spin_lock(&vfsmount_lock); | |
528 | ++ | |
529 | ++ if (vfsmnt->mnt_parent == vfsmnt) { | |
530 | ++ spin_unlock(&vfsmount_lock); | |
531 | ++ break; | |
532 | ++ } | |
533 | ++ dentry = vfsmnt->mnt_mountpoint; | |
534 | ++ vfsmnt = vfsmnt->mnt_parent; | |
535 | ++ | |
536 | ++ spin_unlock(&vfsmount_lock); | |
537 | ++ | |
538 | ++ continue; | |
539 | ++ } | |
540 | ++ dentry = dentry->d_parent; | |
541 | ++ | |
542 | ++ } | |
543 | ++ } | |
544 | ++ | |
545 | ++ return flag; | |
546 | ++} | |
547 | ++ | |
548 | ++/** | |
549 | ++ * tmy_conceal_mount - check for conceal mount permission. | |
550 | ++ * @nd: pointer to "struct nameidata". | |
551 | ++ * | |
552 | ++ * Returns zero if permission granted. | |
553 | ++ * Returns nonzero if permission denied. | |
554 | ++ * | |
555 | ++ * People seldom mount on directries that have submounts. | |
556 | ++ * For example, you don't mount on /usr/ directory if /usr/local/ directory | |
557 | ++ * is already mounted, do you? | |
558 | ++ * This function forbids such mount requests. | |
559 | ++ */ | |
560 | ++int tmy_conceal_mount(struct nameidata *nd) | |
561 | ++{ | |
562 | ++ int flag = 0; | |
563 | ++ const unsigned int mode = tmy_flags(TMY_DENY_CONCEAL_MOUNT); | |
564 | ++ const bool is_enforce = (mode == 3); | |
565 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
566 | ++ struct namespace *namespace = current->namespace; | |
567 | ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
568 | ++ struct namespace *namespace = current->nsproxy->namespace; | |
569 | ++#else | |
570 | ++ struct mnt_namespace *namespace = current->nsproxy->mnt_ns; | |
571 | ++#endif | |
572 | ++ | |
573 | ++ if (!mode) | |
574 | ++ return 0; | |
575 | ++ | |
576 | ++ if (namespace) { | |
577 | ++ struct list_head *p; | |
578 | ++ | |
579 | ++ list_for_each(p, &namespace->list) { | |
580 | ++ struct vfsmount *vfsmnt = | |
581 | ++ list_entry(p, struct vfsmount, mnt_list); | |
582 | ++ struct dentry *dentry = vfsmnt->mnt_root; | |
583 | ++ | |
584 | ++ spin_lock(&dcache_lock); | |
585 | ++ | |
586 | ++ flag = tmy_find_conceal(nd, vfsmnt, dentry); | |
587 | ++ | |
588 | ++ spin_unlock(&dcache_lock); | |
589 | ++ | |
590 | ++ if (flag) | |
591 | ++ break; | |
592 | ++ } | |
593 | ++ } | |
594 | ++ | |
595 | ++ | |
596 | ++ if (flag) { | |
597 | ++ int error = -EPERM; | |
598 | ++ char *dir; | |
599 | ++ dir = tmy_realpath_dentry(nd->dentry, nd->mnt); | |
600 | ++ if (dir) { | |
601 | ++ const char *exename = tmy_get_exe(); | |
602 | ++ tmy_audit("TOMOYO-%s: mount %s (pid=%d:exe=%s): " | |
603 | ++ "Permission denied.\n", | |
604 | ++ tmy_getmsg(is_enforce), | |
605 | ++ dir, current->pid, exename); | |
606 | ++ if (is_enforce && | |
607 | ++ tmy_supervisor("# %s is requesting\nmount on %s\n", | |
608 | ++ exename, dir) == 0) | |
609 | ++ error = 0; | |
610 | ++ | |
611 | ++ tmy_free(exename); | |
612 | ++ } | |
613 | ++ tmy_free(dir); | |
614 | ++ | |
615 | ++ if (is_enforce) | |
616 | ++ return error; | |
617 | ++ } | |
618 | ++ | |
619 | ++ return 0; | |
620 | ++} | |
621 | ++ | |
622 | ++/************************ UMOUNT RESTRICTION HANDLER ************************/ | |
623 | ++ | |
624 | ++static LIST_HEAD(no_umount_list); | |
625 | ++ | |
626 | ++/* Add or remove a no-unmount entry. */ | |
627 | ++static int tmy_add_no_umount_acl(const char *dir, const bool is_delete) | |
628 | ++{ | |
629 | ++ struct no_umount_entry *new_entry; | |
630 | ++ struct no_umount_entry *ptr; | |
631 | ++ const struct path_info *saved_dir; | |
632 | ++ static DEFINE_MUTEX(mutex); | |
633 | ++ int error = -ENOMEM; | |
634 | ++ | |
635 | ++ if (!tmy_correct_path(dir, 1, 0, 1, __FUNCTION__)) | |
636 | ++ return -EINVAL; | |
637 | ++ saved_dir = tmy_save_name(dir); | |
638 | ++ if (!saved_dir) | |
639 | ++ return -ENOMEM; | |
640 | ++ | |
641 | ++ mutex_lock(&mutex); | |
642 | ++ | |
643 | ++ list_for_each_entry(ptr, &no_umount_list, list) { | |
644 | ++ if (ptr->dir == saved_dir) { | |
645 | ++ ptr->is_deleted = is_delete; | |
646 | ++ error = 0; | |
647 | ++ goto out; | |
648 | ++ } | |
649 | ++ } | |
650 | ++ | |
651 | ++ if (is_delete) { | |
652 | ++ error = -ENOENT; | |
653 | ++ goto out; | |
654 | ++ } | |
655 | ++ | |
656 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
657 | ++ if (!new_entry) | |
658 | ++ goto out; | |
659 | ++ | |
660 | ++ new_entry->dir = saved_dir; | |
661 | ++ list_add_tail_mb(&new_entry->list, &no_umount_list); | |
662 | ++ error = 0; | |
663 | ++out: ; | |
664 | ++ | |
665 | ++ mutex_unlock(&mutex); | |
666 | ++ | |
667 | ++ return error; | |
668 | ++} | |
669 | ++ | |
670 | ++/** | |
671 | ++ * tmy_umount_perm - check for no-unmount permission. | |
672 | ++ * @mnt: pointer to "struct vfsmount". | |
673 | ++ * | |
674 | ++ * Returns zero if permission granted. | |
675 | ++ * Returns nonzero if permission denied. | |
676 | ++ */ | |
677 | ++int tmy_umount_perm(struct vfsmount *mnt) | |
678 | ++{ | |
679 | ++ int error = -EPERM; | |
680 | ++ const char *dir0; | |
681 | ++ const unsigned int mode = tmy_flags(TMY_RESTRICT_UMOUNT); | |
682 | ++ const bool is_enforce = (mode == 3); | |
683 | ++ | |
684 | ++ if (!mode) | |
685 | ++ return 0; | |
686 | ++ | |
687 | ++ dir0 = tmy_realpath_dentry(mnt->mnt_root, mnt); | |
688 | ++ | |
689 | ++ if (dir0) { | |
690 | ++ struct no_umount_entry *ptr; | |
691 | ++ struct path_info dir; | |
692 | ++ bool found = 0; | |
693 | ++ | |
694 | ++ dir.name = dir0; | |
695 | ++ tmy_fill_path_info(&dir); | |
696 | ++ | |
697 | ++ list_for_each_entry(ptr, &no_umount_list, list) { | |
698 | ++ if (ptr->is_deleted) | |
699 | ++ continue; | |
700 | ++ if (tmy_path_match(&dir, ptr->dir)) { | |
701 | ++ found = 1; | |
702 | ++ break; | |
703 | ++ } | |
704 | ++ } | |
705 | ++ | |
706 | ++ if (found) { | |
707 | ++ const char *exename = tmy_get_exe(); | |
708 | ++ tmy_audit("TOMOYO-%s: umount %s (pid=%d:exe=%s): " | |
709 | ++ "Permission denied.\n", | |
710 | ++ tmy_getmsg(is_enforce), | |
711 | ++ dir0, current->pid, exename); | |
712 | ++ if (is_enforce && | |
713 | ++ tmy_supervisor("# %s is requesting\nunmount %s\n", | |
714 | ++ exename, dir0) == 0) | |
715 | ++ error = 0; | |
716 | ++ | |
717 | ++ tmy_free(exename); | |
718 | ++ } else { | |
719 | ++ error = 0; | |
720 | ++ } | |
721 | ++ | |
722 | ++ tmy_free(dir0); | |
723 | ++ } | |
724 | ++ | |
725 | ++ if (!is_enforce) | |
726 | ++ error = 0; | |
727 | ++ return error; | |
728 | ++} | |
729 | ++ | |
730 | ++/** | |
731 | ++ * tmy_add_no_umount_policy - add or delete no-unmount policy. | |
732 | ++ * @data: a line to parse. | |
733 | ++ * @is_delete: is this delete request? | |
734 | ++ * | |
735 | ++ * Returns zero on success. | |
736 | ++ * Returns nonzero on failure. | |
737 | ++ */ | |
738 | ++int tmy_add_no_umount_policy(char *data, const bool is_delete) | |
739 | ++{ | |
740 | ++ return tmy_add_no_umount_acl(data, is_delete); | |
741 | ++} | |
742 | ++ | |
743 | ++/** | |
744 | ++ * tmy_read_no_umount_policy - read no-unmount policy. | |
745 | ++ * @head: pointer to "struct io_buffer". | |
746 | ++ * | |
747 | ++ * Returns nonzero if reading incomplete. | |
748 | ++ * Returns zero otherwise. | |
749 | ++ */ | |
750 | ++int tmy_read_no_umount_policy(struct io_buffer *head) | |
751 | ++{ | |
752 | ++ struct list_head *pos; | |
753 | ++ list_for_each_cookie(pos, head->read_var2, &no_umount_list) { | |
754 | ++ struct no_umount_entry *ptr; | |
755 | ++ ptr = list_entry(pos, struct no_umount_entry, list); | |
756 | ++ if (ptr->is_deleted) | |
757 | ++ continue; | |
758 | ++ if (tmy_io_printf(head, TMY_DENY_UNMOUNT "%s\n", | |
759 | ++ ptr->dir->name)) | |
760 | ++ return -ENOMEM; | |
761 | ++ } | |
762 | ++ return 0; | |
763 | ++} | |
764 | ++ | |
765 | ++/***** The structure for pivot_root restrictions. *****/ | |
766 | ++ | |
767 | ++struct pivot_root_entry { | |
768 | ++ struct list_head list; | |
769 | ++ const struct path_info *old_root; | |
770 | ++ const struct path_info *new_root; | |
771 | ++ bool is_deleted; | |
772 | ++}; | |
773 | ++ | |
774 | ++/********************** PIVOT_ROOT RESTRICTION HANDLER **********************/ | |
775 | ++ | |
776 | ++static LIST_HEAD(pivot_root_list); | |
777 | ++ | |
778 | ++/* Add or remove a pivot_root entry. */ | |
779 | ++static int tmy_add_pivot_root_acl(const char *old_root, | |
780 | ++ const char *new_root, | |
781 | ++ const bool is_delete) | |
782 | ++{ | |
783 | ++ struct pivot_root_entry *new_entry; | |
784 | ++ struct pivot_root_entry *ptr; | |
785 | ++ const struct path_info *saved_old_root; | |
786 | ++ const struct path_info *saved_new_root; | |
787 | ++ static DEFINE_MUTEX(mutex); | |
788 | ++ int error = -ENOMEM; | |
789 | ++ | |
790 | ++ if (!tmy_correct_path(old_root, 1, 0, 1, __FUNCTION__) || | |
791 | ++ !tmy_correct_path(new_root, 1, 0, 1, __FUNCTION__)) | |
792 | ++ return -EINVAL; | |
793 | ++ | |
794 | ++ saved_old_root = tmy_save_name(old_root); | |
795 | ++ if (!saved_old_root) | |
796 | ++ return -ENOMEM; | |
797 | ++ saved_new_root = tmy_save_name(new_root); | |
798 | ++ if (!saved_new_root) | |
799 | ++ return -ENOMEM; | |
800 | ++ | |
801 | ++ mutex_lock(&mutex); | |
802 | ++ | |
803 | ++ list_for_each_entry(ptr, &pivot_root_list, list) { | |
804 | ++ if (ptr->old_root == saved_old_root && | |
805 | ++ ptr->new_root == saved_new_root) { | |
806 | ++ ptr->is_deleted = is_delete; | |
807 | ++ error = 0; | |
808 | ++ goto out; | |
809 | ++ } | |
810 | ++ } | |
811 | ++ | |
812 | ++ if (is_delete) { | |
813 | ++ error = -ENOENT; | |
814 | ++ goto out; | |
815 | ++ } | |
816 | ++ | |
817 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
818 | ++ if (!new_entry) | |
819 | ++ goto out; | |
820 | ++ | |
821 | ++ new_entry->old_root = saved_old_root; | |
822 | ++ new_entry->new_root = saved_new_root; | |
823 | ++ list_add_tail_mb(&new_entry->list, &pivot_root_list); | |
824 | ++ error = 0; | |
825 | ++out: ; | |
826 | ++ | |
827 | ++ mutex_unlock(&mutex); | |
828 | ++ | |
829 | ++ return error; | |
830 | ++} | |
831 | ++ | |
832 | ++/** | |
833 | ++ * tmy_pivot_root_perm - check for pivot_root permission. | |
834 | ++ * @old_nd: pointer to "struct nameidata". | |
835 | ++ * @new_nd: pointer to "struct nameidata". | |
836 | ++ * | |
837 | ++ * Returns zero if permission granted. | |
838 | ++ * Returns nonzero if permission denied. | |
839 | ++ */ | |
840 | ++int tmy_pivot_root_perm(struct nameidata *old_nd, struct nameidata *new_nd) | |
841 | ++{ | |
842 | ++ int error = -EPERM; | |
843 | ++ const unsigned int mode = tmy_flags(TMY_RESTRICT_PIVOT_ROOT); | |
844 | ++ const bool is_enforce = (mode == 3); | |
845 | ++ char *old_root; | |
846 | ++ char *new_root; | |
847 | ++ struct path_info old_root_dir; | |
848 | ++ struct path_info new_root_dir; | |
849 | ++ | |
850 | ++ if (!mode) | |
851 | ++ return 0; | |
852 | ++ | |
853 | ++ old_root = tmy_realpath_dentry(old_nd->dentry, old_nd->mnt); | |
854 | ++ new_root = tmy_realpath_dentry(new_nd->dentry, new_nd->mnt); | |
855 | ++ | |
856 | ++ if (!old_root || !new_root) | |
857 | ++ goto out; | |
858 | ++ | |
859 | ++ old_root_dir.name = old_root; | |
860 | ++ tmy_fill_path_info(&old_root_dir); | |
861 | ++ new_root_dir.name = new_root; | |
862 | ++ tmy_fill_path_info(&new_root_dir); | |
863 | ++ | |
864 | ++ if (old_root_dir.is_dir && new_root_dir.is_dir) { | |
865 | ++ struct pivot_root_entry *ptr; | |
866 | ++ list_for_each_entry(ptr, &pivot_root_list, list) { | |
867 | ++ if (ptr->is_deleted) | |
868 | ++ continue; | |
869 | ++ if (tmy_path_match(&old_root_dir, ptr->old_root) && | |
870 | ++ tmy_path_match(&new_root_dir, ptr->new_root)) { | |
871 | ++ error = 0; | |
872 | ++ break; | |
873 | ++ } | |
874 | ++ } | |
875 | ++ } | |
876 | ++ | |
877 | ++out: ; | |
878 | ++ if (error) { | |
879 | ++ const char *exename = tmy_get_exe(); | |
880 | ++ tmy_audit("TOMOYO-%s: pivot_root %s %s (pid=%d:exe=%s): " | |
881 | ++ "Permission denied.\n", tmy_getmsg(is_enforce), | |
882 | ++ new_root, old_root, current->pid, exename); | |
883 | ++ if (is_enforce && | |
884 | ++ tmy_supervisor("# %s is requesting\npivot_root %s %s\n", | |
885 | ++ exename, new_root, old_root) == 0) | |
886 | ++ error = 0; | |
887 | ++ | |
888 | ++ if (exename) | |
889 | ++ tmy_free(exename); | |
890 | ++ | |
891 | ++ if (!is_enforce && mode == 1 && old_root && new_root) { | |
892 | ++ tmy_add_pivot_root_acl(old_root, new_root, 0); | |
893 | ++ tmy_update_counter(TMY_UPDATE_SYSTEMPOLICY); | |
894 | ++ } | |
895 | ++ | |
896 | ++ if (!is_enforce) | |
897 | ++ error = 0; | |
898 | ++ } | |
899 | ++ | |
900 | ++ tmy_free(old_root); | |
901 | ++ tmy_free(new_root); | |
902 | ++ return error; | |
903 | ++} | |
904 | ++ | |
905 | ++/** | |
906 | ++ * tmy_add_pivot_root_policy - add or delete pivot_root policy. | |
907 | ++ * @data: a line to parse. | |
908 | ++ * @is_delete: is this delete request? | |
909 | ++ * | |
910 | ++ * Returns zero on success. | |
911 | ++ * Returns nonzero on failure. | |
912 | ++ */ | |
913 | ++int tmy_add_pivot_root_policy(char *data, const bool is_delete) | |
914 | ++{ | |
915 | ++ char *cp = strchr(data, ' '); | |
916 | ++ | |
917 | ++ if (!cp) | |
918 | ++ return -EINVAL; | |
919 | ++ *cp++ = '\0'; | |
920 | ++ | |
921 | ++ return tmy_add_pivot_root_acl(cp, data, is_delete); | |
922 | ++} | |
923 | ++ | |
924 | ++/** | |
925 | ++ * tmy_read_pivot_root_policy - read pivot_root policy. | |
926 | ++ * @head: pointer to "struct io_buffer". | |
927 | ++ * | |
928 | ++ * Returns nonzero if reading incomplete. | |
929 | ++ * Returns zero otherwise. | |
930 | ++ */ | |
931 | ++int tmy_read_pivot_root_policy(struct io_buffer *head) | |
932 | ++{ | |
933 | ++ struct list_head *pos; | |
934 | ++ list_for_each_cookie(pos, head->read_var2, &pivot_root_list) { | |
935 | ++ struct pivot_root_entry *ptr; | |
936 | ++ ptr = list_entry(pos, struct pivot_root_entry, list); | |
937 | ++ if (ptr->is_deleted) | |
938 | ++ continue; | |
939 | ++ if (tmy_io_printf(head, TMY_ALLOW_PIVOT_ROOT "%s %s\n", | |
940 | ++ ptr->new_root->name, ptr->old_root->name)) | |
941 | ++ return -ENOMEM; | |
942 | ++ } | |
943 | ++ return 0; | |
944 | ++} |
@@ -0,0 +1,260 @@ | ||
1 | +TOMOYO Linux uses /sys/kernel/security/tomoyo/ interface | |
2 | +for reporting access logs in domain policy format. | |
3 | +One is 'grant_log', used for auditing accesses which are | |
4 | +granted in the TOMOYO Linux policy. | |
5 | +The other is 'reject_log', used for auditing accesses which | |
6 | +are not granted in the TOMOYO Linux policy. | |
7 | +The userland daemon /usr/lib/ccs/ccs-auditd will save these logs. | |
8 | + | |
9 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
10 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
11 | + security/tomoyo/audit.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++++ | |
12 | + 1 file changed, 244 insertions(+) | |
13 | + | |
14 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
15 | ++++ linux-2.6.23/security/tomoyo/audit.c 2007-11-09 18:09:28.000000000 +0900 | |
16 | +@@ -0,0 +1,244 @@ | |
17 | ++/* | |
18 | ++ * security/tomoyo/audit.c | |
19 | ++ * | |
20 | ++ * Audit functions for TOMOYO Linux | |
21 | ++ */ | |
22 | ++ | |
23 | ++#include "tomoyo.h" | |
24 | ++ | |
25 | ++#ifdef CONFIG_SECURITY_TOMOYO_USE_AUDITD | |
26 | ++/** | |
27 | ++ * tmy_audit - write audit log. | |
28 | ++ * @fmt: format strings for printf(). | |
29 | ++ * | |
30 | ++ * Returns zero on success. | |
31 | ++ * Returns nonzero on failure. | |
32 | ++ * | |
33 | ++ * Write audit log. | |
34 | ++ */ | |
35 | ++int tmy_audit(const char *fmt, ...) | |
36 | ++{ | |
37 | ++ struct audit_buffer *ab; | |
38 | ++ int len; | |
39 | ++ va_list args; | |
40 | ++ char *buf; | |
41 | ++ char *cp; | |
42 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) | |
43 | ++ ab = audit_log_start(current->audit_context); | |
44 | ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
45 | ++ ab = audit_log_start(current->audit_context, AUDIT_KERNEL); | |
46 | ++#else | |
47 | ++ ab = audit_log_start(current->audit_context, GFP_KERNEL, AUDIT_KERNEL); | |
48 | ++#endif | |
49 | ++ if (!ab) | |
50 | ++ return -ENOMEM; | |
51 | ++ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
52 | ++ if (!buf) | |
53 | ++ goto out; | |
54 | ++ va_start(args, fmt); | |
55 | ++ len = vsnprintf(buf, PAGE_SIZE - 1, fmt, args); | |
56 | ++ va_end(args); | |
57 | ++ if (len > PAGE_SIZE - 1) { | |
58 | ++ kfree(buf); | |
59 | ++ buf = kzalloc(len + 16, GFP_KERNEL); | |
60 | ++ if (!buf) | |
61 | ++ goto out; | |
62 | ++ va_start(args, fmt); | |
63 | ++ vsnprintf(buf, len + 15, fmt, args); | |
64 | ++ va_end(args); | |
65 | ++ } | |
66 | ++ cp = strchr(buf, '\0') - 1; | |
67 | ++ if (cp >= buf && *cp == '\n') | |
68 | ++ *cp = '\0'; | |
69 | ++ audit_log_format(ab, "TOMOYO: %s", buf); | |
70 | ++ kfree(buf); | |
71 | ++out: ; | |
72 | ++ audit_log_end(ab); | |
73 | ++ return buf ? 0 : -ENOMEM; | |
74 | ++} | |
75 | ++#endif | |
76 | ++ | |
77 | ++static DECLARE_WAIT_QUEUE_HEAD(grant_log_wait); | |
78 | ++static DECLARE_WAIT_QUEUE_HEAD(reject_log_wait); | |
79 | ++ | |
80 | ++static DEFINE_SPINLOCK(audit_log_lock); | |
81 | ++ | |
82 | ++struct log_entry { | |
83 | ++ struct list_head list; | |
84 | ++ char *log; | |
85 | ++}; | |
86 | ++ | |
87 | ++static LIST_HEAD(grant_log); | |
88 | ++static LIST_HEAD(reject_log); | |
89 | ++ | |
90 | ++static int grant_log_count; | |
91 | ++static int reject_log_count; | |
92 | ++ | |
93 | ++/** | |
94 | ++ * tmy_audit_grant - get flags of auditing grant logs. | |
95 | ++ * | |
96 | ++ * Returns current value of auditing grant log flags. | |
97 | ++ */ | |
98 | ++bool tmy_audit_grant(void) | |
99 | ++{ | |
100 | ++ return grant_log_count < tmy_flags(TMY_MAX_GRANT_LOG); | |
101 | ++} | |
102 | ++ | |
103 | ++/** | |
104 | ++ * tmy_audit_reject - get flags of auditing reject logs. | |
105 | ++ * | |
106 | ++ * Returns current value of auditing reject log flags. | |
107 | ++ */ | |
108 | ++bool tmy_audit_reject(void) | |
109 | ++{ | |
110 | ++ return reject_log_count < tmy_flags(TMY_MAX_REJECT_LOG); | |
111 | ++} | |
112 | ++ | |
113 | ++/** | |
114 | ++ * tmy_init_audit_log - allocate and initialize audit buffer. | |
115 | ++ * @len: pointer to length of requested size. | |
116 | ++ * @profile: profile number for this log. | |
117 | ++ * @mode: profile value for this log. | |
118 | ++ * | |
119 | ++ * Returns pointer to audit buffer on success. @len received allocated size. | |
120 | ++ * Returns NULL on failure. | |
121 | ++ * | |
122 | ++ * @len must not be a NULL. | |
123 | ++ */ | |
124 | ++char *tmy_init_audit_log(int *len, const u8 profile, const unsigned int mode) | |
125 | ++{ | |
126 | ++ char *buf; | |
127 | ++ struct timeval tv; | |
128 | ++ struct task_struct *task = current; | |
129 | ++ const char *domainname = TMY_SECURITY->domain->domainname->name; | |
130 | ++ do_gettimeofday(&tv); | |
131 | ++ *len += strlen(domainname) + 256; | |
132 | ++ buf = tmy_alloc(*len); | |
133 | ++ if (!buf) | |
134 | ++ return NULL; | |
135 | ++ snprintf(buf, (*len) - 1, "#timestamp=%lu profile=%u mode=%u " | |
136 | ++ "pid=%d uid=%d gid=%d euid=%d egid=%d " | |
137 | ++ "suid=%d sgid=%d fsuid=%d fsgid=%d \n%s\n", | |
138 | ++ tv.tv_sec, profile, mode, | |
139 | ++ task->pid, task->uid, task->gid, task->euid, task->egid, | |
140 | ++ task->suid, task->sgid, task->fsuid, task->fsgid, domainname); | |
141 | ++ return buf; | |
142 | ++} | |
143 | ++ | |
144 | ++/** | |
145 | ++ * tmy_write_audit_log - write audit log. | |
146 | ++ * @buf: pointer to access log contents. | |
147 | ++ * @is_granted: is the access request granted? | |
148 | ++ * | |
149 | ++ * Returns zero on success. | |
150 | ++ * Returns nonzero on failure. | |
151 | ++ * | |
152 | ++ * Write audit log. | |
153 | ++ * Caller must allocate @buf with tmy_init_audit_log(). | |
154 | ++ */ | |
155 | ++int tmy_write_audit_log(char *buf, const bool is_granted) | |
156 | ++{ | |
157 | ++ struct log_entry *new_entry; | |
158 | ++ new_entry = tmy_alloc(sizeof(*new_entry)); | |
159 | ++ if (!new_entry) { | |
160 | ++ tmy_free(buf); | |
161 | ++ return -ENOMEM; | |
162 | ++ } | |
163 | ++ INIT_LIST_HEAD(&new_entry->list); | |
164 | ++ new_entry->log = buf; | |
165 | ++ /***** CRITICAL SECTION START *****/ | |
166 | ++ spin_lock(&audit_log_lock); | |
167 | ++ if (is_granted) { | |
168 | ++ list_add_tail(&new_entry->list, &grant_log); | |
169 | ++ grant_log_count++; | |
170 | ++ buf = NULL; | |
171 | ++ tmy_update_counter(TMY_UPDATE_GRANT_LOG); | |
172 | ++ } else { | |
173 | ++ list_add_tail(&new_entry->list, &reject_log); | |
174 | ++ reject_log_count++; | |
175 | ++ buf = NULL; | |
176 | ++ tmy_update_counter(TMY_UPDATE_REJECT_LOG); | |
177 | ++ } | |
178 | ++ spin_unlock(&audit_log_lock); | |
179 | ++ /***** CRITICAL SECTION END *****/ | |
180 | ++ if (is_granted) | |
181 | ++ wake_up(&grant_log_wait); | |
182 | ++ else | |
183 | ++ wake_up(&reject_log_wait); | |
184 | ++ return 0; | |
185 | ++} | |
186 | ++ | |
187 | ++int tmy_read_grant_log(struct io_buffer *head) | |
188 | ++{ | |
189 | ++ struct log_entry *ptr = NULL; | |
190 | ++ if (head->read_avail) return 0; | |
191 | ++ if (head->read_buf) { | |
192 | ++ tmy_free(head->read_buf); | |
193 | ++ head->read_buf = NULL; | |
194 | ++ head->readbuf_size = 0; | |
195 | ++ } | |
196 | ++ /***** CRITICAL SECTION START *****/ | |
197 | ++ spin_lock(&audit_log_lock); | |
198 | ++ if (!list_empty(&grant_log)) { | |
199 | ++ ptr = list_entry(grant_log.next, struct log_entry, list); | |
200 | ++ list_del(&ptr->list); | |
201 | ++ grant_log_count--; | |
202 | ++ } | |
203 | ++ spin_unlock(&audit_log_lock); | |
204 | ++ /***** CRITICAL SECTION END *****/ | |
205 | ++ if (ptr) { | |
206 | ++ head->read_buf = ptr->log; | |
207 | ++ head->read_avail = strlen(ptr->log) + 1; | |
208 | ++ head->readbuf_size = head->read_avail; | |
209 | ++ tmy_free(ptr); | |
210 | ++ } | |
211 | ++ return 0; | |
212 | ++} | |
213 | ++ | |
214 | ++int tmy_poll_grant_log(struct file *file, poll_table *wait) | |
215 | ++{ | |
216 | ++ if (grant_log_count) | |
217 | ++ return POLLIN | POLLRDNORM; | |
218 | ++ poll_wait(file, &grant_log_wait, wait); | |
219 | ++ if (grant_log_count) | |
220 | ++ return POLLIN | POLLRDNORM; | |
221 | ++ return 0; | |
222 | ++} | |
223 | ++ | |
224 | ++int tmy_read_reject_log(struct io_buffer *head) | |
225 | ++{ | |
226 | ++ struct log_entry *ptr = NULL; | |
227 | ++ if (head->read_avail) | |
228 | ++ return 0; | |
229 | ++ if (head->read_buf) { | |
230 | ++ tmy_free(head->read_buf); | |
231 | ++ head->read_buf = NULL; | |
232 | ++ head->readbuf_size = 0; | |
233 | ++ } | |
234 | ++ /***** CRITICAL SECTION START *****/ | |
235 | ++ spin_lock(&audit_log_lock); | |
236 | ++ if (!list_empty(&reject_log)) { | |
237 | ++ ptr = list_entry(reject_log.next, struct log_entry, list); | |
238 | ++ list_del(&ptr->list); | |
239 | ++ reject_log_count--; | |
240 | ++ } | |
241 | ++ spin_unlock(&audit_log_lock); | |
242 | ++ /***** CRITICAL SECTION END *****/ | |
243 | ++ if (ptr) { | |
244 | ++ head->read_buf = ptr->log; | |
245 | ++ head->read_avail = strlen(ptr->log) + 1; | |
246 | ++ head->readbuf_size = head->read_avail; | |
247 | ++ tmy_free(ptr); | |
248 | ++ } | |
249 | ++ return 0; | |
250 | ++} | |
251 | ++ | |
252 | ++int tmy_poll_reject_log(struct file *file, poll_table *wait) | |
253 | ++{ | |
254 | ++ if (reject_log_count) | |
255 | ++ return POLLIN | POLLRDNORM; | |
256 | ++ poll_wait(file, &reject_log_wait, wait); | |
257 | ++ if (reject_log_count) | |
258 | ++ return POLLIN | POLLRDNORM; | |
259 | ++ return 0; | |
260 | ++} |
@@ -0,0 +1,1183 @@ | ||
1 | +Domain transition functions for TOMOYO Linux. | |
2 | +Every process belongs to a domain in TOMOYO Linux. | |
3 | +Domain transition occurs when execve(2) is called | |
4 | +and the domain is expressed as 'process invocation history', | |
5 | +such as '<kernel> /sbin/init /etc/init.d/rc'. | |
6 | +Domain information is stored in task_struct->security. | |
7 | + | |
8 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
9 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
10 | + security/tomoyo/domain.c | 1168 +++++++++++++++++++++++++++++++++++++++++++++++ | |
11 | + 1 file changed, 1168 insertions(+) | |
12 | + | |
13 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
14 | ++++ linux-2.6.23/security/tomoyo/domain.c 2007-11-02 13:48:04.000000000 +0900 | |
15 | +@@ -0,0 +1,1168 @@ | |
16 | ++/* | |
17 | ++ * security/tomoyo/domain.c | |
18 | ++ * | |
19 | ++ * Domain transition functions for TOMOYO Linux. | |
20 | ++ */ | |
21 | ++ | |
22 | ++#include "tomoyo.h" | |
23 | ++#include "realpath.h" | |
24 | ++ | |
25 | ++/************************* VARIABLES *************************/ | |
26 | ++ | |
27 | ++/* The initial domain. */ | |
28 | ++struct domain_info KERNEL_DOMAIN; | |
29 | ++ | |
30 | ++/* Lock for appending domain's ACL. */ | |
31 | ++DEFINE_MUTEX(domain_acl_lock); | |
32 | ++ | |
33 | ++/* Domain creation lock. */ | |
34 | ++static DEFINE_MUTEX(new_domain_assign_lock); | |
35 | ++ | |
36 | ++/***** The structure for program files to force domain reconstruction. *****/ | |
37 | ++ | |
38 | ++struct domain_initializer_entry { | |
39 | ++ struct list_head list; | |
40 | ++ const struct path_info *domainname; /* This may be NULL */ | |
41 | ++ const struct path_info *program; | |
42 | ++ bool is_deleted; | |
43 | ++ bool is_not; | |
44 | ++ bool is_last_name; | |
45 | ++}; | |
46 | ++ | |
47 | ++/***** The structure for domains to not to transit domains. *****/ | |
48 | ++ | |
49 | ++struct domain_keeper_entry { | |
50 | ++ struct list_head list; | |
51 | ++ const struct path_info *domainname; | |
52 | ++ const struct path_info *program; /* This may be NULL */ | |
53 | ++ bool is_deleted; | |
54 | ++ bool is_not; | |
55 | ++ bool is_last_name; | |
56 | ++}; | |
57 | ++ | |
58 | ++/***** The structure for program files that should be aggregated. *****/ | |
59 | ++ | |
60 | ++struct aggregator_entry { | |
61 | ++ struct list_head list; | |
62 | ++ const struct path_info *original_name; | |
63 | ++ const struct path_info *aggregated_name; | |
64 | ++ bool is_deleted; | |
65 | ++}; | |
66 | ++ | |
67 | ++/***** The structure for program files that should be aliased. *****/ | |
68 | ++ | |
69 | ++struct alias_entry { | |
70 | ++ struct list_head list; | |
71 | ++ const struct path_info *original_name; | |
72 | ++ const struct path_info *aliased_name; | |
73 | ++ bool is_deleted; | |
74 | ++}; | |
75 | ++ | |
76 | ++/************************* UTILITY FUNCTIONS *************************/ | |
77 | ++ | |
78 | ++/** | |
79 | ++ * tmy_is_domain_def - check if the line is likely a domain definition. | |
80 | ++ * @buffer: the line to check. | |
81 | ++ * | |
82 | ++ * Returns true if @buffer is likely a domain definition. | |
83 | ++ * Returns false otherwise. | |
84 | ++ * | |
85 | ++ * For complete validation check, use tmy_is_correct_domain(). | |
86 | ++ */ | |
87 | ++bool tmy_is_domain_def(const unsigned char *buffer) | |
88 | ++{ | |
89 | ++ return strncmp(buffer, TMY_ROOT_NAME, TMY_ROOT_NAME_LEN) == 0; | |
90 | ++} | |
91 | ++ | |
92 | ++/** | |
93 | ++ * tmy_add_acl - add an entry to a domain. | |
94 | ++ * @domain: pointer to "struct domain_info". | |
95 | ++ * @acl: pointer to "struct acl_info" to add. | |
96 | ++ * | |
97 | ++ * Returns zero. | |
98 | ++ */ | |
99 | ++int tmy_add_acl(struct domain_info *domain, | |
100 | ++ struct acl_info *acl) | |
101 | ++{ | |
102 | ++ list_add_tail_mb(&acl->list, &domain->acl_info_list); | |
103 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
104 | ++ return 0; | |
105 | ++} | |
106 | ++ | |
107 | ++/** | |
108 | ++ * tmy_del_acl - remove an entry from a domain | |
109 | ++ * @ptr: pointer to "struct acl_info" to remove. | |
110 | ++ * | |
111 | ++ * Returns zero. | |
112 | ++ * | |
113 | ++ * TOMOYO Linux doesn't free memory used by policy because policies are not | |
114 | ++ * so frequently changed after entring into enforcing mode. | |
115 | ++ * This makes the code free of read-lock. | |
116 | ++ * The caller uses "down(&domain_acl_lock);" as write-lock. | |
117 | ++ */ | |
118 | ++int tmy_del_acl(struct acl_info *ptr) | |
119 | ++{ | |
120 | ++ ptr->is_deleted = 1; | |
121 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
122 | ++ return 0; | |
123 | ++} | |
124 | ++ | |
125 | ++/************************ DOMAIN INITIALIZER HANDLER ************************/ | |
126 | ++ | |
127 | ++static LIST_HEAD(domain_initializer_list); | |
128 | ++ | |
129 | ++/* Update domain initializer list. */ | |
130 | ++static int tmy_add_domain_initializer_entry(const char *domainname, | |
131 | ++ const char *program, | |
132 | ++ const bool is_not, | |
133 | ++ const bool is_delete) | |
134 | ++{ | |
135 | ++ struct domain_initializer_entry *new_entry; | |
136 | ++ struct domain_initializer_entry *ptr; | |
137 | ++ static DEFINE_MUTEX(mutex); | |
138 | ++ const struct path_info *saved_program; | |
139 | ++ const struct path_info *saved_domainname = NULL; | |
140 | ++ int error = -ENOMEM; | |
141 | ++ bool is_last_name = 0; | |
142 | ++ | |
143 | ++ if (!tmy_correct_path(program, 1, -1, -1, __FUNCTION__)) | |
144 | ++ return -EINVAL; /* No patterns allowed. */ | |
145 | ++ | |
146 | ++ if (domainname) { | |
147 | ++ if (!tmy_is_domain_def(domainname) && | |
148 | ++ tmy_correct_path(domainname, 1, -1, -1, __FUNCTION__)) | |
149 | ++ is_last_name = 1; | |
150 | ++ | |
151 | ++ else if (!tmy_is_correct_domain(domainname, __FUNCTION__)) | |
152 | ++ return -EINVAL; | |
153 | ++ | |
154 | ++ saved_domainname = tmy_save_name(domainname); | |
155 | ++ if (!saved_domainname) | |
156 | ++ return -ENOMEM; | |
157 | ++ } | |
158 | ++ | |
159 | ++ saved_program = tmy_save_name(program); | |
160 | ++ if (!saved_program) | |
161 | ++ return -ENOMEM; | |
162 | ++ | |
163 | ++ mutex_lock(&mutex); | |
164 | ++ | |
165 | ++ list_for_each_entry(ptr, &domain_initializer_list, list) { | |
166 | ++ if (ptr->is_not == is_not && | |
167 | ++ ptr->domainname == saved_domainname && | |
168 | ++ ptr->program == saved_program) { | |
169 | ++ ptr->is_deleted = is_delete; | |
170 | ++ error = 0; | |
171 | ++ goto out; | |
172 | ++ } | |
173 | ++ } | |
174 | ++ | |
175 | ++ if (is_delete) { | |
176 | ++ error = -ENOENT; | |
177 | ++ goto out; | |
178 | ++ } | |
179 | ++ | |
180 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
181 | ++ if (!new_entry) | |
182 | ++ goto out; | |
183 | ++ | |
184 | ++ new_entry->domainname = saved_domainname; | |
185 | ++ new_entry->program = saved_program; | |
186 | ++ new_entry->is_not = is_not; | |
187 | ++ new_entry->is_last_name = is_last_name; | |
188 | ++ list_add_tail_mb(&new_entry->list, &domain_initializer_list); | |
189 | ++ error = 0; | |
190 | ++out: ; | |
191 | ++ | |
192 | ++ mutex_unlock(&mutex); | |
193 | ++ return error; | |
194 | ++} | |
195 | ++ | |
196 | ++/** | |
197 | ++ * tmy_read_domain_initializer_policy - read domain initializer policy. | |
198 | ++ * @head: pointer to "struct io_buffer". | |
199 | ++ * | |
200 | ++ * Returns nonzero if reading incomplete. | |
201 | ++ * Returns zero otherwise. | |
202 | ++ */ | |
203 | ++int tmy_read_domain_initializer_policy(struct io_buffer *head) | |
204 | ++{ | |
205 | ++ struct list_head *pos; | |
206 | ++ list_for_each_cookie(pos, head->read_var2, &domain_initializer_list) { | |
207 | ++ struct domain_initializer_entry *ptr; | |
208 | ++ ptr = list_entry(pos, struct domain_initializer_entry, list); | |
209 | ++ if (ptr->is_deleted) | |
210 | ++ continue; | |
211 | ++ if (ptr->domainname) { | |
212 | ++ if (tmy_io_printf(head, "%s" TMY_INITIALIZE_DOMAIN | |
213 | ++ "%s from %s\n", | |
214 | ++ ptr->is_not ? "no_" : "", | |
215 | ++ ptr->program->name, | |
216 | ++ ptr->domainname->name)) | |
217 | ++ return -ENOMEM; | |
218 | ++ } else { | |
219 | ++ if (tmy_io_printf(head, "%s" TMY_INITIALIZE_DOMAIN | |
220 | ++ "%s\n", | |
221 | ++ ptr->is_not ? "no_" : "", | |
222 | ++ ptr->program->name)) | |
223 | ++ return -ENOMEM; | |
224 | ++ } | |
225 | ++ } | |
226 | ++ return 0; | |
227 | ++} | |
228 | ++ | |
229 | ++/** | |
230 | ++ * tmy_add_domain_initializer_policy - add domain initializer policy | |
231 | ++ * @data: a line to parse. | |
232 | ++ * @is_not: is this overriding? | |
233 | ++ * @is_delete: is this remove request? | |
234 | ++ * | |
235 | ++ * Returns zero on success. | |
236 | ++ * Returns nonzero on failure. | |
237 | ++ * | |
238 | ++ * This function adds or removes a domain initializer entry. | |
239 | ++ */ | |
240 | ++int tmy_add_domain_initializer_policy(char *data, | |
241 | ++ const bool is_not, | |
242 | ++ const bool is_delete) | |
243 | ++{ | |
244 | ++ char *cp = strstr(data, " from "); | |
245 | ++ | |
246 | ++ if (cp) { | |
247 | ++ *cp = '\0'; | |
248 | ++ return tmy_add_domain_initializer_entry(cp + 6, data, is_not, | |
249 | ++ is_delete); | |
250 | ++ } | |
251 | ++ | |
252 | ++ return tmy_add_domain_initializer_entry(NULL, data, is_not, is_delete); | |
253 | ++} | |
254 | ++ | |
255 | ++/* Should I transit to a domain under "<kernel>" domain? */ | |
256 | ++static int tmy_is_domain_initializer(const struct path_info *domainname, | |
257 | ++ const struct path_info *program, | |
258 | ++ const struct path_info *last_name) | |
259 | ++{ | |
260 | ++ struct domain_initializer_entry *ptr; | |
261 | ++ int flag = 0; | |
262 | ++ list_for_each_entry(ptr, &domain_initializer_list, list) { | |
263 | ++ if (ptr->is_deleted) | |
264 | ++ continue; | |
265 | ++ if (ptr->domainname) { | |
266 | ++ if (!ptr->is_last_name) { | |
267 | ++ if (ptr->domainname != domainname) | |
268 | ++ continue; | |
269 | ++ } else { | |
270 | ++ if (tmy_pathcmp(ptr->domainname, last_name)) | |
271 | ++ continue; | |
272 | ++ } | |
273 | ++ } | |
274 | ++ if (tmy_pathcmp(ptr->program, program)) | |
275 | ++ continue; | |
276 | ++ if (ptr->is_not) | |
277 | ++ return 0; | |
278 | ++ flag = 1; | |
279 | ++ } | |
280 | ++ return flag; | |
281 | ++} | |
282 | ++ | |
283 | ++/************************* DOMAIN KEEPER HANDLER *************************/ | |
284 | ++ | |
285 | ++static LIST_HEAD(domain_keeper_list); | |
286 | ++ | |
287 | ++/* Update domain keeper list. */ | |
288 | ++static int tmy_add_domain_keeper_entry(const char *domainname, | |
289 | ++ const char *program, | |
290 | ++ const bool is_not, | |
291 | ++ const bool is_delete) | |
292 | ++{ | |
293 | ++ struct domain_keeper_entry *new_entry; | |
294 | ++ struct domain_keeper_entry *ptr; | |
295 | ++ const struct path_info *saved_domainname; | |
296 | ++ const struct path_info *saved_program = NULL; | |
297 | ++ static DEFINE_MUTEX(mutex); | |
298 | ++ int error = -ENOMEM; | |
299 | ++ bool is_last_name = 0; | |
300 | ++ | |
301 | ++ if (!tmy_is_domain_def(domainname) && | |
302 | ++ tmy_correct_path(domainname, 1, -1, -1, __FUNCTION__)) | |
303 | ++ is_last_name = 1; | |
304 | ++ | |
305 | ++ else if (!tmy_is_correct_domain(domainname, __FUNCTION__)) | |
306 | ++ return -EINVAL; | |
307 | ++ | |
308 | ++ if (program) { | |
309 | ++ if (!tmy_correct_path(program, 1, -1, -1, __FUNCTION__)) | |
310 | ++ return -EINVAL; | |
311 | ++ | |
312 | ++ saved_program = tmy_save_name(program); | |
313 | ++ if (!saved_program) | |
314 | ++ return -ENOMEM; | |
315 | ++ } | |
316 | ++ | |
317 | ++ saved_domainname = tmy_save_name(domainname); | |
318 | ++ if (!saved_domainname) | |
319 | ++ return -ENOMEM; | |
320 | ++ | |
321 | ++ mutex_lock(&mutex); | |
322 | ++ list_for_each_entry(ptr, &domain_keeper_list, list) { | |
323 | ++ if (ptr->is_not == is_not && | |
324 | ++ ptr->domainname == saved_domainname && | |
325 | ++ ptr->program == saved_program) { | |
326 | ++ ptr->is_deleted = is_delete; | |
327 | ++ error = 0; | |
328 | ++ goto out; | |
329 | ++ } | |
330 | ++ } | |
331 | ++ | |
332 | ++ if (is_delete) { | |
333 | ++ error = -ENOENT; | |
334 | ++ goto out; | |
335 | ++ } | |
336 | ++ | |
337 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
338 | ++ if (!new_entry) | |
339 | ++ goto out; | |
340 | ++ | |
341 | ++ new_entry->domainname = saved_domainname; | |
342 | ++ new_entry->program = saved_program; | |
343 | ++ new_entry->is_not = is_not; | |
344 | ++ new_entry->is_last_name = is_last_name; | |
345 | ++ list_add_tail_mb(&new_entry->list, &domain_keeper_list); | |
346 | ++ error = 0; | |
347 | ++ | |
348 | ++out: ; | |
349 | ++ | |
350 | ++ mutex_unlock(&mutex); | |
351 | ++ return error; | |
352 | ++} | |
353 | ++ | |
354 | ++/** | |
355 | ++ * tmy_add_domain_keeper_policy - add domain keeper policy. | |
356 | ++ * @data: a line to parse. | |
357 | ++ * @is_not: is this overriding? | |
358 | ++ * @is_delete: is this remove request? | |
359 | ++ * | |
360 | ++ * Returns zero on success. | |
361 | ++ * Returns nonzero on failure. | |
362 | ++ * | |
363 | ++ * This function adds or removes a domain keeper entry. | |
364 | ++ * | |
365 | ++ */ | |
366 | ++int tmy_add_domain_keeper_policy(char *data, | |
367 | ++ const bool is_not, | |
368 | ++ const bool is_delete) | |
369 | ++{ | |
370 | ++ char *cp = strstr(data, " from "); | |
371 | ++ | |
372 | ++ if (cp) { | |
373 | ++ *cp = '\0'; | |
374 | ++ return tmy_add_domain_keeper_entry(cp + 6, data, | |
375 | ++ is_not, is_delete); | |
376 | ++ } | |
377 | ++ | |
378 | ++ return tmy_add_domain_keeper_entry(data, NULL, is_not, is_delete); | |
379 | ++} | |
380 | ++ | |
381 | ++/** | |
382 | ++ * tmy_read_domain_keeper_policy - read domain keeper policy. | |
383 | ++ * @head: pointer to "struct io_buffer". | |
384 | ++ * | |
385 | ++ * Returns nonzero if reading incomplete. | |
386 | ++ * Returns zero otherwise. | |
387 | ++ */ | |
388 | ++int tmy_read_domain_keeper_policy(struct io_buffer *head) | |
389 | ++{ | |
390 | ++ struct list_head *pos; | |
391 | ++ list_for_each_cookie(pos, head->read_var2, &domain_keeper_list) { | |
392 | ++ struct domain_keeper_entry *ptr; | |
393 | ++ ptr = list_entry(pos, struct domain_keeper_entry, list); | |
394 | ++ if (ptr->is_deleted) | |
395 | ++ continue; | |
396 | ++ if (ptr->program) { | |
397 | ++ if (tmy_io_printf(head, | |
398 | ++ "%s" TMY_KEEP_DOMAIN | |
399 | ++ "%s from %s\n", | |
400 | ++ ptr->is_not ? "no_" : "", | |
401 | ++ ptr->program->name, | |
402 | ++ ptr->domainname->name)) | |
403 | ++ return -ENOMEM; | |
404 | ++ } else { | |
405 | ++ if (tmy_io_printf(head, | |
406 | ++ "%s" TMY_KEEP_DOMAIN | |
407 | ++ "%s\n", | |
408 | ++ ptr->is_not ? "no_" : "", | |
409 | ++ ptr->domainname->name)) | |
410 | ++ return -ENOMEM; | |
411 | ++ } | |
412 | ++ } | |
413 | ++ return 0; | |
414 | ++} | |
415 | ++ | |
416 | ++/* Should I remain in current domain? */ | |
417 | ++static int tmy_is_domain_keeper(const struct path_info *domainname, | |
418 | ++ const struct path_info *program, | |
419 | ++ const struct path_info *last_name) | |
420 | ++{ | |
421 | ++ struct domain_keeper_entry *ptr; | |
422 | ++ int flag = 0; | |
423 | ++ list_for_each_entry(ptr, &domain_keeper_list, list) { | |
424 | ++ if (ptr->is_deleted) | |
425 | ++ continue; | |
426 | ++ if (!ptr->is_last_name) { | |
427 | ++ if (ptr->domainname != domainname) | |
428 | ++ continue; | |
429 | ++ } else { | |
430 | ++ if (tmy_pathcmp(ptr->domainname, last_name)) | |
431 | ++ continue; | |
432 | ++ } | |
433 | ++ if (ptr->program && tmy_pathcmp(ptr->program, program)) | |
434 | ++ continue; | |
435 | ++ if (ptr->is_not) | |
436 | ++ return 0; | |
437 | ++ flag = 1; | |
438 | ++ } | |
439 | ++ return flag; | |
440 | ++} | |
441 | ++ | |
442 | ++/********************* SYMBOLIC LINKED PROGRAM HANDLER *********************/ | |
443 | ++ | |
444 | ++static LIST_HEAD(alias_list); | |
445 | ++ | |
446 | ++/* Update alias list. */ | |
447 | ++static int tmy_add_alias_entry(const char *original_name, | |
448 | ++ const char *aliased_name, | |
449 | ++ const bool is_delete) | |
450 | ++{ | |
451 | ++ struct alias_entry *new_entry; | |
452 | ++ struct alias_entry *ptr; | |
453 | ++ static DEFINE_MUTEX(mutex); | |
454 | ++ const struct path_info *saved_original_name; | |
455 | ++ const struct path_info *saved_aliased_name; | |
456 | ++ int error = -ENOMEM; | |
457 | ++ | |
458 | ++ if (!tmy_correct_path(original_name, 1, -1, -1, __FUNCTION__) || | |
459 | ++ !tmy_correct_path(aliased_name, 1, -1, -1, __FUNCTION__)) | |
460 | ++ return -EINVAL; /* No patterns allowed. */ | |
461 | ++ | |
462 | ++ saved_original_name = tmy_save_name(original_name); | |
463 | ++ saved_aliased_name = tmy_save_name(aliased_name); | |
464 | ++ if (!saved_original_name || !saved_aliased_name) | |
465 | ++ return -ENOMEM; | |
466 | ++ | |
467 | ++ mutex_lock(&mutex); | |
468 | ++ | |
469 | ++ list_for_each_entry(ptr, &alias_list, list) { | |
470 | ++ if (ptr->original_name == saved_original_name && | |
471 | ++ ptr->aliased_name == saved_aliased_name) { | |
472 | ++ ptr->is_deleted = is_delete; | |
473 | ++ error = 0; | |
474 | ++ goto out; | |
475 | ++ } | |
476 | ++ } | |
477 | ++ | |
478 | ++ if (is_delete) { | |
479 | ++ error = -ENOENT; | |
480 | ++ goto out; | |
481 | ++ } | |
482 | ++ | |
483 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
484 | ++ if (!new_entry) | |
485 | ++ goto out; | |
486 | ++ | |
487 | ++ new_entry->original_name = saved_original_name; | |
488 | ++ new_entry->aliased_name = saved_aliased_name; | |
489 | ++ list_add_tail_mb(&new_entry->list, &alias_list); | |
490 | ++ error = 0; | |
491 | ++out: ; | |
492 | ++ mutex_unlock(&mutex); | |
493 | ++ return error; | |
494 | ++} | |
495 | ++ | |
496 | ++/** | |
497 | ++ * tmy_read_alias_policy - read alias policy. | |
498 | ++ * @head: pointer to "struct io_buffer". | |
499 | ++ * | |
500 | ++ * Returns nonzero if reading incomplete. | |
501 | ++ * Returns zero otherwise. | |
502 | ++ */ | |
503 | ++int tmy_read_alias_policy(struct io_buffer *head) | |
504 | ++{ | |
505 | ++ struct list_head *pos; | |
506 | ++ list_for_each_cookie(pos, head->read_var2, &alias_list) { | |
507 | ++ struct alias_entry *ptr; | |
508 | ++ ptr = list_entry(pos, struct alias_entry, list); | |
509 | ++ if (ptr->is_deleted) | |
510 | ++ continue; | |
511 | ++ if (tmy_io_printf(head, | |
512 | ++ TMY_ALIAS "%s %s\n", | |
513 | ++ ptr->original_name->name, | |
514 | ++ ptr->aliased_name->name)) | |
515 | ++ return -ENOMEM; | |
516 | ++ } | |
517 | ++ return 0; | |
518 | ++} | |
519 | ++ | |
520 | ++/** | |
521 | ++ * tmy_add_alias_policy - add alias policy. | |
522 | ++ * @data: a line to parse. | |
523 | ++ * @is_delete: is this remove request? | |
524 | ++ * | |
525 | ++ * Returns zero on success. | |
526 | ++ * Returns nonzero on failure. | |
527 | ++ * | |
528 | ++ * This function adds or removes an alias entry. | |
529 | ++ */ | |
530 | ++int tmy_add_alias_policy(char *data, const bool is_delete) | |
531 | ++{ | |
532 | ++ char *cp = strchr(data, ' '); | |
533 | ++ | |
534 | ++ if (!cp) | |
535 | ++ return -EINVAL; | |
536 | ++ *cp++ = '\0'; | |
537 | ++ | |
538 | ++ return tmy_add_alias_entry(data, cp, is_delete); | |
539 | ++} | |
540 | ++ | |
541 | ++/************************ DOMAIN AGGREGATOR HANDLER ************************/ | |
542 | ++ | |
543 | ++static LIST_HEAD(aggregator_list); | |
544 | ++ | |
545 | ++/* Update aggregator list. */ | |
546 | ++static int tmy_add_aggregator_entry(const char *original_name, | |
547 | ++ const char *aggregated_name, | |
548 | ++ const bool is_delete) | |
549 | ++{ | |
550 | ++ struct aggregator_entry *new_entry; | |
551 | ++ struct aggregator_entry *ptr; | |
552 | ++ static DEFINE_MUTEX(mutex); | |
553 | ++ const struct path_info *saved_original_name; | |
554 | ++ const struct path_info *saved_aggregated_name; | |
555 | ++ int error = -ENOMEM; | |
556 | ++ | |
557 | ++ if (!tmy_correct_path(original_name, 1, 0, -1, __FUNCTION__) || | |
558 | ++ !tmy_correct_path(aggregated_name, 1, -1, -1, __FUNCTION__)) | |
559 | ++ return -EINVAL; | |
560 | ++ | |
561 | ++ saved_original_name = tmy_save_name(original_name); | |
562 | ++ saved_aggregated_name = tmy_save_name(aggregated_name); | |
563 | ++ if (!saved_original_name || !saved_aggregated_name) | |
564 | ++ return -ENOMEM; | |
565 | ++ | |
566 | ++ mutex_lock(&mutex); | |
567 | ++ | |
568 | ++ list_for_each_entry(ptr, &aggregator_list, list) { | |
569 | ++ if (ptr->original_name == saved_original_name && | |
570 | ++ ptr->aggregated_name == saved_aggregated_name) { | |
571 | ++ ptr->is_deleted = is_delete; | |
572 | ++ error = 0; | |
573 | ++ goto out; | |
574 | ++ } | |
575 | ++ } | |
576 | ++ | |
577 | ++ if (is_delete) { | |
578 | ++ error = -ENOENT; | |
579 | ++ goto out; | |
580 | ++ } | |
581 | ++ | |
582 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
583 | ++ if (!new_entry) | |
584 | ++ goto out; | |
585 | ++ | |
586 | ++ new_entry->original_name = saved_original_name; | |
587 | ++ new_entry->aggregated_name = saved_aggregated_name; | |
588 | ++ list_add_tail_mb(&new_entry->list, &aggregator_list); | |
589 | ++ error = 0; | |
590 | ++out: ; | |
591 | ++ | |
592 | ++ mutex_unlock(&mutex); | |
593 | ++ | |
594 | ++ return error; | |
595 | ++} | |
596 | ++ | |
597 | ++/** | |
598 | ++ * tmy_read_aggregator_policy - read aggregator policy. | |
599 | ++ * @head: pointer to "struct io_buffer". | |
600 | ++ * | |
601 | ++ * Returns nonzero if reading incomplete. | |
602 | ++ * Returns zero otherwise. | |
603 | ++ */ | |
604 | ++int tmy_read_aggregator_policy(struct io_buffer *head) | |
605 | ++{ | |
606 | ++ struct list_head *pos; | |
607 | ++ list_for_each_cookie(pos, head->read_var2, &aggregator_list) { | |
608 | ++ struct aggregator_entry *ptr; | |
609 | ++ ptr = list_entry(pos, struct aggregator_entry, list); | |
610 | ++ if (ptr->is_deleted) | |
611 | ++ continue; | |
612 | ++ if (tmy_io_printf(head, | |
613 | ++ TMY_AGGREGATOR "%s %s\n", | |
614 | ++ ptr->original_name->name, | |
615 | ++ ptr->aggregated_name->name)) | |
616 | ++ return -ENOMEM; | |
617 | ++ } | |
618 | ++ return 0; | |
619 | ++} | |
620 | ++ | |
621 | ++/** | |
622 | ++ * tmy_add_aggregator_policy - add aggregator policy. | |
623 | ++ * @data: a line to parse. | |
624 | ++ * @is_delete: is this remove request? | |
625 | ++ * | |
626 | ++ * Returns zero on success. | |
627 | ++ * Returns nonzero on failure. | |
628 | ++ * | |
629 | ++ * This function adds or removes an aggregator entry. | |
630 | ++ */ | |
631 | ++int tmy_add_aggregator_policy(char *data, const bool is_delete) | |
632 | ++{ | |
633 | ++ char *cp = strchr(data, ' '); | |
634 | ++ | |
635 | ++ if (!cp) | |
636 | ++ return -EINVAL; | |
637 | ++ *cp++ = '\0'; | |
638 | ++ | |
639 | ++ return tmy_add_aggregator_entry(data, cp, is_delete); | |
640 | ++} | |
641 | ++ | |
642 | ++/************************* DOMAIN DELETION HANDLER *************************/ | |
643 | ++ | |
644 | ++/** | |
645 | ++ * tmy_delete_domain - delete a domain. | |
646 | ++ * @domainname0: domainname to delete. | |
647 | ++ * | |
648 | ++ * Returns zero. | |
649 | ++ * | |
650 | ++ * This function deletes domains. | |
651 | ++ * The behavior of deleting domain is like deleting files on Linux's | |
652 | ++ * filesystem. A process transits to different domain upon do_execve(), | |
653 | ++ * and the process can refer the deleted domains after the domain is deleted, | |
654 | ++ * like a process opens a file and the process can read()/write() the deleted | |
655 | ++ * file after the file is deleted. | |
656 | ++ * This avoids processes from crashing due to referring non-existent domains. | |
657 | ++ * Administrator manually terminates processes thet are referring deleted | |
658 | ++ * domains after deleting domains. | |
659 | ++ * Also, undeleting domains is supported. See tmy_undelete_domain(). | |
660 | ++ */ | |
661 | ++int tmy_delete_domain(char *domainname0) | |
662 | ++{ | |
663 | ++ struct domain_info *domain; | |
664 | ++ struct path_info domainname; | |
665 | ++ | |
666 | ++ domainname.name = domainname0; | |
667 | ++ tmy_fill_path_info(&domainname); | |
668 | ++ | |
669 | ++ mutex_lock(&new_domain_assign_lock); | |
670 | ++ /* Is there an active domain? */ /* Never delete KERNEL_DOMAIN */ | |
671 | ++ list_for_each_entry(domain, &domain_list, list) { | |
672 | ++ struct domain_info *domain2; | |
673 | ++ if (domain == &KERNEL_DOMAIN || | |
674 | ++ domain->is_deleted || | |
675 | ++ tmy_pathcmp(domain->domainname, &domainname)) | |
676 | ++ continue; | |
677 | ++ /* Mark already deleted domains as non undeletable. */ | |
678 | ++ list_for_each_entry(domain2, &domain_list, list) { | |
679 | ++ if (!domain2->is_deleted || | |
680 | ++ tmy_pathcmp(domain2->domainname, &domainname)) | |
681 | ++ continue; | |
682 | ++ domain2->is_deleted = 255; | |
683 | ++ } | |
684 | ++ /* Delete and mark active domain as undeletable. */ | |
685 | ++ domain->is_deleted = 1; | |
686 | ++ break; | |
687 | ++ } | |
688 | ++ mutex_unlock(&new_domain_assign_lock); | |
689 | ++ return 0; | |
690 | ++} | |
691 | ++ | |
692 | ++/** | |
693 | ++ * tmy_undelete_domain - undelete a domain. | |
694 | ++ * @domainname0: domainname to undelete. | |
695 | ++ * | |
696 | ++ * Returns pointer to undeleted "struct domain_info" on success. | |
697 | ++ * Returns NULL on failure. | |
698 | ++ * | |
699 | ++ * This function undeletes domains. | |
700 | ++ * Not only the domain previously deleted by tmy_delete_domain() | |
701 | ++ * but also all domains deleted by tmy_delete_domain() are undeletable. | |
702 | ++ * If there is no deleted domain named @domainname0 or | |
703 | ++ * a not-yet-deleted domain named @domainname0 exists, undelete fails. | |
704 | ++ * Otherwise, previously deleted domain named @domainname0 is undeleted. | |
705 | ++ */ | |
706 | ++struct domain_info *tmy_undelete_domain(const char *domainname0) | |
707 | ++{ | |
708 | ++ struct domain_info *domain; | |
709 | ++ struct domain_info *candidate_domain = NULL; | |
710 | ++ struct path_info domainname; | |
711 | ++ | |
712 | ++ domainname.name = domainname0; | |
713 | ++ tmy_fill_path_info(&domainname); | |
714 | ++ | |
715 | ++ mutex_lock(&new_domain_assign_lock); | |
716 | ++ | |
717 | ++ list_for_each_entry(domain, &domain_list, list) { | |
718 | ++ if (tmy_pathcmp(&domainname, domain->domainname)) | |
719 | ++ continue; | |
720 | ++ | |
721 | ++ if (!domain->is_deleted) { | |
722 | ++ /* This domain is active. I can't undelete. */ | |
723 | ++ candidate_domain = NULL; | |
724 | ++ break; | |
725 | ++ } | |
726 | ++ | |
727 | ++ /* Is this domain undeletable? */ | |
728 | ++ if (domain->is_deleted == 1) | |
729 | ++ candidate_domain = domain; | |
730 | ++ } | |
731 | ++ if (candidate_domain) | |
732 | ++ candidate_domain->is_deleted = 0; | |
733 | ++ | |
734 | ++ mutex_unlock(&new_domain_assign_lock); | |
735 | ++ | |
736 | ++ return candidate_domain; | |
737 | ++} | |
738 | ++ | |
739 | ++/************************ DOMAIN TRANSITION HANDLER ************************/ | |
740 | ++ | |
741 | ++/** | |
742 | ++ * tmy_find_domain - find a domain with given domainname. | |
743 | ++ * @domainname0: the domainname to find. | |
744 | ++ * | |
745 | ++ * Returns pointer to "struct domain_info" on success. | |
746 | ++ * Returns NULL on failure. | |
747 | ++ * | |
748 | ++ * This function does not create a new domain | |
749 | ++ * if a domain named @domainname0 does not exist. | |
750 | ++ */ | |
751 | ++struct domain_info *tmy_find_domain(const char *domainname0) | |
752 | ++{ | |
753 | ++ struct domain_info *domain; | |
754 | ++ static int first = 1; | |
755 | ++ struct path_info domainname; | |
756 | ++ | |
757 | ++ domainname.name = domainname0; | |
758 | ++ tmy_fill_path_info(&domainname); | |
759 | ++ | |
760 | ++ if (first) { | |
761 | ++ KERNEL_DOMAIN.domainname = tmy_save_name(TMY_ROOT_NAME); | |
762 | ++ first = 0; | |
763 | ++ } | |
764 | ++ | |
765 | ++ list_for_each_entry(domain, &domain_list, list) { | |
766 | ++ if (!domain->is_deleted && | |
767 | ++ !tmy_pathcmp(&domainname, domain->domainname)) | |
768 | ++ return domain; | |
769 | ++ } | |
770 | ++ | |
771 | ++ return NULL; | |
772 | ++} | |
773 | ++ | |
774 | ++/** | |
775 | ++ * tmy_new_domain - find or assign a domain with given domainname. | |
776 | ++ * @domainname: the domainname to find. | |
777 | ++ * @profile: profile number to assign if newly created. | |
778 | ++ * | |
779 | ++ * Returns pointer to "struct domain_info" on success. | |
780 | ++ * Returns NULL on failure. | |
781 | ++ * | |
782 | ++ * This function creates a new domain if a domain named @domainname0 | |
783 | ++ * does not exist. | |
784 | ++ */ | |
785 | ++struct domain_info *tmy_new_domain(const char *domainname, | |
786 | ++ const u8 profile) | |
787 | ++{ | |
788 | ++ struct domain_info *domain = NULL; | |
789 | ++ const struct path_info *saved_domainname; | |
790 | ++ | |
791 | ++ mutex_lock(&new_domain_assign_lock); | |
792 | ++ | |
793 | ++ domain = tmy_find_domain(domainname); | |
794 | ++ if (domain) | |
795 | ++ goto out; | |
796 | ++ | |
797 | ++ if (!tmy_is_correct_domain(domainname, __FUNCTION__)) | |
798 | ++ goto out; | |
799 | ++ | |
800 | ++ saved_domainname = tmy_save_name(domainname); | |
801 | ++ if (!saved_domainname) | |
802 | ++ goto out; | |
803 | ++ | |
804 | ++ /* Can I reuse memory of deleted domain? */ | |
805 | ++ list_for_each_entry(domain, &domain_list, list) { | |
806 | ++ struct task_struct *p; | |
807 | ++ struct acl_info *ptr; | |
808 | ++ int flag; | |
809 | ++ | |
810 | ++ if (!domain->is_deleted || | |
811 | ++ domain->domainname != saved_domainname) | |
812 | ++ continue; | |
813 | ++ flag = 0; | |
814 | ++ | |
815 | ++ /***** CRITICAL SECTION START *****/ | |
816 | ++ read_lock(&tasklist_lock); | |
817 | ++ for_each_process(p) { | |
818 | ++ /* "struct task_struct"->security is not NULL. */ | |
819 | ++ if (((struct tmy_security *) p->security)->domain | |
820 | ++ == domain) { | |
821 | ++ flag = 1; | |
822 | ++ break; | |
823 | ++ } | |
824 | ++ } | |
825 | ++ read_unlock(&tasklist_lock); | |
826 | ++ /***** CRITICAL SECTION END *****/ | |
827 | ++ | |
828 | ++ /* Somebody is still referring this deleted domain. */ | |
829 | ++ if (flag) | |
830 | ++ continue; | |
831 | ++ | |
832 | ++ /* OK. Let's reuse memory for this deleted domain. */ | |
833 | ++ | |
834 | ++ /* Delete all entries in this deleted domain. */ | |
835 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) | |
836 | ++ ptr->is_deleted = 1; | |
837 | ++ | |
838 | ++ domain->profile = profile; | |
839 | ++ domain->quota_warned = 0; | |
840 | ++ | |
841 | ++ mb(); /* Avoid out-of-order execution. */ | |
842 | ++ /* Undelete this deleted domain. */ | |
843 | ++ domain->is_deleted = 0; | |
844 | ++ goto out; | |
845 | ++ } | |
846 | ++ | |
847 | ++ /* No memory reusable. Create using new memory. */ | |
848 | ++ domain = tmy_alloc_element(sizeof(*domain)); | |
849 | ++ if (domain) { | |
850 | ++ INIT_LIST_HEAD(&domain->acl_info_list); | |
851 | ++ domain->domainname = saved_domainname; | |
852 | ++ domain->profile = profile; | |
853 | ++ list_add_tail_mb(&domain->list, &domain_list); | |
854 | ++ } | |
855 | ++out: ; | |
856 | ++ mutex_unlock(&new_domain_assign_lock); | |
857 | ++ | |
858 | ++ return domain; | |
859 | ++} | |
860 | ++ | |
861 | ++/* Convert non ASCII printable characters to ASCII printable characters. */ | |
862 | ++static int tmy_escape(char *dest, const char *src, int dest_len) | |
863 | ++{ | |
864 | ++ while (*src) { | |
865 | ++ const unsigned char c = *(const unsigned char *) src; | |
866 | ++ | |
867 | ++ if (c == '\\') { | |
868 | ++ dest_len -= 2; | |
869 | ++ if (dest_len <= 0) | |
870 | ++ goto out; | |
871 | ++ *dest++ = '\\'; | |
872 | ++ *dest++ = '\\'; | |
873 | ++ } else if (c > ' ' && c < 127) { | |
874 | ++ if (--dest_len <= 0) | |
875 | ++ goto out; | |
876 | ++ *dest++ = c; | |
877 | ++ } else { | |
878 | ++ dest_len -= 4; | |
879 | ++ if (dest_len <= 0) | |
880 | ++ goto out; | |
881 | ++ *dest++ = '\\'; | |
882 | ++ *dest++ = (c >> 6) + '0'; | |
883 | ++ *dest++ = ((c >> 3) & 7) + '0'; | |
884 | ++ *dest++ = (c & 7) + '0'; | |
885 | ++ } | |
886 | ++ src++; | |
887 | ++ } | |
888 | ++ | |
889 | ++ if (--dest_len <= 0) | |
890 | ++ goto out; | |
891 | ++ *dest = '\0'; | |
892 | ++ | |
893 | ++ return 0; | |
894 | ++out: ; | |
895 | ++ return -ENOMEM; | |
896 | ++} | |
897 | ++ | |
898 | ++/* Get argv[0] of "struct linux_binprm". */ | |
899 | ++static char *tmy_get_argv0(struct linux_binprm *bprm) | |
900 | ++{ | |
901 | ++ char *arg_ptr; | |
902 | ++ int arg_len = 0; | |
903 | ++ unsigned long pos = bprm->p; | |
904 | ++ int i = pos / PAGE_SIZE; | |
905 | ++ int offset = pos % PAGE_SIZE; | |
906 | ++ | |
907 | ++ if (bprm->argc <= 0) | |
908 | ++ return NULL; | |
909 | ++ | |
910 | ++ arg_ptr = tmy_alloc(PAGE_SIZE); | |
911 | ++ | |
912 | ++ if (!arg_ptr) | |
913 | ++ goto out; | |
914 | ++ | |
915 | ++ while (1) { | |
916 | ++ struct page *page; | |
917 | ++ const char *kaddr; | |
918 | ++ char *tmp_arg; | |
919 | ++ | |
920 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU) | |
921 | ++ if (get_user_pages(current, bprm->mm, pos, | |
922 | ++ 1, 0, 1, &page, NULL) <= 0) | |
923 | ++ goto out; | |
924 | ++#else | |
925 | ++ page = bprm->page[i]; | |
926 | ++#endif | |
927 | ++ kaddr = kmap(page); | |
928 | ++ if (!kaddr) { /* Mapping failed. */ | |
929 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU) | |
930 | ++ put_page(page); | |
931 | ++#endif | |
932 | ++ goto out; | |
933 | ++ } | |
934 | ++ | |
935 | ++ memmove(arg_ptr + arg_len, kaddr + offset, PAGE_SIZE - offset); | |
936 | ++ kunmap(page); | |
937 | ++ | |
938 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) && defined(CONFIG_MMU) | |
939 | ++ put_page(page); | |
940 | ++ pos += PAGE_SIZE - offset; | |
941 | ++#endif | |
942 | ++ | |
943 | ++ arg_len += PAGE_SIZE - offset; | |
944 | ++ | |
945 | ++ if (memchr(arg_ptr, '\0', arg_len)) | |
946 | ++ break; | |
947 | ++ | |
948 | ++ tmp_arg = tmy_alloc(arg_len + PAGE_SIZE); | |
949 | ++ if (!tmp_arg) | |
950 | ++ goto out; | |
951 | ++ | |
952 | ++ memmove(tmp_arg, arg_ptr, arg_len); | |
953 | ++ tmy_free(arg_ptr); | |
954 | ++ arg_ptr = tmp_arg; | |
955 | ++ i++; | |
956 | ++ offset = 0; | |
957 | ++ } | |
958 | ++ return arg_ptr; | |
959 | ++out: ; | |
960 | ++ tmy_free(arg_ptr); | |
961 | ++ | |
962 | ++ return NULL; | |
963 | ++} | |
964 | ++ | |
965 | ++/** | |
966 | ++ * tmy_find_next_domain - find a domain to transit to if do_execve() succeeds. | |
967 | ++ * @bprm: pointer to "struct linux_binprm". | |
968 | ++ * @next_domain: pointer to pointer to "struct domain_info". | |
969 | ++ * | |
970 | ++ * Returns zero if success. @next_domain receives new domain to transit to. | |
971 | ++ * Returns nonzero on failure. | |
972 | ++ * | |
973 | ++ * This function handles TOMOYO Linux's domain transition. | |
974 | ++ * New domains are automatically created unless the domain the caller process | |
975 | ++ * belongs to is assigned a profile for enforcing mode. | |
976 | ++ */ | |
977 | ++int tmy_find_next_domain(struct linux_binprm *bprm, | |
978 | ++ struct domain_info **next_domain) | |
979 | ++{ | |
980 | ++ /* This function assumes that the size of buffer returned */ | |
981 | ++ /* by tmy_realpath() = TMY_MAX_PATHNAME_LEN. */ | |
982 | ++ struct domain_info *old_domain = TMY_SECURITY->domain; | |
983 | ++ struct domain_info *domain = NULL; | |
984 | ++ const char *old_domain_name = old_domain->domainname->name; | |
985 | ++ const char *original_name = bprm->filename; | |
986 | ++ struct file *filp = bprm->file; | |
987 | ++ char *new_domain_name = NULL; | |
988 | ++ char *real_program_name = NULL; | |
989 | ++ char *symlink_program_name = NULL; | |
990 | ++ const bool is_enforce = (tmy_flags(TMY_MAC_FOR_FILE) == 3); | |
991 | ++ int retval; | |
992 | ++ struct path_info r; | |
993 | ++ struct path_info s; | |
994 | ++ struct path_info l; | |
995 | ++ | |
996 | ++ /* | |
997 | ++ * Built-in initializers. | |
998 | ++ * This is needed because policies are not loaded | |
999 | ++ * until starting /sbin/init . | |
1000 | ++ */ | |
1001 | ++ static int first = 1; | |
1002 | ++ if (first) { | |
1003 | ++ tmy_add_domain_initializer_entry(NULL, "/sbin/hotplug", 0, 0); | |
1004 | ++ tmy_add_domain_initializer_entry(NULL, "/sbin/modprobe", 0, 0); | |
1005 | ++ tmy_add_domain_initializer_entry(NULL, "/sbin/udevd", 0, 0); | |
1006 | ++ first = 0; | |
1007 | ++ } | |
1008 | ++ | |
1009 | ++ /* Get realpath of program. */ | |
1010 | ++ /* I hope tmy_realpath() won't fail with -ENOMEM. */ | |
1011 | ++ retval = -ENOENT; | |
1012 | ++ real_program_name = tmy_realpath(original_name); | |
1013 | ++ | |
1014 | ++ if (!real_program_name) | |
1015 | ++ goto out; | |
1016 | ++ | |
1017 | ++ /* Get realpath of symbolic link. */ | |
1018 | ++ symlink_program_name = tmy_realpath_nofollow(original_name); | |
1019 | ++ if (!symlink_program_name) | |
1020 | ++ goto out; | |
1021 | ++ | |
1022 | ++ r.name = real_program_name; | |
1023 | ++ tmy_fill_path_info(&r); | |
1024 | ++ s.name = symlink_program_name; | |
1025 | ++ tmy_fill_path_info(&s); | |
1026 | ++ l.name = strrchr(old_domain_name, ' '); | |
1027 | ++ | |
1028 | ++ if (l.name) | |
1029 | ++ l.name++; | |
1030 | ++ else | |
1031 | ++ l.name = old_domain_name; | |
1032 | ++ tmy_fill_path_info(&l); | |
1033 | ++ | |
1034 | ++ /* Check 'alias' directive. */ | |
1035 | ++ if (tmy_pathcmp(&r, &s)) { | |
1036 | ++ struct alias_entry *ptr; | |
1037 | ++ | |
1038 | ++ /* Is this program allowed to be called via symbolic links? */ | |
1039 | ++ list_for_each_entry(ptr, &alias_list, list) { | |
1040 | ++ if (ptr->is_deleted || | |
1041 | ++ tmy_pathcmp(&r, ptr->original_name) || | |
1042 | ++ tmy_pathcmp(&s, ptr->aliased_name)) | |
1043 | ++ continue; | |
1044 | ++ memset(real_program_name, 0, TMY_MAX_PATHNAME_LEN); | |
1045 | ++ strncpy(real_program_name, | |
1046 | ++ ptr->aliased_name->name, | |
1047 | ++ TMY_MAX_PATHNAME_LEN - 1); | |
1048 | ++ tmy_fill_path_info(&r); | |
1049 | ++ break; | |
1050 | ++ } | |
1051 | ++ } | |
1052 | ++ | |
1053 | ++ /* Compare basename of real_program_name and argv[0] */ | |
1054 | ++ if (bprm->argc > 0 && tmy_flags(TMY_MAC_FOR_ARGV0)) { | |
1055 | ++ | |
1056 | ++ char *org_argv0 = tmy_get_argv0(bprm); | |
1057 | ++ | |
1058 | ++ retval = -ENOMEM; | |
1059 | ++ if (org_argv0) { | |
1060 | ++ | |
1061 | ++ const int len = strlen(org_argv0); | |
1062 | ++ char *printable_argv0 = tmy_alloc(len * 4 + 8); | |
1063 | ++ | |
1064 | ++ if (printable_argv0 && | |
1065 | ++ !tmy_escape(printable_argv0, org_argv0, | |
1066 | ++ len * 4 + 8)) { | |
1067 | ++ const char *base_argv0; | |
1068 | ++ const char *base_filename; | |
1069 | ++ | |
1070 | ++ base_argv0 = strrchr(printable_argv0, '/'); | |
1071 | ++ if (!base_argv0) | |
1072 | ++ base_argv0 = printable_argv0; | |
1073 | ++ else | |
1074 | ++ base_argv0++; | |
1075 | ++ | |
1076 | ++ base_filename = strrchr(real_program_name, '/'); | |
1077 | ++ if (!base_filename) | |
1078 | ++ base_filename = real_program_name; | |
1079 | ++ else | |
1080 | ++ base_filename++; | |
1081 | ++ | |
1082 | ++ if (strcmp(base_argv0, base_filename)) | |
1083 | ++ retval = tmy_argv0_perm(&r, base_argv0); | |
1084 | ++ else | |
1085 | ++ retval = 0; | |
1086 | ++ } | |
1087 | ++ | |
1088 | ++ tmy_free(printable_argv0); | |
1089 | ++ tmy_free(org_argv0); | |
1090 | ++ } | |
1091 | ++ | |
1092 | ++ if (retval) | |
1093 | ++ goto out; | |
1094 | ++ | |
1095 | ++ } | |
1096 | ++ | |
1097 | ++ | |
1098 | ++ /* Check 'aggregator' directive. */ | |
1099 | ++ { | |
1100 | ++ struct aggregator_entry *ptr; | |
1101 | ++ | |
1102 | ++ /* Is this program allowed to be aggregated? */ | |
1103 | ++ list_for_each_entry(ptr, &aggregator_list, list) { | |
1104 | ++ if (ptr->is_deleted || | |
1105 | ++ !tmy_path_match(&r, ptr->original_name)) | |
1106 | ++ continue; | |
1107 | ++ memset(real_program_name, 0, TMY_MAX_PATHNAME_LEN); | |
1108 | ++ strncpy(real_program_name, | |
1109 | ++ ptr->aggregated_name->name, | |
1110 | ++ TMY_MAX_PATHNAME_LEN - 1); | |
1111 | ++ tmy_fill_path_info(&r); | |
1112 | ++ break; | |
1113 | ++ } | |
1114 | ++ } | |
1115 | ++ | |
1116 | ++ /* Check execute permission. */ | |
1117 | ++ retval = tmy_exec_perm(&r, filp); | |
1118 | ++ if (retval < 0) | |
1119 | ++ goto out; | |
1120 | ++ | |
1121 | ++ /* Allocate memory for calcurating domain name. */ | |
1122 | ++ retval = -ENOMEM; | |
1123 | ++ new_domain_name = tmy_alloc(TMY_MAX_PATHNAME_LEN + 16); | |
1124 | ++ if (!new_domain_name) | |
1125 | ++ goto out; | |
1126 | ++ | |
1127 | ++ if (tmy_is_domain_initializer(old_domain->domainname, &r, &l)) | |
1128 | ++ /* Transit to the child of KERNEL_DOMAIN domain. */ | |
1129 | ++ snprintf(new_domain_name, TMY_MAX_PATHNAME_LEN + 1, | |
1130 | ++ TMY_ROOT_NAME " " "%s", real_program_name); | |
1131 | ++ else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) | |
1132 | ++ /* | |
1133 | ++ * Needn't to transit from kernel domain | |
1134 | ++ * before starting /sbin/init . | |
1135 | ++ * But transit from kernel domain if executing initializers, | |
1136 | ++ * for they might start before /sbin/init . | |
1137 | ++ */ | |
1138 | ++ domain = old_domain; | |
1139 | ++ else if (tmy_is_domain_keeper(old_domain->domainname, &r, &l)) | |
1140 | ++ /* Keep current domain. */ | |
1141 | ++ domain = old_domain; | |
1142 | ++ else | |
1143 | ++ /* Normal domain transition. */ | |
1144 | ++ snprintf(new_domain_name, | |
1145 | ++ TMY_MAX_PATHNAME_LEN + 1, | |
1146 | ++ "%s %s", | |
1147 | ++ old_domain_name, | |
1148 | ++ real_program_name); | |
1149 | ++ | |
1150 | ++ if (domain || strlen(new_domain_name) >= TMY_MAX_PATHNAME_LEN) | |
1151 | ++ goto ok; | |
1152 | ++ | |
1153 | ++ if (is_enforce) { | |
1154 | ++ domain = tmy_find_domain(new_domain_name); | |
1155 | ++ if (!domain && | |
1156 | ++ tmy_supervisor("#Need to create domain\n%s\n", | |
1157 | ++ new_domain_name) == 0) { | |
1158 | ++ const u8 profile = TMY_SECURITY->domain->profile; | |
1159 | ++ domain = tmy_new_domain(new_domain_name, profile); | |
1160 | ++ } | |
1161 | ++ } else { | |
1162 | ++ const u8 profile = TMY_SECURITY->domain->profile; | |
1163 | ++ domain = tmy_new_domain(new_domain_name, profile); | |
1164 | ++ } | |
1165 | ++ | |
1166 | ++ok: ; | |
1167 | ++ | |
1168 | ++ if (!domain) { | |
1169 | ++ printk(KERN_INFO "TOMOYO-ERROR: Domain '%s' not defined.\n", | |
1170 | ++ new_domain_name); | |
1171 | ++ if (is_enforce) | |
1172 | ++ retval = -EPERM; | |
1173 | ++ } else | |
1174 | ++ retval = 0; | |
1175 | ++out: ; | |
1176 | ++ | |
1177 | ++ tmy_free(new_domain_name); | |
1178 | ++ tmy_free(real_program_name); | |
1179 | ++ tmy_free(symlink_program_name); | |
1180 | ++ *next_domain = domain ? domain : old_domain; | |
1181 | ++ | |
1182 | ++ return retval; | |
1183 | ++} |
@@ -0,0 +1,776 @@ | ||
1 | +Data structures and prototype defitions for TOMOYO Linux. | |
2 | + | |
3 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
4 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | + security/tomoyo/include/realpath.h | 45 ++ | |
6 | + security/tomoyo/include/tomoyo.h | 717 +++++++++++++++++++++++++++++++++++++ | |
7 | + 2 files changed, 762 insertions(+) | |
8 | + | |
9 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
10 | ++++ linux-2.6.23/security/tomoyo/include/realpath.h 2007-11-21 09:30:48.805755344 +0900 | |
11 | +@@ -0,0 +1,45 @@ | |
12 | ++/* | |
13 | ++ * security/tomoyo/include/realpath.h | |
14 | ++ * | |
15 | ++ * Get the canonicalized absolute pathnames. | |
16 | ++ * The basis for TOMOYO. | |
17 | ++ */ | |
18 | ++ | |
19 | ++#ifndef _TMY_REALPATH_H | |
20 | ++#define _TMY_REALPATH_H | |
21 | ++ | |
22 | ++#include "tomoyo.h" | |
23 | ++ | |
24 | ++struct path_info; | |
25 | ++ | |
26 | ++/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ | |
27 | ++int tmy_realpath_dentry2(struct dentry *dentry, | |
28 | ++ struct vfsmount *mnt, | |
29 | ++ char *newname, | |
30 | ++ int newname_len); | |
31 | ++ | |
32 | ++/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ | |
33 | ++/* These functions use tmy_alloc(), so caller must tmy_free() */ | |
34 | ++/* if these functions didn't return NULL. */ | |
35 | ++char *tmy_realpath(const char *pathname); | |
36 | ++char *tmy_realpath_nofollow(const char *pathname); | |
37 | ++char *tmy_realpath_dentry(struct dentry *dentry, struct vfsmount *mnt); | |
38 | ++ | |
39 | ++/* Allocate memory for structures. */ | |
40 | ++/* The RAM is chunked, so NEVER try to kfree() the returned pointer. */ | |
41 | ++void *tmy_alloc_element(const unsigned int size); | |
42 | ++ | |
43 | ++/* Get used RAM size for tmy_alloc_elements(). */ | |
44 | ++unsigned int tmy_get_memory_used_for_elements(void); | |
45 | ++ | |
46 | ++/* Keep the given name on the RAM. */ | |
47 | ++/* The RAM is shared, so NEVER try to modify or kfree() the returned name. */ | |
48 | ++const struct path_info *tmy_save_name(const char *name); | |
49 | ++ | |
50 | ++/* Get used RAM size for tmy_save_name(). */ | |
51 | ++unsigned int tmy_get_memory_used_for_save_name(void); | |
52 | ++ | |
53 | ++unsigned int tmy_get_memory_used_for_dynamic(void); | |
54 | ++char *sysctlpath_from_table(struct ctl_table *table); | |
55 | ++ | |
56 | ++#endif | |
57 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
58 | ++++ linux-2.6.23/security/tomoyo/include/tomoyo.h 2007-11-21 09:31:22.958563328 +0900 | |
59 | +@@ -0,0 +1,717 @@ | |
60 | ++/* | |
61 | ++ * security/tomoyo/include/tomoyo.h | |
62 | ++ * | |
63 | ++ * Header for TOMOYO Linux. | |
64 | ++ */ | |
65 | ++ | |
66 | ++#ifndef _TOMOYO_H | |
67 | ++#define _TOMOYO_H | |
68 | ++ | |
69 | ++#define TOMOYO_VERSION_CODE "2.1.1" | |
70 | ++ | |
71 | ++#include <linux/kernel.h> | |
72 | ++#include <linux/sched.h> | |
73 | ++ | |
74 | ++#include <asm/ioctls.h> | |
75 | ++#include <linux/audit.h> | |
76 | ++#include <linux/binfmts.h> | |
77 | ++#include <linux/delay.h> | |
78 | ++#include <linux/file.h> | |
79 | ++#include <linux/highmem.h> | |
80 | ++#include <linux/init.h> | |
81 | ++#include <linux/mm.h> | |
82 | ++#include <linux/module.h> | |
83 | ++#include <linux/mount.h> | |
84 | ++#include <linux/namei.h> | |
85 | ++#include <linux/net.h> | |
86 | ++#include <linux/poll.h> | |
87 | ++#include <linux/proc_fs.h> | |
88 | ++#include <linux/security.h> | |
89 | ++#include <linux/seq_file.h> | |
90 | ++#include <linux/slab.h> | |
91 | ++#include <linux/smp_lock.h> | |
92 | ++#include <linux/string.h> | |
93 | ++#include <linux/sysctl.h> | |
94 | ++#include <linux/utime.h> | |
95 | ++#include <linux/version.h> | |
96 | ++#include <net/ip.h> | |
97 | ++#include <net/ipv6.h> | |
98 | ++#include <stdarg.h> | |
99 | ++ | |
100 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) | |
101 | ++#include <asm/uaccess.h> | |
102 | ++#else | |
103 | ++#include <linux/uaccess.h> | |
104 | ++#endif | |
105 | ++ | |
106 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) | |
107 | ++#include <asm/irq.h> | |
108 | ++#else | |
109 | ++#include <linux/hardirq.h> | |
110 | ++#endif | |
111 | ++ | |
112 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
113 | ++#include <linux/namespace.h> | |
114 | ++#else | |
115 | ++#include <linux/mount.h> | |
116 | ++#include <linux/mnt_namespace.h> | |
117 | ++#endif | |
118 | ++ | |
119 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
120 | ++typedef _Bool bool; | |
121 | ++#endif | |
122 | ++ | |
123 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | |
124 | ++#define mutex semaphore | |
125 | ++#define mutex_init(mutex) init_MUTEX(mutex) | |
126 | ++#define mutex_lock(mutex) down(mutex) | |
127 | ++#define mutex_unlock(mutex) up(mutex) | |
128 | ++#define mutex_lock_interruptible(mutex) down_interruptible(mutex) | |
129 | ++#define DEFINE_MUTEX(mutexname) DECLARE_MUTEX(mutexname) | |
130 | ++#endif | |
131 | ++ | |
132 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) | |
133 | ++#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED | |
134 | ++#endif | |
135 | ++ | |
136 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) | |
137 | ++static inline void *kzalloc(size_t size, int flags) | |
138 | ++{ | |
139 | ++ void *ret = kmalloc(size, flags); | |
140 | ++ if (ret) | |
141 | ++ memset(ret, 0, size); | |
142 | ++ return ret; | |
143 | ++} | |
144 | ++#endif | |
145 | ++ | |
146 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16) | |
147 | ++#define NIPQUAD_FMT "%u.%u.%u.%u" | |
148 | ++#endif | |
149 | ++ | |
150 | ++/** | |
151 | ++ * list_for_each_cookie - iterate over a list with cookie. | |
152 | ++ * @pos: the &struct list_head to use as a loop cursor. | |
153 | ++ * @cookie: the &struct list_head to use as a cookie. | |
154 | ++ * @head: the head for your list. | |
155 | ++ * | |
156 | ++ * Same with list_for_each except that this primitive uses cookie | |
157 | ++ * so that we can continue iteration. | |
158 | ++ */ | |
159 | ++#define list_for_each_cookie(pos, cookie, head) \ | |
160 | ++ for ((cookie) || ((cookie) = (head)), pos = (cookie)->next; \ | |
161 | ++ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ | |
162 | ++ (cookie) = pos, pos = pos->next) | |
163 | ++ | |
164 | ++/** | |
165 | ++ * list_add_tail_mb - add a new entry with memory barrier. | |
166 | ++ * @new: new entry to be added. | |
167 | ++ * @head: list head to add it before. | |
168 | ++ * | |
169 | ++ * Same with list_add_tail_rcu() except that this primitive uses mb() | |
170 | ++ * so that we can traverse forwards using list_for_each() and | |
171 | ++ * list_for_each_cookie(). | |
172 | ++ */ | |
173 | ++static inline void list_add_tail_mb(struct list_head *new, | |
174 | ++ struct list_head *head) | |
175 | ++{ | |
176 | ++ struct list_head *prev = head->prev; | |
177 | ++ struct list_head *next = head; | |
178 | ++ new->next = next; | |
179 | ++ new->prev = prev; | |
180 | ++ mb(); /* Avoid out-of-order execution. */ | |
181 | ++ next->prev = new; | |
182 | ++ prev->next = new; | |
183 | ++} | |
184 | ++ | |
185 | ++extern struct seq_operations mounts_op; | |
186 | ++extern struct mutex domain_acl_lock; | |
187 | ++extern bool sbin_init_started; | |
188 | ++ | |
189 | ++struct tmy_security { | |
190 | ++ struct domain_info *domain; | |
191 | ++ struct domain_info *prev_domain; | |
192 | ++ u32 flags; | |
193 | ++}; | |
194 | ++ | |
195 | ++#define TMY_SECURITY ((struct tmy_security *) current->security) | |
196 | ++ | |
197 | ++struct path_info { | |
198 | ++ const char *name; | |
199 | ++ u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
200 | ++ u16 total_len; /* = strlen(name) */ | |
201 | ++ u16 const_len; /* = tmy_const_part_length(name) */ | |
202 | ++ bool is_dir; /* = tmy_strendswith(name, "/") */ | |
203 | ++ bool is_patterned; /* = PathContainsPattern(name) */ | |
204 | ++ u16 depth; /* = tmy_path_depth(name) */ | |
205 | ++}; | |
206 | ++ | |
207 | ++#define TMY_MAX_PATHNAME_LEN 4000 | |
208 | ++ | |
209 | ++struct path_group_member { | |
210 | ++ struct list_head list; | |
211 | ++ const struct path_info *member_name; | |
212 | ++ bool is_deleted; | |
213 | ++}; | |
214 | ++ | |
215 | ++struct path_group_entry { | |
216 | ++ struct list_head list; | |
217 | ++ const struct path_info *group_name; | |
218 | ++ struct list_head path_group_member_list; | |
219 | ++}; | |
220 | ++ | |
221 | ++ | |
222 | ++struct mini_stat { | |
223 | ++ uid_t uid; | |
224 | ++ gid_t gid; | |
225 | ++ ino_t ino; | |
226 | ++}; | |
227 | ++struct dentry; | |
228 | ++struct vfsmount; | |
229 | ++struct obj_info { | |
230 | ++ bool validate_done; | |
231 | ++ bool path1_valid; | |
232 | ++ bool path1_parent_valid; | |
233 | ++ bool path2_parent_valid; | |
234 | ++ struct dentry *path1_dentry; | |
235 | ++ struct vfsmount *path1_vfsmnt; | |
236 | ++ struct dentry *path2_dentry; | |
237 | ++ struct vfsmount *path2_vfsmnt; | |
238 | ++ struct mini_stat path1_stat; | |
239 | ++ /* I don't handle path2_stat for rename operation. */ | |
240 | ++ struct mini_stat path1_parent_stat; | |
241 | ++ struct mini_stat path2_parent_stat; | |
242 | ++}; | |
243 | ++struct condition_list; | |
244 | ++ | |
245 | ++struct linux_binprm; | |
246 | ++struct pt_regs; | |
247 | ++ | |
248 | ++/* | |
249 | ++ * TOMOYO uses the following structures. | |
250 | ++ * Memory allocated for these structures are never kfree()ed. | |
251 | ++ * Since no locks are used for reading, | |
252 | ++ * assignment must be performed atomically. | |
253 | ++ */ | |
254 | ++ | |
255 | ++/************************ The structure for domains. ************************/ | |
256 | ++ | |
257 | ++struct acl_info { | |
258 | ++ struct list_head list; | |
259 | ++ const struct condition_list *cond; | |
260 | ++ u8 type; | |
261 | ++ bool is_deleted; | |
262 | ++} __attribute__((__packed__)); | |
263 | ++ | |
264 | ++struct domain_info { | |
265 | ++ struct list_head list; | |
266 | ++ struct list_head acl_info_list; | |
267 | ++ const struct path_info *domainname; /* Never NULL. */ | |
268 | ++ u8 profile; | |
269 | ++ u8 is_deleted; /* 0: active, 1:deleted, 255:deleted-permanently */ | |
270 | ++ bool quota_warned; | |
271 | ++}; | |
272 | ++ | |
273 | ++#define TMY_MAX_PROFILES 256 | |
274 | ++ | |
275 | ++struct file_acl { | |
276 | ++ /* type = TMY_TYPE_FILE_ACL */ | |
277 | ++ struct acl_info head; | |
278 | ++ u8 perm; | |
279 | ++ bool u_is_group; | |
280 | ++ union { | |
281 | ++ const struct path_info *filename; | |
282 | ++ const struct path_group_entry *group; | |
283 | ++ } u; | |
284 | ++}; | |
285 | ++ | |
286 | ++struct argv0_acl { | |
287 | ++ /* type = TMY_TYPE_ARGV0_ACL */ | |
288 | ++ struct acl_info head; | |
289 | ++ const struct path_info *filename; /* Pointer to single pathname. */ | |
290 | ++ const struct path_info *argv0; /* strrchr(argv[0], '/') + 1 */ | |
291 | ++}; | |
292 | ++ | |
293 | ++struct single_acl { | |
294 | ++ /* type = TMY_TYPE_* */ | |
295 | ++ struct acl_info head; | |
296 | ++ bool u_is_group; | |
297 | ++ union { | |
298 | ++ const struct path_info *filename; | |
299 | ++ const struct path_group_entry *group; | |
300 | ++ } u; | |
301 | ++}; | |
302 | ++ | |
303 | ++struct double_acl { | |
304 | ++ /* type = TMY_TYPE_RENAME_ACL or TMY_TYPE_LINK_ACL, */ | |
305 | ++ struct acl_info head; | |
306 | ++ bool u1_is_group; | |
307 | ++ bool u2_is_group; | |
308 | ++ union { | |
309 | ++ const struct path_info *filename1; | |
310 | ++ const struct path_group_entry *group1; | |
311 | ++ } u1; | |
312 | ++ union { | |
313 | ++ const struct path_info *filename2; | |
314 | ++ const struct path_group_entry *group2; | |
315 | ++ } u2; | |
316 | ++}; | |
317 | ++ | |
318 | ++struct address_group_member { | |
319 | ++ struct list_head list; | |
320 | ++ union { | |
321 | ++ u32 ipv4; /* Host byte order */ | |
322 | ++ u16 ipv6[8]; /* Network byte order */ | |
323 | ++ } min, max; | |
324 | ++ bool is_deleted; | |
325 | ++ bool is_ipv6; | |
326 | ++}; | |
327 | ++ | |
328 | ++struct address_group_entry { | |
329 | ++ struct list_head list; | |
330 | ++ const struct path_info *group_name; | |
331 | ++ struct list_head address_group_member_list; | |
332 | ++}; | |
333 | ++ | |
334 | ++#define TMY_TYPE_ADDRESS_GROUP 0 | |
335 | ++#define TMY_TYPE_IPv4 1 | |
336 | ++#define TMY_TYPE_IPv6 2 | |
337 | ++ | |
338 | ++struct net_acl { | |
339 | ++ /* type = TYPE_IP_NETWORK_ACL */ | |
340 | ++ struct acl_info head; | |
341 | ++ u8 operation_type; | |
342 | ++ u8 record_type; | |
343 | ++ union { | |
344 | ++ struct { | |
345 | ++ u32 min; | |
346 | ++ u32 max; | |
347 | ++ } ipv4; | |
348 | ++ struct { | |
349 | ++ u16 min[8]; | |
350 | ++ u16 max[8]; | |
351 | ++ } ipv6; | |
352 | ++ const struct address_group_entry *group; | |
353 | ++ } u; | |
354 | ++ u16 min_port; /* Start of port number range. */ | |
355 | ++ u16 max_port; /* End of port number range. */ | |
356 | ++}; | |
357 | ++ | |
358 | ++struct signal_acl { | |
359 | ++ /* type = TYPE_SIGNAL_ACL */ | |
360 | ++ struct acl_info head; | |
361 | ++ u16 sig; | |
362 | ++ /* Pointer to destination pattern. */ | |
363 | ++ const struct path_info *domainname; | |
364 | ++}; | |
365 | ++ | |
366 | ++struct capability_acl { | |
367 | ++ /* type = TYPE_CAPABILITY_ACL */ | |
368 | ++ struct acl_info head; | |
369 | ++ u16 capability; | |
370 | ++}; | |
371 | ++ | |
372 | ++/************************* Keywords for ACLs. *************************/ | |
373 | ++ | |
374 | ++#define TMY_AGGREGATOR "aggregator " | |
375 | ++#define TMY_AGGREGATOR_LEN (sizeof(TMY_AGGREGATOR) - 1) | |
376 | ++#define TMY_ALIAS "alias " | |
377 | ++#define TMY_ALIAS_LEN (sizeof(TMY_ALIAS) - 1) | |
378 | ++#define TMY_ALLOW_READ "allow_read " | |
379 | ++#define TMY_ALLOW_READ_LEN (sizeof(TMY_ALLOW_READ) - 1) | |
380 | ++#define TMY_DELETE "delete " | |
381 | ++#define TMY_DELETE_LEN (sizeof(TMY_DELETE) - 1) | |
382 | ++#define TMY_DENY_REWRITE "deny_rewrite " | |
383 | ++#define TMY_DENY_REWRITE_LEN (sizeof(TMY_DENY_REWRITE) - 1) | |
384 | ++#define TMY_FILE_PATTERN "file_pattern " | |
385 | ++#define TMY_FILE_PATTERN_LEN (sizeof(TMY_FILE_PATTERN) - 1) | |
386 | ++#define TMY_INITIALIZE_DOMAIN "initialize_domain " | |
387 | ++#define TMY_INITIALIZE_DOMAIN_LEN (sizeof(TMY_INITIALIZE_DOMAIN) - 1) | |
388 | ++#define TMY_KEEP_DOMAIN "keep_domain " | |
389 | ++#define TMY_KEEP_DOMAIN_LEN (sizeof(TMY_KEEP_DOMAIN) - 1) | |
390 | ++#define TMY_NO_INITIALIZE_DOMAIN "no_initialize_domain " | |
391 | ++#define TMY_NO_INITIALIZE_DOMAIN_LEN (sizeof(TMY_NO_INITIALIZE_DOMAIN) - 1) | |
392 | ++#define TMY_NO_KEEP_DOMAIN "no_keep_domain " | |
393 | ++#define TMY_NO_KEEP_DOMAIN_LEN (sizeof(TMY_NO_KEEP_DOMAIN) - 1) | |
394 | ++#define TMY_PATH_GROUP "path_group " | |
395 | ++#define TMY_PATH_GROUP_LEN (sizeof(TMY_PATH_GROUP) - 1) | |
396 | ++#define TMY_SELECT "select " | |
397 | ++#define TMY_SELECT_LEN (sizeof(TMY_SELECT) - 1) | |
398 | ++#define TMY_UNDELETE "undelete " | |
399 | ++#define TMY_UNDELETE_LEN (sizeof(TMY_UNDELETE) - 1) | |
400 | ++ | |
401 | ++#define TMY_ALLOW_MOUNT "allow_mount " | |
402 | ++#define TMY_ALLOW_MOUNT_LEN (sizeof(TMY_ALLOW_MOUNT) - 1) | |
403 | ++#define TMY_DENY_UNMOUNT "deny_unmount " | |
404 | ++#define TMY_DENY_UNMOUNT_LEN (sizeof(TMY_DENY_UNMOUNT) - 1) | |
405 | ++#define TMY_ALLOW_PIVOT_ROOT "allow_pivot_root " | |
406 | ++#define TMY_ALLOW_PIVOT_ROOT_LEN (sizeof(TMY_ALLOW_PIVOT_ROOT) - 1) | |
407 | ++ | |
408 | ++#define TMY_USE_PROFILE "use_profile " | |
409 | ++ | |
410 | ++#define TMY_ROOT_NAME "<kernel>" | |
411 | ++#define TMY_ROOT_NAME_LEN (sizeof(TMY_ROOT_NAME) - 1) | |
412 | ++ | |
413 | ++#define TMY_ALLOW_ARGV0 "allow_argv0 " | |
414 | ++#define TMY_ALLOW_ARGV0_LEN (sizeof(TMY_ALLOW_ARGV0) - 1) | |
415 | ++ | |
416 | ++#define TMY_ADDRESS_GROUP "address_group " | |
417 | ++#define TMY_ADDRESS_GROUP_LEN (sizeof(TMY_ADDRESS_GROUP) - 1) | |
418 | ++#define TMY_ALLOW_NETWORK "allow_network " | |
419 | ++#define TMY_ALLOW_NETWORK_LEN (sizeof(TMY_ALLOW_NETWORK) - 1) | |
420 | ++ | |
421 | ++#define TMY_ALLOW_SIGNAL "allow_signal " | |
422 | ++#define TMY_ALLOW_SIGNAL_LEN (sizeof(TMY_ALLOW_SIGNAL) - 1) | |
423 | ++ | |
424 | ++#define TMY_ALLOW_CAPABILITY "allow_capability " | |
425 | ++#define TMY_ALLOW_CAPABILITY_LEN (sizeof(TMY_ALLOW_CAPABILITY) - 1) | |
426 | ++#define TMY_MAC_FOR_CAPABILITY "MAC_FOR_CAPABILITY::" | |
427 | ++#define TMY_MAC_FOR_CAPABILITY_LEN (sizeof(TMY_MAC_FOR_CAPABILITY) - 1) | |
428 | ++ | |
429 | ++/******************** Index numbers for Access Controls. ********************/ | |
430 | ++ | |
431 | ++#define TMY_COMMENT 0 | |
432 | ++#define TMY_MAC_FOR_FILE 1 | |
433 | ++#define TMY_MAC_FOR_ARGV0 2 | |
434 | ++#define TMY_MAC_FOR_NETWORK 3 | |
435 | ++#define TMY_MAC_FOR_SIGNAL 4 | |
436 | ++#define TMY_DENY_CONCEAL_MOUNT 5 | |
437 | ++#define TMY_RESTRICT_MOUNT 6 | |
438 | ++#define TMY_RESTRICT_UMOUNT 7 | |
439 | ++#define TMY_RESTRICT_PIVOT_ROOT 8 | |
440 | ++#define TMY_MAX_ACCEPT_ENTRY 9 | |
441 | ++#define TMY_MAX_GRANT_LOG 10 | |
442 | ++#define TMY_MAX_REJECT_LOG 11 | |
443 | ++#define TMY_VERBOSE 12 | |
444 | ++#define TMY_ALLOW_ENFORCE_GRACE 13 | |
445 | ++#define TMY_MAX_CONTROL_INDEX 14 | |
446 | ++ | |
447 | ++#define TMY_NETWORK_ACL_UDP_BIND 0 | |
448 | ++#define TMY_NETWORK_ACL_UDP_CONNECT 1 | |
449 | ++#define TMY_NETWORK_ACL_TCP_BIND 2 | |
450 | ++#define TMY_NETWORK_ACL_TCP_LISTEN 3 | |
451 | ++#define TMY_NETWORK_ACL_TCP_CONNECT 4 | |
452 | ++#define TMY_NETWORK_ACL_TCP_ACCEPT 5 | |
453 | ++#define TMY_NETWORK_ACL_RAW_BIND 6 | |
454 | ++#define TMY_NETWORK_ACL_RAW_CONNECT 7 | |
455 | ++ | |
456 | ++/************* Index numbers for Capability Controls. **********/ | |
457 | ++ | |
458 | ++/* socket(PF_INET or PF_INET6, SOCK_STREAM, *) */ | |
459 | ++#define TMY_INET_STREAM_SOCKET_CREATE 0 | |
460 | ++/* listen() for PF_INET or PF_INET6, SOCK_STREAM */ | |
461 | ++#define TMY_INET_STREAM_SOCKET_LISTEN 1 | |
462 | ++/* connect() for PF_INET or PF_INET6, SOCK_STREAM */ | |
463 | ++#define TMY_INET_STREAM_SOCKET_CONNECT 2 | |
464 | ++/* socket(PF_INET or PF_INET6, SOCK_DGRAM, *) */ | |
465 | ++#define TMY_USE_INET_DGRAM_SOCKET 3 | |
466 | ++/* socket(PF_INET or PF_INET6, SOCK_RAW, *) */ | |
467 | ++#define TMY_USE_INET_RAW_SOCKET 4 | |
468 | ++/* socket(PF_ROUTE, *, *) */ | |
469 | ++#define TMY_USE_ROUTE_SOCKET 5 | |
470 | ++/* socket(PF_PACKET, *, *) */ | |
471 | ++#define TMY_USE_PACKET_SOCKET 6 | |
472 | ++/* sys_mount() */ | |
473 | ++#define TMY_SYS_MOUNT 7 | |
474 | ++/* sys_umount() */ | |
475 | ++#define TMY_SYS_UMOUNT 8 | |
476 | ++/* sys_reboot() */ | |
477 | ++#define TMY_SYS_REBOOT 9 | |
478 | ++/* sys_chroot() */ | |
479 | ++#define TMY_SYS_CHROOT 10 | |
480 | ++/* sys_kill(), sys_tkill(), sys_tgkill() */ | |
481 | ++#define TMY_SYS_KILL 11 | |
482 | ++/* sys_vhangup() */ | |
483 | ++#define TMY_SYS_VHANGUP 12 | |
484 | ++/* do_settimeofday(), sys_adjtimex() */ | |
485 | ++#define TMY_SYS_SETTIME 13 | |
486 | ++/* sys_nice(), sys_setpriority() */ | |
487 | ++#define TMY_SYS_NICE 14 | |
488 | ++/* sys_sethostname(), sys_setdomainname() */ | |
489 | ++#define TMY_SYS_SETHOSTNAME 15 | |
490 | ++/* sys_create_module(), sys_init_module(), sys_delete_module() */ | |
491 | ++#define TMY_USE_KERNEL_MODULE 16 | |
492 | ++/* sys_mknod(S_IFIFO) */ | |
493 | ++#define TMY_CREATE_FIFO 17 | |
494 | ++/* sys_mknod(S_IFBLK) */ | |
495 | ++#define TMY_CREATE_BLOCK_DEV 18 | |
496 | ++/* sys_mknod(S_IFCHR) */ | |
497 | ++#define TMY_CREATE_CHAR_DEV 19 | |
498 | ++/* sys_mknod(S_IFSOCK) */ | |
499 | ++#define TMY_CREATE_UNIX_SOCKET 20 | |
500 | ++/* sys_link() */ | |
501 | ++#define TMY_SYS_LINK 21 | |
502 | ++/* sys_symlink() */ | |
503 | ++#define TMY_SYS_SYMLINK 22 | |
504 | ++/* sys_rename() */ | |
505 | ++#define TMY_SYS_RENAME 23 | |
506 | ++/* sys_unlink() */ | |
507 | ++#define TMY_SYS_UNLINK 24 | |
508 | ++/* sys_chmod(), sys_fchmod() */ | |
509 | ++#define TMY_SYS_CHMOD 25 | |
510 | ++/* sys_chown(), sys_fchown(), sys_lchown() */ | |
511 | ++#define TMY_SYS_CHOWN 26 | |
512 | ++/* sys_ioctl(), compat_sys_ioctl() */ | |
513 | ++#define TMY_SYS_IOCTL 27 | |
514 | ++/* sys_kexec_load() */ | |
515 | ++#define TMY_SYS_KEXEC_LOAD 28 | |
516 | ++/* sys_pivot_root() */ | |
517 | ++#define TMY_SYS_PIVOT_ROOT 29 | |
518 | ++#define TMY_MAX_CAPABILITY_INDEX 30 | |
519 | ++ | |
520 | ++/******************** Index numbers for updates counter. ********************/ | |
521 | ++ | |
522 | ++#define TMY_UPDATE_DOMAINPOLICY 0 | |
523 | ++#define TMY_UPDATE_SYSTEMPOLICY 1 | |
524 | ++#define TMY_UPDATE_EXCEPTIONPOLICY 2 | |
525 | ++#define TMY_UPDATE_PROFILE 3 | |
526 | ++#define TMY_UPDATE_QUERY 4 | |
527 | ++#define TMY_UPDATE_MANAGER 5 | |
528 | ++#define TMY_UPDATE_GRANT_LOG 6 | |
529 | ++#define TMY_UPDATE_REJECT_LOG 7 | |
530 | ++#define TMY_MAX_UPDATES_COUNTER 8 | |
531 | ++ | |
532 | ++/*************** Indexes for /sys/kernel/security interfaces. ***************/ | |
533 | ++ | |
534 | ++#define TMY_DOMAINPOLICY 0 | |
535 | ++#define TMY_SYSTEMPOLICY 1 | |
536 | ++#define TMY_EXCEPTIONPOLICY 2 | |
537 | ++#define TMY_DOMAIN_STATUS 3 | |
538 | ++#define TMY_PROCESS_STATUS 4 | |
539 | ++#define TMY_MEMINFO 5 | |
540 | ++#define TMY_SELFDOMAIN 6 | |
541 | ++#define TMY_PROFILE 7 | |
542 | ++#define TMY_QUERY 8 | |
543 | ++#define TMY_MANAGER 9 | |
544 | ++#define TMY_UPDATESCOUNTER 10 | |
545 | ++#define TMY_GRANT_LOG 11 | |
546 | ++#define TMY_REJECT_LOG 12 | |
547 | ++#define TMY_VERSION 13 | |
548 | ++ | |
549 | ++/*************** The structure for /sys/kernel/security interfaces. *********/ | |
550 | ++ | |
551 | ++struct io_buffer { | |
552 | ++ int (*read) (struct io_buffer *); | |
553 | ++ struct mutex read_mutex; /* Lock for reading. */ | |
554 | ++ int (*write) (struct io_buffer *); | |
555 | ++ struct mutex write_mutex; /* Lock for updating. */ | |
556 | ++ int (*poll) (struct file *file, poll_table *wait); | |
557 | ++ struct list_head *read_var1; /* The position currently reading from. */ | |
558 | ++ struct list_head *read_var2; /* Extra variables for reading. */ | |
559 | ++ struct domain_info *write_var1; /* The position currently writing to. */ | |
560 | ++ int read_step; /* The step for reading. */ | |
561 | ++ char *read_buf; /* Buffer for reading. */ | |
562 | ++ bool read_eof; /* EOF flag for reading. */ | |
563 | ++ int read_avail; /* Bytes available for reading. */ | |
564 | ++ int readbuf_size; /* Size of read buffer. */ | |
565 | ++ char *write_buf; /* Buffer for writing. */ | |
566 | ++ int write_avail; /* Bytes available for writing. */ | |
567 | ++ int writebuf_size; /* Size of write buffer. */ | |
568 | ++}; | |
569 | ++ | |
570 | ++/************************* PROTOTYPES *************************/ | |
571 | ++ | |
572 | ++char *tmy_find_condition_part(char *data); | |
573 | ++const struct condition_list *tmy_assign_condition(const char *condition); | |
574 | ++int tmy_check_condition(const struct condition_list *ptr, | |
575 | ++ struct obj_info *obj); | |
576 | ++int tmy_dump_condition(struct io_buffer *head, | |
577 | ++ const struct condition_list *ptr); | |
578 | ++const char *tmy_get_exe(void); | |
579 | ++const char *tmy_getmsg(bool is_enforce); | |
580 | ++const char *tmy_lastname(const struct domain_info *domain); | |
581 | ++const char *tmy_acltype2keyword(const unsigned int acl_type); | |
582 | ++ | |
583 | ++int tmy_mount_perm(char *dev_name, | |
584 | ++ char *dir_name, | |
585 | ++ char *type, | |
586 | ++ unsigned long flags); | |
587 | ++int tmy_conceal_mount(struct nameidata *nd); | |
588 | ++int tmy_umount_perm(struct vfsmount *mnt); | |
589 | ++int tmy_add_mount_policy(char *data, const bool is_delete); | |
590 | ++int tmy_read_mount_policy(struct io_buffer *head); | |
591 | ++int tmy_add_no_umount_policy(char *data, const bool is_delete); | |
592 | ++int tmy_read_no_umount_policy(struct io_buffer *head); | |
593 | ++int tmy_pivot_root_perm(struct nameidata *old_nd, | |
594 | ++ struct nameidata *new_nd); | |
595 | ++int tmy_add_pivot_root_policy(char *data, const bool is_delete); | |
596 | ++int tmy_read_pivot_root_policy(struct io_buffer *head); | |
597 | ++ | |
598 | ++int tmy_add_aggregator_policy(char *data, const bool is_delete); | |
599 | ++int tmy_add_address_group_policy(char *data, const bool is_delete); | |
600 | ++int tmy_add_alias_policy(char *data, const bool is_delete); | |
601 | ++int tmy_add_argv0_policy(char *data, | |
602 | ++ struct domain_info *domain, | |
603 | ++ const struct condition_list *cond, | |
604 | ++ const bool is_delete); | |
605 | ++int tmy_add_acl(struct domain_info *domain, struct acl_info *acl); | |
606 | ++int tmy_add_capability_policy(char *data, | |
607 | ++ struct domain_info *domain, | |
608 | ++ const struct condition_list *cond, | |
609 | ++ const bool is_delete); | |
610 | ++int tmy_add_domain_initializer_policy(char *data, | |
611 | ++ const bool is_not, | |
612 | ++ const bool is_delete); | |
613 | ++int tmy_add_domain_keeper_policy(char *data, | |
614 | ++ const bool is_not, | |
615 | ++ const bool is_delete); | |
616 | ++int tmy_file_perm(const char *filename0, const u8 perm, const char *operation); | |
617 | ++int tmy_add_file_policy(char *data, | |
618 | ++ struct domain_info *domain, | |
619 | ++ const struct condition_list *cond, | |
620 | ++ const bool is_delete); | |
621 | ++int tmy_add_globally_readable_policy(char *data, const bool is_delete); | |
622 | ++int tmy_add_group_policy(char *data, const bool is_delete); | |
623 | ++int tmy_add_network_policy(char *data, | |
624 | ++ struct domain_info *domain, | |
625 | ++ const struct condition_list *cond, | |
626 | ++ const bool is_delete); | |
627 | ++int tmy_add_no_rewrite_policy(char *pattern, const bool is_delete); | |
628 | ++int tmy_add_pattern_policy(char *data, const bool is_delete); | |
629 | ++int tmy_supervisor(const char *fmt, ...) | |
630 | ++ __attribute__((format(printf, 1, 2))); | |
631 | ++#ifdef CONFIG_SECURITY_TOMOYO_USE_AUDITD | |
632 | ++int tmy_audit(const char *fmt, ...) | |
633 | ++ __attribute__((format(printf, 1, 2))); | |
634 | ++#else | |
635 | ++#define tmy_audit printk | |
636 | ++#endif | |
637 | ++ | |
638 | ++int tmy_del_acl(struct acl_info *ptr); | |
639 | ++int tmy_delete_domain(char *data); | |
640 | ++bool tmy_is_correct_domain(const unsigned char *domainname, | |
641 | ++ const char *function); | |
642 | ++bool tmy_correct_path(const char *filename, | |
643 | ++ const int start_type, | |
644 | ++ const int pattern_type, | |
645 | ++ const int end_type, | |
646 | ++ const char *function); | |
647 | ++bool tmy_is_domain_def(const unsigned char *buffer); | |
648 | ++bool tmy_path_match(const struct path_info *pathname0, | |
649 | ++ const struct path_info *pattern0); | |
650 | ++int tmy_read_aggregator_policy(struct io_buffer *head); | |
651 | ++int tmy_read_alias_policy(struct io_buffer *head); | |
652 | ++int tmy_read_domain_initializer_policy(struct io_buffer *head); | |
653 | ++int tmy_read_domain_keeper_policy(struct io_buffer *head); | |
654 | ++int tmy_read_globally_readable_policy(struct io_buffer *head); | |
655 | ++int tmy_read_path_group_policy(struct io_buffer *head); | |
656 | ++int tmy_read_no_rewrite_policy(struct io_buffer *head); | |
657 | ++int tmy_read_pattern_policy(struct io_buffer *head); | |
658 | ++int tmy_read_address_group_policy(struct io_buffer *head); | |
659 | ++int tmy_argv0_perm(const struct path_info *filename, const char *argv0); | |
660 | ++int tmy_capable(const unsigned int capability); | |
661 | ++int tmy_network_listen_acl(const bool is_ipv6, | |
662 | ++ const u8 *address, | |
663 | ++ const u16 port); | |
664 | ++int tmy_network_connect_acl(const bool is_ipv6, | |
665 | ++ const int sock_type, | |
666 | ++ const u8 *address, | |
667 | ++ const u16 port); | |
668 | ++int tmy_network_bind_acl(const bool is_ipv6, | |
669 | ++ const int sock_type, | |
670 | ++ const u8 *address, | |
671 | ++ const u16 port); | |
672 | ++int tmy_network_sendmsg_acl(const bool is_ipv6, | |
673 | ++ const int sock_type, | |
674 | ++ const u8 *address, | |
675 | ++ const u16 port); | |
676 | ++int tmy_network_accept_acl(const bool is_ipv6, | |
677 | ++ const u8 *address, | |
678 | ++ const u16 port); | |
679 | ++int tmy_network_recvmsg_acl(const bool is_ipv6, | |
680 | ++ const int sock_type, | |
681 | ++ const u8 *address, | |
682 | ++ const u16 port); | |
683 | ++ | |
684 | ++int tmy_signal_acl(int sig, int pid); | |
685 | ++int tmy_add_signal_policy(char *data, | |
686 | ++ struct domain_info *domain, | |
687 | ++ const struct condition_list *cond, | |
688 | ++ const bool is_delete); | |
689 | ++ | |
690 | ++char *tmy_init_audit_log(int *len, const u8 profile, const unsigned int mode); | |
691 | ++int tmy_write_audit_log(char *log, const bool is_granted); | |
692 | ++int tmy_acltype2paths(const unsigned int acl_type); | |
693 | ++int tmy_io_printf(struct io_buffer *head, const char *fmt, ...) | |
694 | ++ __attribute__((format(printf, 2, 3))); | |
695 | ++struct domain_info *tmy_find_domain(const char *domainname); | |
696 | ++struct domain_info *tmy_new_domain(const char *domainname, const u8 profile); | |
697 | ++struct domain_info *tmy_undelete_domain(const char *domainname0); | |
698 | ++bool tmy_quota(void); | |
699 | ++unsigned int tmy_flags(const unsigned int index); | |
700 | ++bool tmy_audit_grant(void); | |
701 | ++bool tmy_audit_reject(void); | |
702 | ++void tmy_update_counter(const unsigned char index); | |
703 | ++void *tmy_alloc(const size_t size); | |
704 | ++void tmy_free(const void *p); | |
705 | ++void tmy_fill_path_info(struct path_info *ptr); | |
706 | ++const char *tmy_capability2keyword(const unsigned int capability); | |
707 | ++int tmy_read_capability_profile(struct io_buffer *head); | |
708 | ++int tmy_set_capability_profile(const char *data, unsigned int value, | |
709 | ++ const unsigned int profile); | |
710 | ++ | |
711 | ++int tmy_read_grant_log(struct io_buffer *head); | |
712 | ++int tmy_poll_grant_log(struct file *file, poll_table *wait); | |
713 | ++int tmy_read_reject_log(struct io_buffer *head); | |
714 | ++int tmy_poll_reject_log(struct file *file, poll_table *wait); | |
715 | ++ | |
716 | ++static inline bool tmy_pathcmp(const struct path_info *a, | |
717 | ++ const struct path_info *b) | |
718 | ++{ | |
719 | ++ return a->hash != b->hash || strcmp(a->name, b->name); | |
720 | ++} | |
721 | ++ | |
722 | ++extern struct domain_info KERNEL_DOMAIN; | |
723 | ++extern struct list_head domain_list; | |
724 | ++void tmy_load_policy(const char *filename); | |
725 | ++int tmy_find_next_domain(struct linux_binprm *, | |
726 | ++ struct domain_info **); | |
727 | ++ | |
728 | ++struct path_info; | |
729 | ++ | |
730 | ++int tmy_exec_perm(const struct path_info *filename, struct file *filp); | |
731 | ++/* Check whether the given dentry is allowed to read/write/execute. */ | |
732 | ++int tmy_open_perm(struct dentry *dentry, struct vfsmount *mnt, const int flag); | |
733 | ++/* Check whether the given dentry is allowed to write. */ | |
734 | ++int tmy_single_write_perm(const unsigned int operation, | |
735 | ++ struct dentry *dentry, | |
736 | ++ struct vfsmount *mnt); | |
737 | ++int tmy_double_write_perm(const unsigned int operation, | |
738 | ++ struct dentry *dentry1, | |
739 | ++ struct vfsmount *mnt1, | |
740 | ++ struct dentry *dentry2, | |
741 | ++ struct vfsmount *mnt2); | |
742 | ++int tmy_rewrite_perm(struct file *filp); | |
743 | ++ | |
744 | ++struct inode; | |
745 | ++ | |
746 | ++/******************** Index numbers for Access Controls. ********************/ | |
747 | ++ | |
748 | ++#define TMY_TYPE_CREATE_ACL 0 | |
749 | ++#define TMY_TYPE_UNLINK_ACL 1 | |
750 | ++#define TMY_TYPE_MKDIR_ACL 2 | |
751 | ++#define TMY_TYPE_RMDIR_ACL 3 | |
752 | ++#define TMY_TYPE_MKFIFO_ACL 4 | |
753 | ++#define TMY_TYPE_MKSOCK_ACL 5 | |
754 | ++#define TMY_TYPE_MKBLOCK_ACL 6 | |
755 | ++#define TMY_TYPE_MKCHAR_ACL 7 | |
756 | ++#define TMY_TYPE_TRUNCATE_ACL 8 | |
757 | ++#define TMY_TYPE_SYMLINK_ACL 9 | |
758 | ++#define TMY_TYPE_LINK_ACL 10 | |
759 | ++#define TMY_TYPE_RENAME_ACL 11 | |
760 | ++#define TMY_TYPE_REWRITE_ACL 12 | |
761 | ++ | |
762 | ++#define TMY_TYPE_FILE_ACL 100 | |
763 | ++#define TMY_TYPE_ARGV0_ACL 101 | |
764 | ++#define TMY_TYPE_IP_NETWORK_ACL 103 | |
765 | ++#define TMY_TYPE_SIGNAL_ACL 104 | |
766 | ++#define TMY_TYPE_CAPABILITY_ACL 105 | |
767 | ++ | |
768 | ++#define TMY_CHECK_READ_FOR_OPEN_EXEC 1 | |
769 | ++ | |
770 | ++char *tmy_print_ipv6(char *buffer, const int buffer_len, const u16 *ip); | |
771 | ++const char *tmy_network2keyword(const unsigned int operation); | |
772 | ++ | |
773 | ++/* to check "if task.parent.pid" condition. */ | |
774 | ++extern asmlinkage long sys_getppid(void); | |
775 | ++ | |
776 | ++#endif |
@@ -0,0 +1,243 @@ | ||
1 | +Signal control functions for TOMOYO Linux. | |
2 | +TOMOYO Linux checks sending signal by signal number and | |
3 | +the domain of target process. In order to check signal | |
4 | +permission, LSM expansion patch [TOMOYO /] is needed. | |
5 | + | |
6 | +Each permission can be automatically accumulated into | |
7 | +the policy of each domain using 'learning mode'. | |
8 | + | |
9 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
10 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
11 | + security/tomoyo/signal.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++ | |
12 | + 1 file changed, 227 insertions(+) | |
13 | + | |
14 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
15 | ++++ linux-2.6.23/security/tomoyo/signal.c 2007-11-09 16:51:15.000000000 +0900 | |
16 | +@@ -0,0 +1,227 @@ | |
17 | ++/* | |
18 | ++ * security/tomoyo/signal.c | |
19 | ++ * | |
20 | ++ * Signal access contol functions for TOMOYO Linux. | |
21 | ++ */ | |
22 | ++ | |
23 | ++#include "tomoyo.h" | |
24 | ++#include "realpath.h" | |
25 | ++ | |
26 | ++/************************* AUDIT FUNCTIONS *************************/ | |
27 | ++ | |
28 | ++static int tmy_audit_signal_log(const int signal, | |
29 | ++ const struct path_info *dest_domain, | |
30 | ++ const bool is_granted, | |
31 | ++ const u8 profile, | |
32 | ++ const unsigned int mode) | |
33 | ++{ | |
34 | ++ char *buf; | |
35 | ++ int len; | |
36 | ++ | |
37 | ++ if (is_granted) { | |
38 | ++ if (!tmy_audit_grant()) | |
39 | ++ return 0; | |
40 | ++ } else { | |
41 | ++ if (!tmy_audit_reject()) | |
42 | ++ return 0; | |
43 | ++ } | |
44 | ++ | |
45 | ++ len = dest_domain->total_len; | |
46 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
47 | ++ | |
48 | ++ if (!buf) | |
49 | ++ return -ENOMEM; | |
50 | ++ | |
51 | ++ snprintf(buf + strlen(buf), | |
52 | ++ len - strlen(buf) - 1, | |
53 | ++ "%s%d %s\n", | |
54 | ++ TMY_ALLOW_SIGNAL, signal, dest_domain->name); | |
55 | ++ | |
56 | ++ return tmy_write_audit_log(buf, is_granted); | |
57 | ++} | |
58 | ++ | |
59 | ++/************************* SIGNAL ACL HANDLER *************************/ | |
60 | ++ | |
61 | ++static int tmy_add_signal_entry(const u16 sig, const char *dest_pattern, | |
62 | ++ struct domain_info *domain, | |
63 | ++ const struct condition_list *cond, | |
64 | ++ const bool is_delete) | |
65 | ++{ | |
66 | ++ struct acl_info *ptr; | |
67 | ++ struct signal_acl *acl; | |
68 | ++ const struct path_info *saved_dest_pattern; | |
69 | ++ int error = -ENOMEM; | |
70 | ++ | |
71 | ++ if (!domain) | |
72 | ++ return -EINVAL; | |
73 | ++ if (!dest_pattern || | |
74 | ++ !tmy_is_correct_domain(dest_pattern, __FUNCTION__)) | |
75 | ++ return -EINVAL; | |
76 | ++ | |
77 | ++ saved_dest_pattern = tmy_save_name(dest_pattern); | |
78 | ++ if (!saved_dest_pattern) | |
79 | ++ return -ENOMEM; | |
80 | ++ | |
81 | ++ mutex_lock(&domain_acl_lock); | |
82 | ++ | |
83 | ++ if (is_delete) | |
84 | ++ goto remove; | |
85 | ++ | |
86 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
87 | ++ acl = (struct signal_acl *) ptr; | |
88 | ++ | |
89 | ++ if (ptr->type == TMY_TYPE_SIGNAL_ACL && acl->sig == sig | |
90 | ++ && ptr->cond == cond | |
91 | ++ && !tmy_pathcmp(acl->domainname, saved_dest_pattern)) { | |
92 | ++ ptr->is_deleted = 0; | |
93 | ++ /* Found. Nothing to do. */ | |
94 | ++ error = 0; | |
95 | ++ goto ok; | |
96 | ++ } | |
97 | ++ } | |
98 | ++ /* Not found. Append it to the tail. */ | |
99 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
100 | ++ if (!acl) | |
101 | ++ goto ok; | |
102 | ++ | |
103 | ++ acl->head.type = TMY_TYPE_SIGNAL_ACL; | |
104 | ++ acl->head.cond = cond; | |
105 | ++ acl->sig = sig; | |
106 | ++ acl->domainname = saved_dest_pattern; | |
107 | ++ error = tmy_add_acl(domain, (struct acl_info *) acl); | |
108 | ++ goto ok; | |
109 | ++remove: ; | |
110 | ++ error = -ENOENT; | |
111 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
112 | ++ acl = (struct signal_acl *) ptr; | |
113 | ++ if (ptr->type != TMY_TYPE_SIGNAL_ACL || ptr->cond != cond || | |
114 | ++ ptr->is_deleted || acl->sig != sig || | |
115 | ++ tmy_pathcmp(acl->domainname, saved_dest_pattern)) | |
116 | ++ continue; | |
117 | ++ error = tmy_del_acl(ptr); | |
118 | ++ break; | |
119 | ++ } | |
120 | ++ | |
121 | ++ok: ; | |
122 | ++ mutex_unlock(&domain_acl_lock); | |
123 | ++ | |
124 | ++ return error; | |
125 | ++} | |
126 | ++ | |
127 | ++/** | |
128 | ++ * tmy_signal_acl - check permission for kill(2)/tkill(2)/tgkill(2). | |
129 | ++ * @sig: signal number. | |
130 | ++ * @pid: pid of destination process. | |
131 | ++ * | |
132 | ++ * Returns zero if permission granted. | |
133 | ++ * Returns nonzero if permission denied. | |
134 | ++ */ | |
135 | ++int tmy_signal_acl(const int sig, const int pid) | |
136 | ++{ | |
137 | ++ struct domain_info *domain = TMY_SECURITY->domain; | |
138 | ++ struct domain_info *dest = NULL; | |
139 | ++ const char *dest_pattern; | |
140 | ++ struct acl_info *ptr; | |
141 | ++ const u16 hash = sig; | |
142 | ++ const u8 profile = domain->profile; | |
143 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_SIGNAL); | |
144 | ++ const bool is_enforce = (mode == 3); | |
145 | ++ bool found = 0; | |
146 | ++ | |
147 | ++ if (!mode) | |
148 | ++ return 0; | |
149 | ++ if (!sig) | |
150 | ++ return 0; /* No check for NULL signal. */ | |
151 | ++ if (current->pid == pid) { | |
152 | ++ tmy_audit_signal_log(sig, domain->domainname, 1, profile, mode); | |
153 | ++ return 0; /* No check for self. */ | |
154 | ++ } | |
155 | ++ | |
156 | ++ { /* Simplified checking. */ | |
157 | ++ struct task_struct *p = NULL; | |
158 | ++ read_lock(&tasklist_lock); | |
159 | ++ if (pid > 0) | |
160 | ++ p = find_task_by_pid((pid_t) pid); | |
161 | ++ else if (pid == 0) | |
162 | ++ p = current; | |
163 | ++ else if (pid == -1) | |
164 | ++ dest = &KERNEL_DOMAIN; | |
165 | ++ else | |
166 | ++ p = find_task_by_pid((pid_t) -pid); | |
167 | ++ if (p) | |
168 | ++ /* "struct task_struct"->security is not NULL. */ | |
169 | ++ dest = ((struct tmy_security *) p->security)->domain; | |
170 | ++ read_unlock(&tasklist_lock); | |
171 | ++ if (!dest) | |
172 | ++ return 0; /* I can't find destinatioin. */ | |
173 | ++ } | |
174 | ++ | |
175 | ++ if (domain == dest) { | |
176 | ++ tmy_audit_signal_log(sig, dest->domainname, 1, profile, mode); | |
177 | ++ return 0; | |
178 | ++ } | |
179 | ++ | |
180 | ++ dest_pattern = dest->domainname->name; | |
181 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
182 | ++ struct signal_acl *acl = (struct signal_acl *) ptr; | |
183 | ++ | |
184 | ++ if (ptr->type == TMY_TYPE_SIGNAL_ACL && ptr->is_deleted == 0 | |
185 | ++ && acl->sig == hash && | |
186 | ++ tmy_check_condition(ptr->cond, NULL) == 0) { | |
187 | ++ const int len = acl->domainname->total_len; | |
188 | ++ | |
189 | ++ if (strncmp(acl->domainname->name, | |
190 | ++ dest_pattern, len) == 0 | |
191 | ++ && (dest_pattern[len] == ' ' || | |
192 | ++ dest_pattern[len] == '\0')) { | |
193 | ++ found = 1; | |
194 | ++ break; | |
195 | ++ } | |
196 | ++ } | |
197 | ++ } | |
198 | ++ | |
199 | ++ tmy_audit_signal_log(sig, dest->domainname, found, profile, mode); | |
200 | ++ | |
201 | ++ if (found) | |
202 | ++ return 0; | |
203 | ++ | |
204 | ++ if (tmy_flags(TMY_VERBOSE)) | |
205 | ++ tmy_audit("TOMOYO-%s: Signal %d to %s denied for %s\n", | |
206 | ++ tmy_getmsg(is_enforce), sig, | |
207 | ++ tmy_lastname(dest), tmy_lastname(domain)); | |
208 | ++ | |
209 | ++ if (is_enforce) | |
210 | ++ return tmy_supervisor("%s\n" TMY_ALLOW_SIGNAL "%d %s\n", | |
211 | ++ domain->domainname->name, | |
212 | ++ sig, dest_pattern); | |
213 | ++ if (mode == 1 && tmy_quota()) | |
214 | ++ tmy_add_signal_entry(sig, dest_pattern, domain, NULL, 0); | |
215 | ++ | |
216 | ++ return 0; | |
217 | ++} | |
218 | ++ | |
219 | ++/** | |
220 | ++ * tmy_add_signal_policy - add or delete signal policy. | |
221 | ++ * @data: a line to parse. | |
222 | ++ * @domain: pointer to "struct domain_info". | |
223 | ++ * @cond: pointer to "struct condition_list". May be NULL. | |
224 | ++ * @is_delete: is this delete request? | |
225 | ++ * | |
226 | ++ * Returns zero on success. | |
227 | ++ * Returns nonzero on failure. | |
228 | ++ */ | |
229 | ++int tmy_add_signal_policy(char *data, | |
230 | ++ struct domain_info *domain, | |
231 | ++ const struct condition_list *cond, | |
232 | ++ const bool is_delete) | |
233 | ++{ | |
234 | ++ int sig; | |
235 | ++ char *domainname = strchr(data, ' '); | |
236 | ++ | |
237 | ++ if (sscanf(data, "%d", &sig) == 1 && domainname && | |
238 | ++ tmy_is_domain_def(domainname + 1)) | |
239 | ++ return tmy_add_signal_entry(sig, domainname + 1, domain, | |
240 | ++ cond, is_delete); | |
241 | ++ | |
242 | ++ return -EINVAL; | |
243 | ++} |
@@ -0,0 +1,1487 @@ | ||
1 | +File access control functions for TOMOYO Linux. | |
2 | +TOMOYO Linux checks permission in | |
3 | +open/creat/unlink/truncate/ftruncate/mknod/mkdir/ | |
4 | +rmdir/symlink/link/rename/uselib/sysctl . | |
5 | + | |
6 | +Each permission can be automatically accumulated into | |
7 | +the policy of each domain using 'learning mode'. | |
8 | + | |
9 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
10 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
11 | + security/tomoyo/file.c | 1471 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
12 | + 1 file changed, 1471 insertions(+) | |
13 | + | |
14 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
15 | ++++ linux-2.6.23/security/tomoyo/file.c 2007-11-09 16:39:25.000000000 +0900 | |
16 | +@@ -0,0 +1,1471 @@ | |
17 | ++/* | |
18 | ++ * security/tomoyo/file.c | |
19 | ++ * | |
20 | ++ * File access control functions for TOMOYO Linux. | |
21 | ++ */ | |
22 | ++ | |
23 | ++#include "tomoyo.h" | |
24 | ++#include "realpath.h" | |
25 | ++ | |
26 | ++#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | |
27 | ++ | |
28 | ++/************************* VARIABLES *************************/ | |
29 | ++ | |
30 | ++/***** The structure for globally readable files. *****/ | |
31 | ++ | |
32 | ++struct globally_readable_file_entry { | |
33 | ++ struct list_head list; | |
34 | ++ const struct path_info *filename; | |
35 | ++ bool is_deleted; | |
36 | ++}; | |
37 | ++ | |
38 | ++/***** The structure for filename patterns. *****/ | |
39 | ++ | |
40 | ++struct pattern_entry { | |
41 | ++ struct list_head list; | |
42 | ++ const struct path_info *pattern; | |
43 | ++ bool is_deleted; | |
44 | ++}; | |
45 | ++ | |
46 | ++/***** The structure for non-rewritable-by-default file patterns. *****/ | |
47 | ++ | |
48 | ++struct no_rewrite_entry { | |
49 | ++ struct list_head list; | |
50 | ++ const struct path_info *pattern; | |
51 | ++ bool is_deleted; | |
52 | ++}; | |
53 | ++ | |
54 | ++/***** The structure for detailed write operations. *****/ | |
55 | ++ | |
56 | ++static struct { | |
57 | ++ const char *keyword; | |
58 | ++ const int paths; | |
59 | ++} acl_type_array[] = { | |
60 | ++ { "create", 1 }, /* TMY_TYPE_CREATE_ACL */ | |
61 | ++ { "unlink", 1 }, /* TMY_TYPE_UNLINK_ACL */ | |
62 | ++ { "mkdir", 1 }, /* TMY_TYPE_MKDIR_ACL */ | |
63 | ++ { "rmdir", 1 }, /* TMY_TYPE_RMDIR_ACL */ | |
64 | ++ { "mkfifo", 1 }, /* TMY_TYPE_MKFIFO_ACL */ | |
65 | ++ { "mksock", 1 }, /* TMY_TYPE_MKSOCK_ACL */ | |
66 | ++ { "mkblock", 1 }, /* TMY_TYPE_MKBLOCK_ACL */ | |
67 | ++ { "mkchar", 1 }, /* TMY_TYPE_MKCHAR_ACL */ | |
68 | ++ { "truncate", 1 }, /* TMY_TYPE_TRUNCATE_ACL */ | |
69 | ++ { "symlink", 1 }, /* TMY_TYPE_SYMLINK_ACL */ | |
70 | ++ { "link", 2 }, /* TMY_TYPE_LINK_ACL */ | |
71 | ++ { "rename", 2 }, /* TMY_TYPE_RENAME_ACL */ | |
72 | ++ { "rewrite", 1 }, /* TMY_TYPE_REWRITE_ACL */ | |
73 | ++ { NULL, 0 } | |
74 | ++}; | |
75 | ++ | |
76 | ++/************************* UTILITY FUNCTIONS *************************/ | |
77 | ++ | |
78 | ++/** | |
79 | ++ * tmy_acltype2keyword - get keyword from access control index. | |
80 | ++ * @acl_type: index number. | |
81 | ++ * | |
82 | ++ * Returns keyword that corresponds with @acl_type . | |
83 | ++ */ | |
84 | ++const char *tmy_acltype2keyword(const unsigned int acl_type) | |
85 | ++{ | |
86 | ++ return (acl_type < ARRAY_SIZE(acl_type_array)) | |
87 | ++ ? acl_type_array[acl_type].keyword : NULL; | |
88 | ++} | |
89 | ++ | |
90 | ++/** | |
91 | ++ * tmy_acltype2paths - get number of arguments from access control index. | |
92 | ++ * @acl_type: index number. | |
93 | ++ * | |
94 | ++ * Returns number of arguments that corresponds with @acl_type . | |
95 | ++ */ | |
96 | ++int tmy_acltype2paths(const unsigned int acl_type) | |
97 | ++{ | |
98 | ++ return (acl_type < ARRAY_SIZE(acl_type_array)) | |
99 | ++ ? acl_type_array[acl_type].paths : 0; | |
100 | ++} | |
101 | ++ | |
102 | ++static int tmy_strendswith(const char *name, const char *tail) | |
103 | ++{ | |
104 | ++ int len; | |
105 | ++ | |
106 | ++ if (!name || !tail) | |
107 | ++ return 0; | |
108 | ++ | |
109 | ++ len = strlen(name) - strlen(tail); | |
110 | ++ return len >= 0 && strcmp(name + len, tail) == 0; | |
111 | ++} | |
112 | ++ | |
113 | ++static struct path_info *tmy_get_path(struct dentry *dentry, | |
114 | ++ struct vfsmount *mnt) | |
115 | ++{ | |
116 | ++ /* sizeof(struct path_info_with_data) <= PAGE_SIZE */ | |
117 | ++ struct path_info_with_data { | |
118 | ++ /* Keep this first, this pointer is passed to tmy_free(). */ | |
119 | ++ struct path_info head; | |
120 | ++ char bariier1[16]; | |
121 | ++ char body[TMY_MAX_PATHNAME_LEN]; | |
122 | ++ char barrier2[16]; | |
123 | ++ } *buf = tmy_alloc(sizeof(*buf)); | |
124 | ++ | |
125 | ++ if (buf) { | |
126 | ++ int error = tmy_realpath_dentry2(dentry, | |
127 | ++ mnt, | |
128 | ++ buf->body, | |
129 | ++ sizeof(buf->body) - 1); | |
130 | ++ | |
131 | ++ if (error == 0) { | |
132 | ++ buf->head.name = buf->body; | |
133 | ++ tmy_fill_path_info(&buf->head); | |
134 | ++ return &buf->head; | |
135 | ++ } | |
136 | ++ | |
137 | ++ tmy_free(buf); | |
138 | ++ buf = NULL; | |
139 | ++ printk(KERN_INFO "tmy_realpath_dentry = %d\n", error); | |
140 | ++ } | |
141 | ++ | |
142 | ++ return NULL; | |
143 | ++} | |
144 | ++ | |
145 | ++/************************* PROTOTYPES *************************/ | |
146 | ++ | |
147 | ++static int tmy_add_double_write_acl(const u8 type, | |
148 | ++ const char *filename1, | |
149 | ++ const char *filename2, | |
150 | ++ struct domain_info * const domain, | |
151 | ++ const struct condition_list *cond, | |
152 | ++ const bool is_delete); | |
153 | ++static int tmy_add_single_write_acl(const u8 type, | |
154 | ++ const char *filename, | |
155 | ++ struct domain_info * const domain, | |
156 | ++ const struct condition_list *cond, | |
157 | ++ const bool is_delete); | |
158 | ++ | |
159 | ++/************************* AUDIT FUNCTIONS *************************/ | |
160 | ++ | |
161 | ++static int tmy_audit_file_log(const struct path_info *filename, | |
162 | ++ const u8 perm, | |
163 | ++ const bool is_granted, | |
164 | ++ const u8 profile, | |
165 | ++ const unsigned int mode) | |
166 | ++{ | |
167 | ++ char *buf; | |
168 | ++ int len; | |
169 | ++ | |
170 | ++ if (is_granted) { | |
171 | ++ if (!tmy_audit_grant()) | |
172 | ++ return 0; | |
173 | ++ } else { | |
174 | ++ if (!tmy_audit_reject()) | |
175 | ++ return 0; | |
176 | ++ } | |
177 | ++ | |
178 | ++ len = filename->total_len + 8; | |
179 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
180 | ++ | |
181 | ++ if (!buf) | |
182 | ++ return -ENOMEM; | |
183 | ++ | |
184 | ++ snprintf(buf + strlen(buf), | |
185 | ++ len - strlen(buf) - 1, | |
186 | ++ "%d %s\n", | |
187 | ++ perm, | |
188 | ++ filename->name); | |
189 | ++ | |
190 | ++ return tmy_write_audit_log(buf, is_granted); | |
191 | ++} | |
192 | ++ | |
193 | ++static int tmy_audit_write_log(const char *operation, | |
194 | ++ const struct path_info *filename1, | |
195 | ++ const struct path_info *filename2, | |
196 | ++ const bool is_granted, | |
197 | ++ const u8 profile, | |
198 | ++ const unsigned int mode) | |
199 | ++{ | |
200 | ++ char *buf; | |
201 | ++ int len; | |
202 | ++ | |
203 | ++ if (is_granted) { | |
204 | ++ if (!tmy_audit_grant()) | |
205 | ++ return 0; | |
206 | ++ } else { | |
207 | ++ if (!tmy_audit_reject()) | |
208 | ++ return 0; | |
209 | ++ } | |
210 | ++ | |
211 | ++ len = strlen(operation) + | |
212 | ++ filename1->total_len + | |
213 | ++ (filename2 ? filename2->total_len : 0) | |
214 | ++ + 16; | |
215 | ++ | |
216 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
217 | ++ if (!buf) | |
218 | ++ return -ENOMEM; | |
219 | ++ | |
220 | ++ snprintf(buf + strlen(buf), len - strlen(buf) - 1, | |
221 | ++ "allow_%s %s %s\n", | |
222 | ++ operation, filename1->name, filename2 ? filename2->name : ""); | |
223 | ++ | |
224 | ++ return tmy_write_audit_log(buf, is_granted); | |
225 | ++} | |
226 | ++ | |
227 | ++/********************** GLOBALLY READABLE FILE HANDLER **********************/ | |
228 | ++ | |
229 | ++static LIST_HEAD(globally_readable_list); | |
230 | ++ | |
231 | ++static int tmy_add_globally_readable_entry(const char *filename, | |
232 | ++ const bool is_delete) | |
233 | ++{ | |
234 | ++ struct globally_readable_file_entry *new_entry; | |
235 | ++ struct globally_readable_file_entry *ptr; | |
236 | ++ static DEFINE_MUTEX(mutex); | |
237 | ++ const struct path_info *saved; | |
238 | ++ int error = -ENOMEM; | |
239 | ++ | |
240 | ++ if (!tmy_correct_path(filename, 1, -1, -1, __FUNCTION__)) | |
241 | ++ return -EINVAL; /* No patterns allowed. */ | |
242 | ++ saved = tmy_save_name(filename); | |
243 | ++ if (!saved) | |
244 | ++ return -ENOMEM; | |
245 | ++ | |
246 | ++ mutex_lock(&mutex); | |
247 | ++ | |
248 | ++ list_for_each_entry(ptr, &globally_readable_list, list) { | |
249 | ++ if (ptr->filename == saved) { | |
250 | ++ ptr->is_deleted = is_delete; | |
251 | ++ error = 0; | |
252 | ++ goto out; | |
253 | ++ } | |
254 | ++ } | |
255 | ++ if (is_delete) { | |
256 | ++ error = -ENOENT; | |
257 | ++ goto out; | |
258 | ++ } | |
259 | ++ | |
260 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
261 | ++ if (!new_entry) | |
262 | ++ goto out; | |
263 | ++ | |
264 | ++ new_entry->filename = saved; | |
265 | ++ list_add_tail_mb(&new_entry->list, &globally_readable_list); | |
266 | ++ error = 0; | |
267 | ++ | |
268 | ++out: ; | |
269 | ++ mutex_unlock(&mutex); | |
270 | ++ | |
271 | ++ return error; | |
272 | ++} | |
273 | ++ | |
274 | ++static int tmy_globally_readable(const struct path_info *filename) | |
275 | ++{ | |
276 | ++ struct globally_readable_file_entry *ptr; | |
277 | ++ | |
278 | ++ list_for_each_entry(ptr, &globally_readable_list, list) { | |
279 | ++ if (!ptr->is_deleted && | |
280 | ++ !tmy_pathcmp(filename, ptr->filename)) | |
281 | ++ return 1; | |
282 | ++ } | |
283 | ++ | |
284 | ++ return 0; | |
285 | ++} | |
286 | ++ | |
287 | ++/** | |
288 | ++ * tmy_add_globally_readable_policy - add or delete globally readable policy. | |
289 | ++ * @filename: pointer to filename to add ore remove. | |
290 | ++ * @is_delete: is this delete request? | |
291 | ++ * | |
292 | ++ * Returns zero on success. | |
293 | ++ * Returns nonzero on failure. | |
294 | ++ */ | |
295 | ++int tmy_add_globally_readable_policy(char *filename, const bool is_delete) | |
296 | ++{ | |
297 | ++ return tmy_add_globally_readable_entry(filename, is_delete); | |
298 | ++} | |
299 | ++ | |
300 | ++/** | |
301 | ++ * tmy_read_globally_readable_policy - read globally readable policy. | |
302 | ++ * @head: pointer to "struct io_buffer". | |
303 | ++ * | |
304 | ++ * Returns nonzero if reading incomplete. | |
305 | ++ * Returns zero otherwise. | |
306 | ++ */ | |
307 | ++int tmy_read_globally_readable_policy(struct io_buffer *head) | |
308 | ++{ | |
309 | ++ struct list_head *pos; | |
310 | ++ list_for_each_cookie(pos, head->read_var2, &globally_readable_list) { | |
311 | ++ struct globally_readable_file_entry *ptr; | |
312 | ++ ptr = list_entry(pos, struct globally_readable_file_entry, | |
313 | ++ list); | |
314 | ++ if (ptr->is_deleted) | |
315 | ++ continue; | |
316 | ++ if (tmy_io_printf(head, TMY_ALLOW_READ "%s\n", | |
317 | ++ ptr->filename->name)) | |
318 | ++ return -ENOMEM; | |
319 | ++ } | |
320 | ++ return 0; | |
321 | ++} | |
322 | ++ | |
323 | ++/************************* FILE GROUP HANDLER *************************/ | |
324 | ++ | |
325 | ++static LIST_HEAD(path_group_list); | |
326 | ++ | |
327 | ++static int tmy_add_group_entry(const char *group_name, | |
328 | ++ const char *member_name, | |
329 | ++ const bool is_delete) | |
330 | ++{ | |
331 | ++ static DEFINE_MUTEX(mutex); | |
332 | ++ struct path_group_entry *new_group; | |
333 | ++ struct path_group_entry *group; | |
334 | ++ struct path_group_member *new_member; | |
335 | ++ struct path_group_member *member; | |
336 | ++ const struct path_info *saved_group; | |
337 | ++ const struct path_info *saved_member; | |
338 | ++ int error = -ENOMEM; | |
339 | ++ bool found = 0; | |
340 | ++ | |
341 | ++ if (!tmy_correct_path(group_name, 0, 0, 0, __FUNCTION__) || | |
342 | ++ !group_name[0] || | |
343 | ++ !tmy_correct_path(member_name, 0, 0, 0, __FUNCTION__) || | |
344 | ++ !member_name[0]) | |
345 | ++ return -EINVAL; | |
346 | ++ | |
347 | ++ saved_group = tmy_save_name(group_name); | |
348 | ++ saved_member = tmy_save_name(member_name); | |
349 | ++ | |
350 | ++ if (!saved_group || !saved_member) | |
351 | ++ return -ENOMEM; | |
352 | ++ | |
353 | ++ mutex_lock(&mutex); | |
354 | ++ list_for_each_entry(group, &path_group_list, list) { | |
355 | ++ if (saved_group != group->group_name) | |
356 | ++ continue; | |
357 | ++ list_for_each_entry(member, &group->path_group_member_list, | |
358 | ++ list) { | |
359 | ++ if (member->member_name == saved_member) { | |
360 | ++ member->is_deleted = is_delete; | |
361 | ++ error = 0; | |
362 | ++ goto out; | |
363 | ++ } | |
364 | ++ } | |
365 | ++ found = 1; | |
366 | ++ break; | |
367 | ++ } | |
368 | ++ | |
369 | ++ if (is_delete) { | |
370 | ++ error = -ENOENT; | |
371 | ++ goto out; | |
372 | ++ } | |
373 | ++ | |
374 | ++ if (!found) { | |
375 | ++ new_group = tmy_alloc_element(sizeof(*new_group)); | |
376 | ++ if (!new_group) | |
377 | ++ goto out; | |
378 | ++ INIT_LIST_HEAD(&new_group->path_group_member_list); | |
379 | ++ new_group->group_name = saved_group; | |
380 | ++ list_add_tail_mb(&new_group->list, &path_group_list); | |
381 | ++ group = new_group; | |
382 | ++ } | |
383 | ++ | |
384 | ++ new_member = tmy_alloc_element(sizeof(*new_member)); | |
385 | ++ if (!new_member) | |
386 | ++ goto out; | |
387 | ++ new_member->member_name = saved_member; | |
388 | ++ list_add_tail_mb(&new_member->list, &group->path_group_member_list); | |
389 | ++ error = 0; | |
390 | ++out: ; | |
391 | ++ mutex_unlock(&mutex); | |
392 | ++ | |
393 | ++ return error; | |
394 | ++} | |
395 | ++ | |
396 | ++/** | |
397 | ++ * tmy_add_group_policy - add or delete path group policy. | |
398 | ++ * @data: a line to parse. | |
399 | ++ * @is_delete: is this delete request? | |
400 | ++ * | |
401 | ++ * Returns zero on success. | |
402 | ++ * Returns nonzero on failure. | |
403 | ++ */ | |
404 | ++int tmy_add_group_policy(char *data, const bool is_delete) | |
405 | ++{ | |
406 | ++ char *cp = strchr(data, ' '); | |
407 | ++ | |
408 | ++ if (!cp) | |
409 | ++ return -EINVAL; | |
410 | ++ *cp++ = '\0'; | |
411 | ++ return tmy_add_group_entry(data, cp, is_delete); | |
412 | ++} | |
413 | ++ | |
414 | ++static struct path_group_entry *tmy_new_path_group(const char *group_name) | |
415 | ++{ | |
416 | ++ int i; | |
417 | ++ struct path_group_entry *group; | |
418 | ++ | |
419 | ++ for (i = 0; i <= 1; i++) { | |
420 | ++ list_for_each_entry(group, &path_group_list, list) { | |
421 | ++ if (strcmp(group_name, group->group_name->name) == 0) | |
422 | ++ return group; | |
423 | ++ } | |
424 | ++ | |
425 | ++ if (i == 0) { | |
426 | ++ /* | |
427 | ++ * Add a dummy entry to create new path group | |
428 | ++ * and delete that entry. | |
429 | ++ */ | |
430 | ++ tmy_add_group_entry(group_name, "/", 0); | |
431 | ++ tmy_add_group_entry(group_name, "/", 1); | |
432 | ++ } | |
433 | ++ } | |
434 | ++ | |
435 | ++ return NULL; | |
436 | ++} | |
437 | ++ | |
438 | ++static int tmy_path_match_group(const struct path_info *pathname, | |
439 | ++ const struct path_group_entry *group, | |
440 | ++ const int may_use_pattern) | |
441 | ++{ | |
442 | ++ struct path_group_member *member; | |
443 | ++ list_for_each_entry(member, &group->path_group_member_list, list) { | |
444 | ++ if (member->is_deleted) | |
445 | ++ continue; | |
446 | ++ if (!member->member_name->is_patterned) { | |
447 | ++ if (!tmy_pathcmp(pathname, member->member_name)) | |
448 | ++ return 1; | |
449 | ++ } else if (may_use_pattern) { | |
450 | ++ if (tmy_path_match(pathname, member->member_name)) | |
451 | ++ return 1; | |
452 | ++ } | |
453 | ++ } | |
454 | ++ | |
455 | ++ return 0; | |
456 | ++} | |
457 | ++ | |
458 | ++/** | |
459 | ++ * tmy_read_path_group_policy - read path group policy. | |
460 | ++ * @head: pointer to "struct io_buffer". | |
461 | ++ * | |
462 | ++ * Returns nonzero if reading incomplete. | |
463 | ++ * Returns zero otherwise. | |
464 | ++ */ | |
465 | ++int tmy_read_path_group_policy(struct io_buffer *head) | |
466 | ++{ | |
467 | ++ struct list_head *gpos; | |
468 | ++ struct list_head *mpos; | |
469 | ++ list_for_each_cookie(gpos, head->read_var1, &path_group_list) { | |
470 | ++ struct path_group_entry *group; | |
471 | ++ group = list_entry(gpos, struct path_group_entry, list); | |
472 | ++ list_for_each_cookie(mpos, head->read_var2, | |
473 | ++ &group->path_group_member_list) { | |
474 | ++ struct path_group_member *member; | |
475 | ++ member = list_entry(mpos, struct path_group_member, | |
476 | ++ list); | |
477 | ++ if (member->is_deleted) | |
478 | ++ continue; | |
479 | ++ if (tmy_io_printf(head, | |
480 | ++ TMY_PATH_GROUP "%s %s\n", | |
481 | ++ group->group_name->name, | |
482 | ++ member->member_name->name)) | |
483 | ++ return -ENOMEM; | |
484 | ++ } | |
485 | ++ } | |
486 | ++ return 0; | |
487 | ++} | |
488 | ++ | |
489 | ++/************************* FILE PATTERN HANDLER *************************/ | |
490 | ++ | |
491 | ++static LIST_HEAD(pattern_list); | |
492 | ++ | |
493 | ++static int tmy_add_pattern_entry(const char *pattern, const bool is_delete) | |
494 | ++{ | |
495 | ++ struct pattern_entry *new_entry; | |
496 | ++ struct pattern_entry *ptr; | |
497 | ++ static DEFINE_MUTEX(mutex); | |
498 | ++ const struct path_info *saved; | |
499 | ++ int error = -ENOMEM; | |
500 | ++ | |
501 | ++ if (!tmy_correct_path(pattern, 0, 1, 0, __FUNCTION__)) | |
502 | ++ return -EINVAL; | |
503 | ++ | |
504 | ++ saved = tmy_save_name(pattern); | |
505 | ++ if (!saved) | |
506 | ++ return -ENOMEM; | |
507 | ++ | |
508 | ++ mutex_lock(&mutex); | |
509 | ++ | |
510 | ++ list_for_each_entry(ptr, &pattern_list, list) { | |
511 | ++ if (saved == ptr->pattern) { | |
512 | ++ ptr->is_deleted = is_delete; | |
513 | ++ error = 0; | |
514 | ++ goto out; | |
515 | ++ } | |
516 | ++ } | |
517 | ++ | |
518 | ++ if (is_delete) { | |
519 | ++ error = -ENOENT; | |
520 | ++ goto out; | |
521 | ++ } | |
522 | ++ | |
523 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
524 | ++ | |
525 | ++ if (!new_entry) | |
526 | ++ goto out; | |
527 | ++ new_entry->pattern = saved; | |
528 | ++ list_add_tail_mb(&new_entry->list, &pattern_list); | |
529 | ++ error = 0; | |
530 | ++out: ; | |
531 | ++ mutex_unlock(&mutex); | |
532 | ++ return error; | |
533 | ++} | |
534 | ++ | |
535 | ++static const struct path_info *tmy_get_pattern(const struct path_info *filename) | |
536 | ++{ | |
537 | ++ struct pattern_entry *ptr; | |
538 | ++ const struct path_info *pattern = NULL; | |
539 | ++ | |
540 | ++ list_for_each_entry(ptr, &pattern_list, list) { | |
541 | ++ if (ptr->is_deleted) | |
542 | ++ continue; | |
543 | ++ if (!tmy_path_match(filename, ptr->pattern)) | |
544 | ++ continue; | |
545 | ++ pattern = ptr->pattern; | |
546 | ++ if (!tmy_strendswith(pattern->name, "/\\*")) | |
547 | ++ break; | |
548 | ++ } | |
549 | ++ | |
550 | ++ if (pattern) | |
551 | ++ filename = pattern; | |
552 | ++ | |
553 | ++ return filename; | |
554 | ++} | |
555 | ++ | |
556 | ++/** | |
557 | ++ * tmy_add_pattern_policy - add or delete file pattern policy. | |
558 | ++ * @pattern: pointer to file pattern entry. | |
559 | ++ * @is_delete: is this delete request? | |
560 | ++ * | |
561 | ++ * Returns zero on success. | |
562 | ++ * Returns nonzero on failure. | |
563 | ++ */ | |
564 | ++int tmy_add_pattern_policy(char *pattern, const bool is_delete) | |
565 | ++{ | |
566 | ++ return tmy_add_pattern_entry(pattern, is_delete); | |
567 | ++} | |
568 | ++ | |
569 | ++/** | |
570 | ++ * tmy_read_pattern_policy - read file pattern policy. | |
571 | ++ * @head: pointer to "struct io_buffer". | |
572 | ++ * | |
573 | ++ * Returns nonzero if reading incomplete. | |
574 | ++ * Returns zero otherwise. | |
575 | ++ */ | |
576 | ++int tmy_read_pattern_policy(struct io_buffer *head) | |
577 | ++{ | |
578 | ++ struct list_head *pos; | |
579 | ++ list_for_each_cookie(pos, head->read_var2, &pattern_list) { | |
580 | ++ struct pattern_entry *ptr; | |
581 | ++ ptr = list_entry(pos, struct pattern_entry, list); | |
582 | ++ if (ptr->is_deleted) | |
583 | ++ continue; | |
584 | ++ if (tmy_io_printf(head, TMY_FILE_PATTERN "%s\n", | |
585 | ++ ptr->pattern->name)) | |
586 | ++ return -ENOMEM; | |
587 | ++ } | |
588 | ++ return 0; | |
589 | ++} | |
590 | ++ | |
591 | ++/*********************** NON REWRITABLE FILE HANDLER ***********************/ | |
592 | ++ | |
593 | ++static LIST_HEAD(no_rewrite_list); | |
594 | ++ | |
595 | ++static int tmy_add_no_rewrite_entry(const char *pattern, const bool is_delete) | |
596 | ++{ | |
597 | ++ struct no_rewrite_entry *new_entry; | |
598 | ++ struct no_rewrite_entry *ptr; | |
599 | ++ static DEFINE_MUTEX(mutex); | |
600 | ++ const struct path_info *saved; | |
601 | ++ int error = -ENOMEM; | |
602 | ++ | |
603 | ++ if (!tmy_correct_path(pattern, 0, 0, 0, __FUNCTION__)) | |
604 | ++ return -EINVAL; | |
605 | ++ saved = tmy_save_name(pattern); | |
606 | ++ if (!saved) | |
607 | ++ return -ENOMEM; | |
608 | ++ | |
609 | ++ mutex_lock(&mutex); | |
610 | ++ list_for_each_entry(ptr, &no_rewrite_list, list) { | |
611 | ++ if (ptr->pattern == saved) { | |
612 | ++ ptr->is_deleted = is_delete; | |
613 | ++ error = 0; | |
614 | ++ goto out; | |
615 | ++ } | |
616 | ++ } | |
617 | ++ | |
618 | ++ if (is_delete) { | |
619 | ++ error = -ENOENT; | |
620 | ++ goto out; | |
621 | ++ } | |
622 | ++ | |
623 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
624 | ++ if (!new_entry) | |
625 | ++ goto out; | |
626 | ++ | |
627 | ++ new_entry->pattern = saved; | |
628 | ++ list_add_tail_mb(&new_entry->list, &no_rewrite_list); | |
629 | ++ error = 0; | |
630 | ++out: ; | |
631 | ++ mutex_unlock(&mutex); | |
632 | ++ | |
633 | ++ return error; | |
634 | ++} | |
635 | ++ | |
636 | ++static int tmy_is_no_rewrite_file(const struct path_info *filename) | |
637 | ++{ | |
638 | ++ struct no_rewrite_entry *ptr; | |
639 | ++ | |
640 | ++ list_for_each_entry(ptr, &no_rewrite_list, list) { | |
641 | ++ if (ptr->is_deleted) | |
642 | ++ continue; | |
643 | ++ if (!tmy_path_match(filename, ptr->pattern)) | |
644 | ++ continue; | |
645 | ++ return 1; | |
646 | ++ } | |
647 | ++ | |
648 | ++ return 0; | |
649 | ++} | |
650 | ++ | |
651 | ++/** | |
652 | ++ * tmy_add_no_rewrite_policy - add or delete no-rewrite policy. | |
653 | ++ * @pattern: pointer to no-rewrite entry. | |
654 | ++ * @is_delete: is this delete request? | |
655 | ++ * | |
656 | ++ * Returns zero on success. | |
657 | ++ * Returns nonzero on failure. | |
658 | ++ */ | |
659 | ++int tmy_add_no_rewrite_policy(char *pattern, const bool is_delete) | |
660 | ++{ | |
661 | ++ return tmy_add_no_rewrite_entry(pattern, is_delete); | |
662 | ++} | |
663 | ++ | |
664 | ++/** | |
665 | ++ * tmy_read_no_rewrite_policy - read no-rewrite policy. | |
666 | ++ * @head: pointer to "struct io_buffer". | |
667 | ++ * | |
668 | ++ * Returns nonzero if reading incomplete. | |
669 | ++ * Returns zero otherwise. | |
670 | ++ */ | |
671 | ++int tmy_read_no_rewrite_policy(struct io_buffer *head) | |
672 | ++{ | |
673 | ++ struct list_head *pos; | |
674 | ++ list_for_each_cookie(pos, head->read_var2, &no_rewrite_list) { | |
675 | ++ struct no_rewrite_entry *ptr; | |
676 | ++ ptr = list_entry(pos, struct no_rewrite_entry, list); | |
677 | ++ if (ptr->is_deleted) | |
678 | ++ continue; | |
679 | ++ if (tmy_io_printf(head, TMY_DENY_REWRITE "%s\n", | |
680 | ++ ptr->pattern->name)) | |
681 | ++ return -ENOMEM; | |
682 | ++ } | |
683 | ++ return 0; | |
684 | ++} | |
685 | ++ | |
686 | ++/************************* FILE ACL HANDLER *************************/ | |
687 | ++ | |
688 | ++static int tmy_add_file_acl(const char *filename, | |
689 | ++ u8 perm, | |
690 | ++ struct domain_info * const domain, | |
691 | ++ const struct condition_list *cond, | |
692 | ++ const bool is_delete) | |
693 | ++{ | |
694 | ++ const struct path_info *saved; | |
695 | ++ struct acl_info *ptr; | |
696 | ++ struct file_acl *acl; | |
697 | ++ int error = -ENOMEM; | |
698 | ++ bool is_group = 0; | |
699 | ++ | |
700 | ++ if (!domain) | |
701 | ++ return -EINVAL; | |
702 | ++ if (perm > 7 || !perm) { | |
703 | ++ printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", | |
704 | ++ __FUNCTION__, perm, filename); | |
705 | ++ return -EINVAL; | |
706 | ++ } | |
707 | ++ if (!tmy_correct_path(filename, 0, 0, 0, __FUNCTION__)) | |
708 | ++ return -EINVAL; | |
709 | ++ | |
710 | ++ if (filename[0] == '@') { | |
711 | ++ /* This cast is OK because I don't dereference. */ | |
712 | ++ saved = (struct path_info *) tmy_new_path_group(filename + 1); | |
713 | ++ if (!saved) | |
714 | ++ return -ENOMEM; | |
715 | ++ is_group = 1; | |
716 | ++ } else { | |
717 | ++ | |
718 | ++ if (tmy_strendswith(filename, "/")) | |
719 | ++ /* | |
720 | ++ * Valid permissions for directory are | |
721 | ++ * only 'allow_mkdir' and 'allow_rmdir'. | |
722 | ++ */ | |
723 | ++ return 0; | |
724 | ++ | |
725 | ++ saved = tmy_save_name(filename); | |
726 | ++ if (!saved) | |
727 | ++ return -ENOMEM; | |
728 | ++ | |
729 | ++ if (!is_delete && perm == 4 && | |
730 | ++ tmy_globally_readable(saved)) | |
731 | ++ return 0; | |
732 | ++ | |
733 | ++ } | |
734 | ++ | |
735 | ++ mutex_lock(&domain_acl_lock); | |
736 | ++ | |
737 | ++ if (is_delete) | |
738 | ++ goto remove; | |
739 | ++ | |
740 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
741 | ++ acl = (struct file_acl *) ptr; | |
742 | ++ if ((ptr->type == TMY_TYPE_FILE_ACL) && | |
743 | ++ ptr->cond == cond && | |
744 | ++ (acl->u.filename == saved)) { | |
745 | ++ if (ptr->is_deleted) { | |
746 | ++ acl->perm = 0; | |
747 | ++ mb(); /* Avoid out-of-order execution. */ | |
748 | ++ ptr->is_deleted = 0; | |
749 | ++ } | |
750 | ++ /* Found. Just 'OR' the permission bits. */ | |
751 | ++ acl->perm |= perm; | |
752 | ++ error = 0; | |
753 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
754 | ++ goto ok; | |
755 | ++ } | |
756 | ++ } | |
757 | ++ /* Not found. Append it to the tail. */ | |
758 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
759 | ++ if (!acl) | |
760 | ++ goto ok; | |
761 | ++ | |
762 | ++ acl->head.type = TMY_TYPE_FILE_ACL; | |
763 | ++ acl->head.cond = cond; | |
764 | ++ acl->perm = perm; | |
765 | ++ acl->u_is_group = is_group; | |
766 | ++ acl->u.filename = saved; | |
767 | ++ error = tmy_add_acl(domain, (struct acl_info *) acl); | |
768 | ++ goto ok; | |
769 | ++remove: ; | |
770 | ++ error = -ENOENT; | |
771 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
772 | ++ acl = (struct file_acl *) ptr; | |
773 | ++ if (ptr->type != TMY_TYPE_FILE_ACL || | |
774 | ++ ptr->cond != cond || | |
775 | ++ ptr->is_deleted || | |
776 | ++ acl->perm != perm || | |
777 | ++ acl->u.filename != saved) | |
778 | ++ continue; | |
779 | ++ error = tmy_del_acl(ptr); | |
780 | ++ break; | |
781 | ++ } | |
782 | ++ok: ; | |
783 | ++ mutex_unlock(&domain_acl_lock); | |
784 | ++ return error; | |
785 | ++} | |
786 | ++ | |
787 | ++static int tmy_file_acl(const struct path_info *filename, const u8 perm, | |
788 | ++ struct obj_info *obj) | |
789 | ++{ | |
790 | ++ const struct domain_info *domain = TMY_SECURITY->domain; | |
791 | ++ struct acl_info *ptr; | |
792 | ++ const int may_use_pat = ((perm & 1) == 0); | |
793 | ++ | |
794 | ++ if (!tmy_flags(TMY_MAC_FOR_FILE)) | |
795 | ++ return 0; | |
796 | ++ if (!filename->is_dir) { | |
797 | ++ if (perm == 4 && tmy_globally_readable(filename)) | |
798 | ++ return 0; | |
799 | ++ } | |
800 | ++ | |
801 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
802 | ++ struct file_acl *acl = (struct file_acl *) ptr; | |
803 | ++ | |
804 | ++ if (ptr->type != TMY_TYPE_FILE_ACL || | |
805 | ++ ptr->is_deleted || | |
806 | ++ (acl->perm & perm) != perm || | |
807 | ++ tmy_check_condition(ptr->cond, obj)) | |
808 | ++ continue; | |
809 | ++ | |
810 | ++ if (acl->u_is_group) { | |
811 | ++ if (tmy_path_match_group(filename, | |
812 | ++ acl->u.group, | |
813 | ++ may_use_pat)) | |
814 | ++ return 0; | |
815 | ++ } else { | |
816 | ++ if ((may_use_pat || !acl->u.filename->is_patterned) && | |
817 | ++ tmy_path_match(filename, acl->u.filename)) | |
818 | ++ return 0; | |
819 | ++ } | |
820 | ++ } | |
821 | ++ | |
822 | ++ return -EPERM; | |
823 | ++} | |
824 | ++ | |
825 | ++static int tmy_file_perm2(const struct path_info *filename, | |
826 | ++ const u8 perm, | |
827 | ++ struct obj_info *obj, | |
828 | ++ const char *operation) | |
829 | ++{ | |
830 | ++ int error = 0; | |
831 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
832 | ++ const u8 profile = domain->profile; | |
833 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
834 | ++ const bool is_enforce = (mode == 3); | |
835 | ++ | |
836 | ++ if (!filename) | |
837 | ++ return 0; | |
838 | ++ | |
839 | ++ error = tmy_file_acl(filename, perm, obj); | |
840 | ++ | |
841 | ++ tmy_audit_file_log(filename, perm, !error, profile, mode); | |
842 | ++ | |
843 | ++ if (!error) | |
844 | ++ return error; | |
845 | ++ | |
846 | ++ if (tmy_flags(TMY_VERBOSE)) | |
847 | ++ tmy_audit("TOMOYO-%s: Access %d(%s) to %s denied for %s\n", | |
848 | ++ tmy_getmsg(is_enforce), perm, operation, | |
849 | ++ filename->name, tmy_lastname(domain)); | |
850 | ++ | |
851 | ++ if (is_enforce) | |
852 | ++ error = tmy_supervisor("%s\n%d %s\n", | |
853 | ++ domain->domainname->name, | |
854 | ++ perm, filename->name); | |
855 | ++ | |
856 | ++ else if (mode == 1 && tmy_quota()) { | |
857 | ++ /* Don't use patterns if execution bit is on. */ | |
858 | ++ const struct path_info *patterned = | |
859 | ++ ((perm & 1) == 0) ? | |
860 | ++ tmy_get_pattern(filename) : filename; | |
861 | ++ tmy_add_file_acl(patterned->name, perm, domain, NULL, 0); | |
862 | ++ } | |
863 | ++ | |
864 | ++ if (!is_enforce) | |
865 | ++ error = 0; | |
866 | ++ | |
867 | ++ return error; | |
868 | ++} | |
869 | ++ | |
870 | ++/** | |
871 | ++ * tmy_file_perm - check permission for sysctl(2) operation. | |
872 | ++ * @filename0: pointer to filename returned by sysctlpath_from_table(). | |
873 | ++ * @perm: mode (read = 4, write = 2, read-write = 6). | |
874 | ++ * @operation: pointer to error message. | |
875 | ++ * | |
876 | ++ * Returns zero if permission granted. | |
877 | ++ * Returns nonzero if permission denied. | |
878 | ++ */ | |
879 | ++int tmy_file_perm(const char *filename0, const u8 perm, const char *operation) | |
880 | ++{ | |
881 | ++ struct path_info filename; | |
882 | ++ | |
883 | ++ if (!tmy_flags(TMY_MAC_FOR_FILE)) | |
884 | ++ return 0; | |
885 | ++ | |
886 | ++ filename.name = filename0; | |
887 | ++ tmy_fill_path_info(&filename); | |
888 | ++ | |
889 | ++ return tmy_file_perm2(&filename, perm, NULL, operation); | |
890 | ++} | |
891 | ++ | |
892 | ++/** | |
893 | ++ * tmy_add_file_policy - add or delete file policy. | |
894 | ++ * @data: a line to parse. | |
895 | ++ * @domain: pointer to "struct domain_info". | |
896 | ++ * @cond: pointer to "struct condition_list". May be NULL. | |
897 | ++ * @is_delete: is this delete request? | |
898 | ++ * | |
899 | ++ * Returns zero on success. | |
900 | ++ * Returns nonzero on failure. | |
901 | ++ */ | |
902 | ++int tmy_add_file_policy(char *data, | |
903 | ++ struct domain_info *domain, | |
904 | ++ const struct condition_list *cond, | |
905 | ++ const bool is_delete) | |
906 | ++{ | |
907 | ++ char *filename = strchr(data, ' '); | |
908 | ++ unsigned int perm; | |
909 | ++ u8 type; | |
910 | ++ | |
911 | ++ if (!filename) | |
912 | ++ return -EINVAL; | |
913 | ++ *filename++ = '\0'; | |
914 | ++ | |
915 | ++ if (sscanf(data, "%u", &perm) == 1) | |
916 | ++ return tmy_add_file_acl(filename, (u8) perm, domain, cond, | |
917 | ++ is_delete); | |
918 | ++ | |
919 | ++ if (strncmp(data, "allow_", 6)) | |
920 | ++ goto out; | |
921 | ++ | |
922 | ++ data += 6; | |
923 | ++ | |
924 | ++ for (type = 0; acl_type_array[type].keyword; type++) { | |
925 | ++ if (strcmp(data, acl_type_array[type].keyword)) | |
926 | ++ continue; | |
927 | ++ | |
928 | ++ if (acl_type_array[type].paths == 2) { | |
929 | ++ char *filename2 = strchr(filename, ' '); | |
930 | ++ | |
931 | ++ if (!filename2) | |
932 | ++ break; | |
933 | ++ *filename2++ = '\0'; | |
934 | ++ return tmy_add_double_write_acl(type, filename, | |
935 | ++ filename2, | |
936 | ++ domain, cond, | |
937 | ++ is_delete); | |
938 | ++ } else | |
939 | ++ return tmy_add_single_write_acl(type, filename, | |
940 | ++ domain, cond, | |
941 | ++ is_delete); | |
942 | ++ | |
943 | ++ break; | |
944 | ++ } | |
945 | ++out: ; | |
946 | ++ return -EINVAL; | |
947 | ++} | |
948 | ++ | |
949 | ++static int tmy_add_single_write_acl(const u8 type, | |
950 | ++ const char *filename, | |
951 | ++ struct domain_info * const domain, | |
952 | ++ const struct condition_list *cond, | |
953 | ++ const bool is_delete) | |
954 | ++{ | |
955 | ++ const struct path_info *saved; | |
956 | ++ struct acl_info *ptr; | |
957 | ++ struct single_acl *acl; | |
958 | ++ int error = -ENOMEM; | |
959 | ++ bool is_group = 0; | |
960 | ++ | |
961 | ++ if (!domain) | |
962 | ++ return -EINVAL; | |
963 | ++ if (!tmy_correct_path(filename, 0, 0, 0, __FUNCTION__)) | |
964 | ++ return -EINVAL; | |
965 | ++ | |
966 | ++ if (filename[0] == '@') { | |
967 | ++ /* This cast is OK because I don't dereference. */ | |
968 | ++ saved = (struct path_info *) tmy_new_path_group(filename + 1); | |
969 | ++ if (!saved) | |
970 | ++ return -ENOMEM; | |
971 | ++ is_group = 1; | |
972 | ++ } else { | |
973 | ++ saved = tmy_save_name(filename); | |
974 | ++ if (!saved) | |
975 | ++ return -ENOMEM; | |
976 | ++ } | |
977 | ++ | |
978 | ++ mutex_lock(&domain_acl_lock); | |
979 | ++ if (is_delete) | |
980 | ++ goto remove; | |
981 | ++ | |
982 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
983 | ++ acl = (struct single_acl *) ptr; | |
984 | ++ if (ptr->type == type && ptr->cond == cond) { | |
985 | ++ if (acl->u.filename == saved) { | |
986 | ++ ptr->is_deleted = 0; | |
987 | ++ /* Found. Nothing to do. */ | |
988 | ++ error = 0; | |
989 | ++ goto ok; | |
990 | ++ } | |
991 | ++ } | |
992 | ++ } | |
993 | ++ /* Not found. Append it to the tail. */ | |
994 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
995 | ++ if (!acl) | |
996 | ++ goto ok; | |
997 | ++ | |
998 | ++ acl->head.type = type; | |
999 | ++ acl->head.cond = cond; | |
1000 | ++ acl->u_is_group = is_group; | |
1001 | ++ acl->u.filename = saved; | |
1002 | ++ error = tmy_add_acl(domain, (struct acl_info *) acl); | |
1003 | ++ goto ok; | |
1004 | ++remove: ; | |
1005 | ++ error = -ENOENT; | |
1006 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
1007 | ++ acl = (struct single_acl *) ptr; | |
1008 | ++ | |
1009 | ++ if (ptr->type != type || ptr->is_deleted || | |
1010 | ++ ptr->cond != cond || acl->u.filename != saved) | |
1011 | ++ continue; | |
1012 | ++ | |
1013 | ++ error = tmy_del_acl(ptr); | |
1014 | ++ break; | |
1015 | ++ } | |
1016 | ++ok: ; | |
1017 | ++ mutex_unlock(&domain_acl_lock); | |
1018 | ++ | |
1019 | ++ return error; | |
1020 | ++} | |
1021 | ++ | |
1022 | ++static int tmy_add_double_write_acl(const u8 type, | |
1023 | ++ const char *filename1, | |
1024 | ++ const char *filename2, | |
1025 | ++ struct domain_info * const domain, | |
1026 | ++ const struct condition_list *cond, | |
1027 | ++ const bool is_delete) | |
1028 | ++{ | |
1029 | ++ const struct path_info *saved1; | |
1030 | ++ const struct path_info *saved2; | |
1031 | ++ struct acl_info *ptr; | |
1032 | ++ struct double_acl *acl; | |
1033 | ++ int error = -ENOMEM; | |
1034 | ++ bool is_group1 = 0; | |
1035 | ++ bool is_group2 = 0; | |
1036 | ++ | |
1037 | ++ if (!domain) | |
1038 | ++ return -EINVAL; | |
1039 | ++ if (!tmy_correct_path(filename1, 0, 0, 0, __FUNCTION__) || | |
1040 | ++ !tmy_correct_path(filename2, 0, 0, 0, __FUNCTION__)) | |
1041 | ++ return -EINVAL; | |
1042 | ++ | |
1043 | ++ if (filename1[0] == '@') { | |
1044 | ++ /* This cast is OK because I don't dereference. */ | |
1045 | ++ saved1 = (struct path_info *) tmy_new_path_group(filename1 + 1); | |
1046 | ++ if (!saved1) | |
1047 | ++ return -ENOMEM; | |
1048 | ++ is_group1 = 1; | |
1049 | ++ } else { | |
1050 | ++ saved1 = tmy_save_name(filename1); | |
1051 | ++ if (!saved1) | |
1052 | ++ return -ENOMEM; | |
1053 | ++ } | |
1054 | ++ | |
1055 | ++ if (filename2[0] == '@') { | |
1056 | ++ /* This cast is OK because I don't dereference. */ | |
1057 | ++ saved2 = (struct path_info *) tmy_new_path_group(filename2 + 1); | |
1058 | ++ if (!saved2) | |
1059 | ++ return -ENOMEM; | |
1060 | ++ is_group2 = 1; | |
1061 | ++ } else { | |
1062 | ++ saved2 = tmy_save_name(filename2); | |
1063 | ++ if (!saved2) | |
1064 | ++ return -ENOMEM; | |
1065 | ++ } | |
1066 | ++ | |
1067 | ++ mutex_lock(&domain_acl_lock); | |
1068 | ++ | |
1069 | ++ if (is_delete) | |
1070 | ++ goto remove; | |
1071 | ++ | |
1072 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
1073 | ++ acl = (struct double_acl *) ptr; | |
1074 | ++ if (ptr->type == type && ptr->cond == cond) { | |
1075 | ++ if (acl->u1.filename1 == saved1 && | |
1076 | ++ acl->u2.filename2 == saved2) { | |
1077 | ++ ptr->is_deleted = 0; | |
1078 | ++ /* Found. Nothing to do. */ | |
1079 | ++ error = 0; | |
1080 | ++ goto ok; | |
1081 | ++ } | |
1082 | ++ } | |
1083 | ++ } | |
1084 | ++ /* Not found. Append it to the tail. */ | |
1085 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
1086 | ++ if (!acl) | |
1087 | ++ goto ok; | |
1088 | ++ | |
1089 | ++ acl->head.type = type; | |
1090 | ++ acl->head.cond = cond; | |
1091 | ++ acl->u1_is_group = is_group1; | |
1092 | ++ acl->u2_is_group = is_group2; | |
1093 | ++ acl->u1.filename1 = saved1; | |
1094 | ++ acl->u2.filename2 = saved2; | |
1095 | ++ error = tmy_add_acl(domain, | |
1096 | ++ (struct acl_info *) acl); | |
1097 | ++ goto ok; | |
1098 | ++remove: ; | |
1099 | ++ error = -ENOENT; | |
1100 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
1101 | ++ acl = (struct double_acl *) ptr; | |
1102 | ++ if (ptr->type != type || ptr->is_deleted || | |
1103 | ++ ptr->cond != cond || | |
1104 | ++ acl->u1.filename1 != saved1 || | |
1105 | ++ acl->u2.filename2 != saved2) | |
1106 | ++ continue; | |
1107 | ++ error = tmy_del_acl(ptr); | |
1108 | ++ break; | |
1109 | ++ } | |
1110 | ++ ok: ; | |
1111 | ++ mutex_unlock(&domain_acl_lock); | |
1112 | ++ return error; | |
1113 | ++} | |
1114 | ++ | |
1115 | ++static int tmy_single_write_acl(const u8 type, | |
1116 | ++ const struct path_info *filename, | |
1117 | ++ struct obj_info *obj) | |
1118 | ++{ | |
1119 | ++ const struct domain_info *domain = TMY_SECURITY->domain; | |
1120 | ++ struct acl_info *ptr; | |
1121 | ++ | |
1122 | ++ if (!tmy_flags(TMY_MAC_FOR_FILE)) | |
1123 | ++ return 0; | |
1124 | ++ | |
1125 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
1126 | ++ struct single_acl *acl = (struct single_acl *) ptr; | |
1127 | ++ | |
1128 | ++ if (ptr->type != type || ptr->is_deleted || | |
1129 | ++ tmy_check_condition(ptr->cond, obj)) | |
1130 | ++ continue; | |
1131 | ++ | |
1132 | ++ if (acl->u_is_group) { | |
1133 | ++ if (!tmy_path_match_group(filename, acl->u.group, 1)) | |
1134 | ++ continue; | |
1135 | ++ } else { | |
1136 | ++ if (!tmy_path_match(filename, acl->u.filename)) | |
1137 | ++ continue; | |
1138 | ++ } | |
1139 | ++ return 0; | |
1140 | ++ } | |
1141 | ++ | |
1142 | ++ return -EPERM; | |
1143 | ++} | |
1144 | ++ | |
1145 | ++static int tmy_double_write_acl(const u8 type, | |
1146 | ++ const struct path_info *filename1, | |
1147 | ++ const struct path_info *filename2, | |
1148 | ++ struct obj_info *obj) | |
1149 | ++{ | |
1150 | ++ const struct domain_info *domain = TMY_SECURITY->domain; | |
1151 | ++ struct acl_info *ptr; | |
1152 | ++ | |
1153 | ++ if (!tmy_flags(TMY_MAC_FOR_FILE)) | |
1154 | ++ return 0; | |
1155 | ++ | |
1156 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
1157 | ++ struct double_acl *acl = (struct double_acl *) ptr; | |
1158 | ++ | |
1159 | ++ if (ptr->type != type || ptr->is_deleted || | |
1160 | ++ tmy_check_condition(ptr->cond, obj)) | |
1161 | ++ continue; | |
1162 | ++ | |
1163 | ++ if (acl->u1_is_group) { | |
1164 | ++ if (!tmy_path_match_group(filename1, | |
1165 | ++ acl->u1.group1, 1)) | |
1166 | ++ continue; | |
1167 | ++ } else { | |
1168 | ++ if (!tmy_path_match(filename1, acl->u1.filename1)) | |
1169 | ++ continue; | |
1170 | ++ } | |
1171 | ++ | |
1172 | ++ if (acl->u2_is_group) { | |
1173 | ++ if (!tmy_path_match_group(filename2, | |
1174 | ++ acl->u2.group2, 1)) | |
1175 | ++ continue; | |
1176 | ++ } else { | |
1177 | ++ if (!tmy_path_match(filename2, acl->u2.filename2)) | |
1178 | ++ continue; | |
1179 | ++ } | |
1180 | ++ | |
1181 | ++ return 0; | |
1182 | ++ } | |
1183 | ++ | |
1184 | ++ return -EPERM; | |
1185 | ++} | |
1186 | ++ | |
1187 | ++static int tmy_single_write_perm2(const unsigned int operation, | |
1188 | ++ const struct path_info *filename, | |
1189 | ++ struct obj_info *obj) | |
1190 | ++{ | |
1191 | ++ int error; | |
1192 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
1193 | ++ const u8 profile = domain->profile; | |
1194 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
1195 | ++ const bool is_enforce = (mode == 3); | |
1196 | ++ | |
1197 | ++ if (!mode) | |
1198 | ++ return 0; | |
1199 | ++ | |
1200 | ++ error = tmy_single_write_acl(operation, filename, obj); | |
1201 | ++ | |
1202 | ++ tmy_audit_write_log(tmy_acltype2keyword(operation), | |
1203 | ++ filename, NULL, !error, profile, mode); | |
1204 | ++ | |
1205 | ++ if (!error) | |
1206 | ++ goto ok; | |
1207 | ++ | |
1208 | ++ if (tmy_flags(TMY_VERBOSE)) | |
1209 | ++ tmy_audit("TOMOYO-%s: Access '%s %s' denied for %s\n", | |
1210 | ++ tmy_getmsg(is_enforce), | |
1211 | ++ tmy_acltype2keyword(operation), filename->name, | |
1212 | ++ tmy_lastname(domain)); | |
1213 | ++ | |
1214 | ++ if (is_enforce) | |
1215 | ++ error = tmy_supervisor("%s\nallow_%s %s\n", | |
1216 | ++ domain->domainname->name, | |
1217 | ++ tmy_acltype2keyword(operation), | |
1218 | ++ filename->name); | |
1219 | ++ | |
1220 | ++ else if (mode == 1 && tmy_quota()) | |
1221 | ++ tmy_add_single_write_acl(operation, | |
1222 | ++ tmy_get_pattern(filename)->name, | |
1223 | ++ domain, NULL, 0); | |
1224 | ++ | |
1225 | ++ if (!is_enforce) | |
1226 | ++ error = 0; | |
1227 | ++ | |
1228 | ++ok: ; | |
1229 | ++ if (!error && operation == TMY_TYPE_TRUNCATE_ACL && | |
1230 | ++ tmy_is_no_rewrite_file(filename)) | |
1231 | ++ error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL, | |
1232 | ++ filename, obj); | |
1233 | ++ | |
1234 | ++ return error; | |
1235 | ++} | |
1236 | ++ | |
1237 | ++/** | |
1238 | ++ * tmy_exec_perm - check permission for execve(2) operation. | |
1239 | ++ * @filename: pointer to filename to execute. | |
1240 | ++ * @filp: pointer to "struct file". | |
1241 | ++ * | |
1242 | ++ * Returns zero if permission granted. | |
1243 | ++ * Returns nonzero if permission denied. | |
1244 | ++ */ | |
1245 | ++int tmy_exec_perm(const struct path_info *filename, struct file *filp) | |
1246 | ++{ | |
1247 | ++ struct obj_info obj; | |
1248 | ++ if (!tmy_flags(TMY_MAC_FOR_FILE)) | |
1249 | ++ return 0; | |
1250 | ++ memset(&obj, 0, sizeof(obj)); | |
1251 | ++ obj.path1_dentry = filp->f_dentry; | |
1252 | ++ obj.path1_vfsmnt = filp->f_vfsmnt; | |
1253 | ++ return tmy_file_perm2(filename, 1, &obj, "do_execve"); | |
1254 | ++} | |
1255 | ++ | |
1256 | ++/** | |
1257 | ++ * tmy_open_perm - check permission for open(2) operation. | |
1258 | ++ * @dentry: pointer to "struct dentry". | |
1259 | ++ * @mnt: pointer to "struct vfsmount". | |
1260 | ++ * @flag: open flags. | |
1261 | ++ * | |
1262 | ++ * Returns zero if permission granted. | |
1263 | ++ * Returns nonzero if permission denied. | |
1264 | ++ */ | |
1265 | ++int tmy_open_perm(struct dentry *dentry, | |
1266 | ++ struct vfsmount *mnt, | |
1267 | ++ const int flag) | |
1268 | ++{ | |
1269 | ++ struct obj_info obj; | |
1270 | ++ const int acc_mode = ACC_MODE(flag); | |
1271 | ++ int error = -ENOMEM; | |
1272 | ++ struct path_info *buf; | |
1273 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
1274 | ++ const bool is_enforce = (mode == 3); | |
1275 | ++ | |
1276 | ++ if (!mode) | |
1277 | ++ return 0; | |
1278 | ++ if (acc_mode == 0) | |
1279 | ++ return 0; | |
1280 | ++ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | |
1281 | ++ /* I don't check directories here */ | |
1282 | ++ /* because mkdir() and rmdir() don't call me. */ | |
1283 | ++ return 0; | |
1284 | ++ | |
1285 | ++ buf = tmy_get_path(dentry, mnt); | |
1286 | ++ | |
1287 | ++ if (!buf) | |
1288 | ++ goto out; | |
1289 | ++ | |
1290 | ++ memset(&obj, 0, sizeof(obj)); | |
1291 | ++ obj.path1_dentry = dentry; | |
1292 | ++ obj.path1_vfsmnt = mnt; | |
1293 | ++ | |
1294 | ++ error = 0; | |
1295 | ++ if ((acc_mode & MAY_WRITE) && | |
1296 | ++ ((flag & O_TRUNC) || !(flag & O_APPEND)) && | |
1297 | ++ tmy_is_no_rewrite_file(buf)) | |
1298 | ++ error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL, | |
1299 | ++ buf, &obj); | |
1300 | ++ | |
1301 | ++ if (error == 0) | |
1302 | ++ error = tmy_file_perm2(buf, acc_mode, &obj, "open"); | |
1303 | ++ | |
1304 | ++ if (error == 0 && (flag & O_TRUNC)) | |
1305 | ++ error = tmy_single_write_perm2(TMY_TYPE_TRUNCATE_ACL, | |
1306 | ++ buf, &obj); | |
1307 | ++ | |
1308 | ++ tmy_free(buf); | |
1309 | ++ | |
1310 | ++out: ; | |
1311 | ++ if (!is_enforce) | |
1312 | ++ error = 0; | |
1313 | ++ return error; | |
1314 | ++} | |
1315 | ++ | |
1316 | ++/** | |
1317 | ++ * tmy_single_write_perm - check permission for create(2) etc. operation. | |
1318 | ++ * @operation: operation index number. | |
1319 | ++ * @dentry: pointer to "struct dentry". | |
1320 | ++ * @mnt: pointer to "struct vfsmount". | |
1321 | ++ * | |
1322 | ++ * Returns zero if permission granted. | |
1323 | ++ * Returns nonzero if permission denied. | |
1324 | ++ */ | |
1325 | ++int tmy_single_write_perm(const unsigned int operation, | |
1326 | ++ struct dentry *dentry, | |
1327 | ++ struct vfsmount *mnt) | |
1328 | ++{ | |
1329 | ++ struct obj_info obj; | |
1330 | ++ int error = -ENOMEM; | |
1331 | ++ struct path_info *buf; | |
1332 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
1333 | ++ const bool is_enforce = (mode == 3); | |
1334 | ++ | |
1335 | ++ if (!mode) | |
1336 | ++ return 0; | |
1337 | ++ | |
1338 | ++ buf = tmy_get_path(dentry, mnt); | |
1339 | ++ | |
1340 | ++ if (!buf) | |
1341 | ++ goto out; | |
1342 | ++ | |
1343 | ++ memset(&obj, 0, sizeof(obj)); | |
1344 | ++ obj.path1_dentry = dentry; | |
1345 | ++ obj.path1_vfsmnt = mnt; | |
1346 | ++ | |
1347 | ++ switch (operation) { | |
1348 | ++ case TMY_TYPE_MKDIR_ACL: | |
1349 | ++ case TMY_TYPE_RMDIR_ACL: | |
1350 | ++ if (!buf->is_dir) { | |
1351 | ++ strcat((char *) buf->name, "/"); | |
1352 | ++ tmy_fill_path_info(buf); | |
1353 | ++ } | |
1354 | ++ } | |
1355 | ++ error = tmy_single_write_perm2(operation, buf, &obj); | |
1356 | ++ tmy_free(buf); | |
1357 | ++ | |
1358 | ++out: ; | |
1359 | ++ if (!is_enforce) | |
1360 | ++ error = 0; | |
1361 | ++ | |
1362 | ++ return error; | |
1363 | ++} | |
1364 | ++ | |
1365 | ++/** | |
1366 | ++ * tmy_rewrite_perm - check permission for truncate/overwrite operation. | |
1367 | ++ * @filp: pointer to "struct file". | |
1368 | ++ * | |
1369 | ++ * Returns zero if permission granted. | |
1370 | ++ * Returns nonzero if permission denied. | |
1371 | ++ */ | |
1372 | ++int tmy_rewrite_perm(struct file *filp) | |
1373 | ++{ | |
1374 | ++ int error = -ENOMEM; | |
1375 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
1376 | ++ const bool is_enforce = (mode == 3); | |
1377 | ++ struct path_info *buf; | |
1378 | ++ | |
1379 | ++ if (!mode) | |
1380 | ++ return 0; | |
1381 | ++ | |
1382 | ++ buf = tmy_get_path(filp->f_dentry, filp->f_vfsmnt); | |
1383 | ++ if (!buf) | |
1384 | ++ goto out; | |
1385 | ++ | |
1386 | ++ if (tmy_is_no_rewrite_file(buf)) { | |
1387 | ++ struct obj_info obj; | |
1388 | ++ memset(&obj, 0, sizeof(obj)); | |
1389 | ++ obj.path1_dentry = filp->f_dentry; | |
1390 | ++ obj.path1_vfsmnt = filp->f_vfsmnt; | |
1391 | ++ error = tmy_single_write_perm2(TMY_TYPE_REWRITE_ACL, | |
1392 | ++ buf, &obj); | |
1393 | ++ } else | |
1394 | ++ error = 0; | |
1395 | ++ | |
1396 | ++ tmy_free(buf); | |
1397 | ++ | |
1398 | ++out: ; | |
1399 | ++ if (!is_enforce) | |
1400 | ++ error = 0; | |
1401 | ++ return error; | |
1402 | ++} | |
1403 | ++ | |
1404 | ++/** | |
1405 | ++ * tmy_double_write_perm - check permission for link(2)/rename(2) operation. | |
1406 | ++ * @operation: operation index number. | |
1407 | ++ * @dentry1: pointer to "struct dentry". | |
1408 | ++ * @mnt1: pointer to "struct vfsmount". | |
1409 | ++ * @dentry2: pointer to "struct dentry". | |
1410 | ++ * @mnt2: pointer to "struct vfsmount". | |
1411 | ++ * | |
1412 | ++ * Returns zero if permission granted. | |
1413 | ++ * Returns nonzero if permission denied. | |
1414 | ++ */ | |
1415 | ++int tmy_double_write_perm(const unsigned int operation, | |
1416 | ++ struct dentry *dentry1, | |
1417 | ++ struct vfsmount *mnt1, | |
1418 | ++ struct dentry *dentry2, | |
1419 | ++ struct vfsmount *mnt2) | |
1420 | ++{ | |
1421 | ++ struct obj_info obj; | |
1422 | ++ int error = -ENOMEM; | |
1423 | ++ struct path_info *buf1; | |
1424 | ++ struct path_info *buf2; | |
1425 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
1426 | ++ const u8 profile = domain->profile; | |
1427 | ++ const unsigned int mode = tmy_flags(TMY_MAC_FOR_FILE); | |
1428 | ++ const bool is_enforce = (mode == 3); | |
1429 | ++ | |
1430 | ++ if (!mode) | |
1431 | ++ return 0; | |
1432 | ++ buf1 = tmy_get_path(dentry1, mnt1); | |
1433 | ++ buf2 = tmy_get_path(dentry2, mnt2); | |
1434 | ++ | |
1435 | ++ if (!buf1 || !buf2) | |
1436 | ++ goto out; | |
1437 | ++ | |
1438 | ++ memset(&obj, 0, sizeof(obj)); | |
1439 | ++ obj.path1_dentry = dentry1; | |
1440 | ++ obj.path1_vfsmnt = mnt1; | |
1441 | ++ obj.path2_dentry = dentry2; | |
1442 | ++ obj.path2_vfsmnt = mnt2; | |
1443 | ++ if (operation == TMY_TYPE_RENAME_ACL) { | |
1444 | ++ /* TMY_TYPE_LINK_ACL can't reach here for directory. */ | |
1445 | ++ if (dentry1->d_inode && S_ISDIR(dentry1->d_inode->i_mode)) { | |
1446 | ++ if (!buf1->is_dir) { | |
1447 | ++ strcat((char *) buf1->name, "/"); | |
1448 | ++ tmy_fill_path_info(buf1); | |
1449 | ++ } | |
1450 | ++ if (!buf2->is_dir) { | |
1451 | ++ strcat((char *) buf2->name, "/"); | |
1452 | ++ tmy_fill_path_info(buf2); | |
1453 | ++ } | |
1454 | ++ } | |
1455 | ++ } | |
1456 | ++ error = tmy_double_write_acl(operation, buf1, buf2, &obj); | |
1457 | ++ | |
1458 | ++ tmy_audit_write_log(tmy_acltype2keyword(operation), | |
1459 | ++ buf1, buf2, !error, profile, mode); | |
1460 | ++ | |
1461 | ++ if (!error) | |
1462 | ++ goto out; | |
1463 | ++ | |
1464 | ++ if (tmy_flags(TMY_VERBOSE)) | |
1465 | ++ tmy_audit("TOMOYO-%s: Access '%s %s %s' denied for %s\n", | |
1466 | ++ tmy_getmsg(is_enforce), | |
1467 | ++ tmy_acltype2keyword(operation), | |
1468 | ++ buf1->name, buf2->name, tmy_lastname(domain)); | |
1469 | ++ | |
1470 | ++ if (is_enforce) | |
1471 | ++ error = tmy_supervisor("%s\nallow_%s %s %s\n", | |
1472 | ++ domain->domainname->name, | |
1473 | ++ tmy_acltype2keyword(operation), | |
1474 | ++ buf1->name, buf2->name); | |
1475 | ++ else if (mode == 1 && tmy_quota()) | |
1476 | ++ tmy_add_double_write_acl(operation, | |
1477 | ++ tmy_get_pattern(buf1)->name, | |
1478 | ++ tmy_get_pattern(buf2)->name, | |
1479 | ++ domain, NULL, 0); | |
1480 | ++ | |
1481 | ++out: ; | |
1482 | ++ tmy_free(buf1); | |
1483 | ++ tmy_free(buf2); | |
1484 | ++ if (!is_enforce) | |
1485 | ++ error = 0; | |
1486 | ++ return error; | |
1487 | ++} |
@@ -0,0 +1,65 @@ | ||
1 | +Kconfig and Makefile for TOMOYO Linux. | |
2 | +TOMOYO Linux is placed in security/tomoyo . | |
3 | + | |
4 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
5 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
6 | + security/Kconfig | 1 + | |
7 | + security/Makefile | 1 + | |
8 | + security/tomoyo/Kconfig | 26 ++++++++++++++++++++++++++ | |
9 | + security/tomoyo/Makefile | 2 ++ | |
10 | + 4 files changed, 30 insertions(+) | |
11 | + | |
12 | +--- linux-2.6.23.orig/security/Kconfig 2007-10-10 05:31:38.000000000 +0900 | |
13 | ++++ linux-2.6.23/security/Kconfig 2007-11-01 17:16:15.000000000 +0900 | |
14 | +@@ -94,6 +94,7 @@ config SECURITY_ROOTPLUG | |
15 | + If you are unsure how to answer this question, answer N. | |
16 | + | |
17 | + source security/selinux/Kconfig | |
18 | ++source security/tomoyo/Kconfig | |
19 | + | |
20 | + endmenu | |
21 | + | |
22 | +--- linux-2.6.23.orig/security/Makefile 2007-10-10 05:31:38.000000000 +0900 | |
23 | ++++ linux-2.6.23/security/Makefile 2007-11-01 17:16:15.000000000 +0900 | |
24 | +@@ -10,6 +10,7 @@ ifneq ($(CONFIG_SECURITY),y) | |
25 | + obj-y += commoncap.o | |
26 | + endif | |
27 | + | |
28 | ++obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/ | |
29 | + # Object file lists | |
30 | + obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o | |
31 | + # Must precede capability.o in order to stack properly. | |
32 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
33 | ++++ linux-2.6.23/security/tomoyo/Kconfig 2007-11-12 16:14:20.288619992 +0900 | |
34 | +@@ -0,0 +1,26 @@ | |
35 | ++config SECURITY_TOMOYO | |
36 | ++ bool "TOMOYO Linux support" | |
37 | ++ depends on SECURITY | |
38 | ++ select SECURITY_NETWORK | |
39 | ++ default n | |
40 | ++ help | |
41 | ++ This selects TOMOYO Linux. | |
42 | ++ | |
43 | ++ TOMOYO Linux is a domain-based access control method using LSM. | |
44 | ++ If you answer Y, you will need a policy loader program | |
45 | ++ (/sbin/tomoyo-init) and some configuration files. | |
46 | ++ You can get them from | |
47 | ++ <http://tomoyo.sourceforge.jp/en/2.1.x/> | |
48 | ++ | |
49 | ++ TOMOYO Linux is also applicable to figuring out the behavior | |
50 | ++ of your system, for TOMOYO uses the canonicalized absolute | |
51 | ++ pathnames and TreeView style domain transitions. | |
52 | ++ | |
53 | ++config SECURITY_TOMOYO_USE_AUDITD | |
54 | ++ bool "Use standard auditing subsystem" | |
55 | ++ depends on SECURITY_TOMOYO && AUDIT | |
56 | ++ default n | |
57 | ++ help | |
58 | ++ This makes messages sent to auditing subsystem. | |
59 | ++ | |
60 | ++ If you say 'N' here, messages will be sent to printk(). | |
61 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
62 | ++++ linux-2.6.23/security/tomoyo/Makefile 2007-11-02 13:48:04.000000000 +0900 | |
63 | +@@ -0,0 +1,2 @@ | |
64 | ++obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo.o domain.o common.o realpath.o audit.o file.o exec.o net.o mount.o signal.o capability.o condition.o | |
65 | ++EXTRA_CFLAGS += -Isecurity/tomoyo/include |
@@ -0,0 +1,333 @@ | ||
1 | +Capability access control functions for TOMOYO Linux. | |
2 | +TOMOYO Linux checks permission for non-POSIX capability | |
3 | +so that the number of capabilities won't be limited to 32 or 64. | |
4 | + | |
5 | +Each permission can be automatically accumulated into | |
6 | +the policy of each domain using 'learning mode'. | |
7 | + | |
8 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
9 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
10 | + security/tomoyo/capability.c | 318 +++++++++++++++++++++++++++++++++++++++++++ | |
11 | + 1 file changed, 318 insertions(+) | |
12 | + | |
13 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
14 | ++++ linux-2.6.23/security/tomoyo/capability.c 2007-11-09 17:06:34.000000000 +0900 | |
15 | +@@ -0,0 +1,318 @@ | |
16 | ++/* | |
17 | ++ * security/tomoyo/capability.c | |
18 | ++ * | |
19 | ++ * Capability access control functions for TOMOYO Linux. | |
20 | ++ */ | |
21 | ++ | |
22 | ++#include "tomoyo.h" | |
23 | ++#include "realpath.h" | |
24 | ++ | |
25 | ++static struct { | |
26 | ++ const char *keyword; | |
27 | ++ unsigned int current_value; | |
28 | ++ const char *capability_name; | |
29 | ++} capability_control_array[TMY_MAX_CAPABILITY_INDEX] = { | |
30 | ++ [TMY_INET_STREAM_SOCKET_CREATE] = /* OK */ | |
31 | ++ { "inet_tcp_create", 0, "socket(PF_INET, SOCK_STREAM)" }, | |
32 | ++ [TMY_INET_STREAM_SOCKET_LISTEN] = /* OK */ | |
33 | ++ { "inet_tcp_listen", 0, "listen(PF_INET, SOCK_STREAM)" }, | |
34 | ++ [TMY_INET_STREAM_SOCKET_CONNECT] = /* OK */ | |
35 | ++ { "inet_tcp_connect", 0, "connect(PF_INET, SOCK_STREAM)" }, | |
36 | ++ [TMY_USE_INET_DGRAM_SOCKET] = /* OK */ | |
37 | ++ { "use_inet_udp", 0, "socket(PF_INET, SOCK_DGRAM)" }, | |
38 | ++ [TMY_USE_INET_RAW_SOCKET] = /* OK */ | |
39 | ++ { "use_inet_ip", 0, "socket(PF_INET, SOCK_RAW)" }, | |
40 | ++ [TMY_USE_ROUTE_SOCKET] = /* OK */ | |
41 | ++ { "use_route", 0, "socket(PF_ROUTE)" }, | |
42 | ++ [TMY_USE_PACKET_SOCKET] = /* OK */ | |
43 | ++ { "use_packet", 0, "socket(PF_PACKET)" }, | |
44 | ++ [TMY_SYS_MOUNT] = /* OK */ | |
45 | ++ { "SYS_MOUNT", 0, "sys_mount()" }, | |
46 | ++ [TMY_SYS_UMOUNT] = /* OK */ | |
47 | ++ { "SYS_UMOUNT", 0, "sys_umount()" }, | |
48 | ++ [TMY_SYS_REBOOT] = /* Too many hooks. */ | |
49 | ++ { "SYS_REBOOT", 0, "sys_reboot()" }, | |
50 | ++ [TMY_SYS_CHROOT] = /* OK */ | |
51 | ++ { "SYS_CHROOT", 0, "sys_chroot()" }, | |
52 | ++ [TMY_SYS_KILL] = /* No appropriate hook. */ | |
53 | ++ { "SYS_KILL", 0, "sys_kill()" }, | |
54 | ++ [TMY_SYS_VHANGUP] = /* Too many hooks. */ | |
55 | ++ { "SYS_VHANGUP", 0, "sys_vhangup()" }, | |
56 | ++ [TMY_SYS_SETTIME] = /* Too many hooks. */ | |
57 | ++ { "SYS_TIME", 0, "sys_settimeofday()" }, | |
58 | ++ [TMY_SYS_NICE] = /* No appropriate hook. */ | |
59 | ++ { "SYS_NICE", 0, "sys_nice()" }, | |
60 | ++ [TMY_SYS_SETHOSTNAME] = /* No appropriate hook. */ | |
61 | ++ { "SYS_SETHOSTNAME", 0, "sys_sethostname()" }, | |
62 | ++ [TMY_USE_KERNEL_MODULE] = /* Too many hooks. */ | |
63 | ++ { "use_kernel_module", 0, "kernel_module" }, | |
64 | ++ [TMY_CREATE_FIFO] = /* OK */ | |
65 | ++ { "create_fifo", 0, "mknod(FIFO)" }, | |
66 | ++ [TMY_CREATE_BLOCK_DEV] = /* OK */ | |
67 | ++ { "create_block_dev", 0, "mknod(BDEV)" }, | |
68 | ++ [TMY_CREATE_CHAR_DEV] = /* OK */ | |
69 | ++ { "create_char_dev", 0, "mknod(CDEV)" }, | |
70 | ++ [TMY_CREATE_UNIX_SOCKET] = /* OK */ | |
71 | ++ { "create_unix_socket", 0, "mknod(SOCKET)" }, | |
72 | ++ [TMY_SYS_LINK] = /* OK */ | |
73 | ++ { "SYS_LINK", 0, "sys_link()" }, | |
74 | ++ [TMY_SYS_SYMLINK] = /* OK */ | |
75 | ++ { "SYS_SYMLINK", 0, "sys_symlink()" }, | |
76 | ++ [TMY_SYS_RENAME] = /* OK */ | |
77 | ++ { "SYS_RENAME", 0, "sys_rename()" }, | |
78 | ++ [TMY_SYS_UNLINK] = /* OK */ | |
79 | ++ { "SYS_UNLINK", 0, "sys_unlink()" }, | |
80 | ++ [TMY_SYS_CHMOD] = /* OK */ | |
81 | ++ { "SYS_CHMOD", 0, "sys_chmod()" }, | |
82 | ++ [TMY_SYS_CHOWN] = /* OK */ | |
83 | ++ { "SYS_CHOWN", 0, "sys_chown()" }, | |
84 | ++ [TMY_SYS_IOCTL] = /* Too many hooks. */ | |
85 | ++ { "SYS_IOCTL", 0, "sys_ioctl()" }, | |
86 | ++ [TMY_SYS_KEXEC_LOAD] = /* No appropriate hook. */ | |
87 | ++ { "SYS_KEXEC_LOAD", 0, "sys_kexec_load()" }, | |
88 | ++ [TMY_SYS_PIVOT_ROOT] = /* OK */ | |
89 | ++ { "SYS_PIVOT_ROOT", 0, "sys_pivot_root()" }, | |
90 | ++}; | |
91 | ++ | |
92 | ++struct profile { | |
93 | ++ unsigned char value[TMY_MAX_CAPABILITY_INDEX]; | |
94 | ++}; | |
95 | ++ | |
96 | ++static struct profile *profile_ptr[TMY_MAX_PROFILES]; | |
97 | ++ | |
98 | ++/************************* UTILITY FUNCTIONS *************************/ | |
99 | ++ | |
100 | ++const char *tmy_capability2keyword(const unsigned int capability) | |
101 | ++{ | |
102 | ++ return capability < TMY_MAX_CAPABILITY_INDEX ? | |
103 | ++ capability_control_array[capability].keyword : NULL; | |
104 | ++} | |
105 | ++ | |
106 | ++static const char *tmy_capability2name(const unsigned int capability) | |
107 | ++{ | |
108 | ++ return capability < TMY_MAX_CAPABILITY_INDEX ? | |
109 | ++ capability_control_array[capability].capability_name : NULL; | |
110 | ++} | |
111 | ++ | |
112 | ++/* Check whether the given capability control is enabled. */ | |
113 | ++static unsigned int tmy_capability_flags(const unsigned int index) | |
114 | ++{ | |
115 | ++ const u8 profile = TMY_SECURITY->domain->profile; | |
116 | ++ /* All operations might sleep. See tmy_supervisor(). */ | |
117 | ++ might_sleep(); | |
118 | ++ if (in_interrupt()) | |
119 | ++ return 0; | |
120 | ++ return sbin_init_started && index < TMY_MAX_CAPABILITY_INDEX | |
121 | ++#if TMY_MAX_PROFILES != 256 | |
122 | ++ && profile < TMY_MAX_PROFILES | |
123 | ++#endif | |
124 | ++ && profile_ptr[profile] ? | |
125 | ++ profile_ptr[profile]->value[index] : 0; | |
126 | ++} | |
127 | ++ | |
128 | ++static struct profile *tmy_new_capability_profile(const unsigned int profile) | |
129 | ++{ | |
130 | ++ static DEFINE_MUTEX(mutex); | |
131 | ++ struct profile *ptr; | |
132 | ++ int i; | |
133 | ++ if (profile >= TMY_MAX_PROFILES) | |
134 | ++ return NULL; | |
135 | ++ mutex_lock(&mutex); | |
136 | ++ ptr = profile_ptr[profile]; | |
137 | ++ if (ptr) | |
138 | ++ goto ok; | |
139 | ++ ptr = tmy_alloc_element(sizeof(*ptr)); | |
140 | ++ if (!ptr) | |
141 | ++ goto ok; | |
142 | ++ for (i = 0; i < TMY_MAX_CAPABILITY_INDEX; i++) | |
143 | ++ ptr->value[i] = capability_control_array[i].current_value; | |
144 | ++ mb(); /* Avoid out-of-order execution. */ | |
145 | ++ profile_ptr[profile] = ptr; | |
146 | ++ok: ; | |
147 | ++ mutex_unlock(&mutex); | |
148 | ++ return ptr; | |
149 | ++} | |
150 | ++ | |
151 | ++int tmy_set_capability_profile(const char *data, unsigned int value, | |
152 | ++ const unsigned int profile) | |
153 | ++{ | |
154 | ++ int i; | |
155 | ++ struct profile *ptr; | |
156 | ++ ptr = tmy_new_capability_profile(profile); | |
157 | ++ if (!ptr) | |
158 | ++ return -EINVAL; | |
159 | ++ for (i = 0; i < TMY_MAX_CAPABILITY_INDEX; i++) { | |
160 | ++ if (strcmp(data, capability_control_array[i].keyword)) continue; | |
161 | ++ if (value > 3) value = 3; | |
162 | ++ ptr->value[i] = value; | |
163 | ++ return 0; | |
164 | ++ } | |
165 | ++ return -EINVAL; | |
166 | ++} | |
167 | ++ | |
168 | ++int tmy_read_capability_profile(struct io_buffer *head) | |
169 | ++{ | |
170 | ++ int step; | |
171 | ++ for (step = head->read_step; | |
172 | ++ step < TMY_MAX_PROFILES * TMY_MAX_CAPABILITY_INDEX; step++) { | |
173 | ++ const int i = step / TMY_MAX_CAPABILITY_INDEX; | |
174 | ++ const int j = step % TMY_MAX_CAPABILITY_INDEX; | |
175 | ++ const struct profile *profile = profile_ptr[i]; | |
176 | ++ head->read_step = step; | |
177 | ++ if (!profile) | |
178 | ++ continue; | |
179 | ++ if (tmy_io_printf(head, "%u-" TMY_MAC_FOR_CAPABILITY "%s=%u\n", | |
180 | ++ i, capability_control_array[j].keyword, | |
181 | ++ profile->value[j])) | |
182 | ++ break; | |
183 | ++ } | |
184 | ++ return step < TMY_MAX_PROFILES * TMY_MAX_CAPABILITY_INDEX ? -ENOMEM : 0; | |
185 | ++} | |
186 | ++ | |
187 | ++/************************* AUDIT FUNCTIONS *************************/ | |
188 | ++ | |
189 | ++static int tmy_audit_capability_log(const unsigned int capability, | |
190 | ++ const bool is_granted, | |
191 | ++ const u8 profile, const unsigned int mode) | |
192 | ++{ | |
193 | ++ char *buf; | |
194 | ++ int len = 64 ; | |
195 | ++ | |
196 | ++ if (is_granted) { | |
197 | ++ if (!tmy_audit_grant()) | |
198 | ++ return 0; | |
199 | ++ } else { | |
200 | ++ if (!tmy_audit_reject()) | |
201 | ++ return 0; | |
202 | ++ } | |
203 | ++ | |
204 | ++ buf = tmy_init_audit_log(&len, profile, mode); | |
205 | ++ | |
206 | ++ if (!buf) | |
207 | ++ return -ENOMEM; | |
208 | ++ | |
209 | ++ snprintf(buf + strlen(buf), | |
210 | ++ len - strlen(buf) - 1, | |
211 | ++ TMY_ALLOW_CAPABILITY "%s\n", | |
212 | ++ tmy_capability2keyword(capability)); | |
213 | ++ | |
214 | ++ return tmy_write_audit_log(buf, is_granted); | |
215 | ++} | |
216 | ++ | |
217 | ++/************************* CAPABILITY ACL HANDLER *************************/ | |
218 | ++ | |
219 | ++static int tmy_add_capability_acl(const unsigned int capability, | |
220 | ++ struct domain_info *domain, | |
221 | ++ const struct condition_list *condition, | |
222 | ++ const bool is_delete) | |
223 | ++{ | |
224 | ++ struct acl_info *ptr; | |
225 | ++ struct capability_acl *acl; | |
226 | ++ int error = -ENOMEM; | |
227 | ++ const u16 hash = capability; | |
228 | ++ if (!domain) | |
229 | ++ return -EINVAL; | |
230 | ++ mutex_lock(&domain_acl_lock); | |
231 | ++ if (is_delete) | |
232 | ++ goto remove; | |
233 | ++ | |
234 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
235 | ++ acl = (struct capability_acl *) ptr; | |
236 | ++ if (ptr->type == TMY_TYPE_CAPABILITY_ACL | |
237 | ++ && acl->capability == hash | |
238 | ++ && ptr->cond == condition) { | |
239 | ++ ptr->is_deleted = 0; | |
240 | ++ /* Found. Nothing to do. */ | |
241 | ++ error = 0; | |
242 | ++ tmy_update_counter(TMY_UPDATE_DOMAINPOLICY); | |
243 | ++ goto ok; | |
244 | ++ } | |
245 | ++ } | |
246 | ++ /* Not found. Append it to the tail. */ | |
247 | ++ acl = tmy_alloc_element(sizeof(*acl)); | |
248 | ++ if (!acl) | |
249 | ++ goto ok; | |
250 | ++ acl->head.type = TMY_TYPE_CAPABILITY_ACL; | |
251 | ++ acl->head.cond = condition; | |
252 | ++ acl->capability = hash; | |
253 | ++ error = tmy_add_acl(domain, (struct acl_info *) acl); | |
254 | ++ goto ok; | |
255 | ++remove: ; | |
256 | ++ error = -ENOENT; | |
257 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
258 | ++ acl = (struct capability_acl *) ptr; | |
259 | ++ if (ptr->type != TMY_TYPE_CAPABILITY_ACL || ptr->is_deleted || | |
260 | ++ acl->capability != hash || ptr->cond != condition) continue; | |
261 | ++ error = tmy_del_acl(ptr); | |
262 | ++ break; | |
263 | ++ } | |
264 | ++ok: ; | |
265 | ++ mutex_unlock(&domain_acl_lock); | |
266 | ++ return error; | |
267 | ++} | |
268 | ++ | |
269 | ++/** | |
270 | ++ * tmy_capable - check permission for capability. | |
271 | ++ * @capability: capability index. | |
272 | ++ * | |
273 | ++ * Returns zero if permission granted. | |
274 | ++ * Returns nonzero if permission denied. | |
275 | ++ */ | |
276 | ++int tmy_capable(const unsigned int capability) | |
277 | ++{ | |
278 | ++ struct domain_info * const domain = TMY_SECURITY->domain; | |
279 | ++ struct acl_info *ptr; | |
280 | ++ const u8 profile = domain->profile; | |
281 | ++ const unsigned int mode = tmy_capability_flags(capability); | |
282 | ++ const bool is_enforce = (mode == 3); | |
283 | ++ const u16 hash = capability; | |
284 | ++ if (!mode) | |
285 | ++ return 0; | |
286 | ++ list_for_each_entry(ptr, &domain->acl_info_list, list) { | |
287 | ++ struct capability_acl *acl = (struct capability_acl *) ptr; | |
288 | ++ if (ptr->type != TMY_TYPE_CAPABILITY_ACL || ptr->is_deleted | |
289 | ++ || acl->capability != hash | |
290 | ++ || tmy_check_condition(ptr->cond, NULL)) | |
291 | ++ continue; | |
292 | ++ tmy_audit_capability_log(capability, 1, profile, mode); | |
293 | ++ return 0; | |
294 | ++ } | |
295 | ++ if (tmy_flags(TMY_VERBOSE)) | |
296 | ++ tmy_audit("TOMOYO-%s: %s denied for %s\n", | |
297 | ++ tmy_getmsg(is_enforce), | |
298 | ++ tmy_capability2name(capability), | |
299 | ++ tmy_lastname(domain)); | |
300 | ++ tmy_audit_capability_log(capability, 0, profile, mode); | |
301 | ++ if (is_enforce) | |
302 | ++ return tmy_supervisor("%s\n" TMY_ALLOW_CAPABILITY "%s\n", | |
303 | ++ domain->domainname->name, | |
304 | ++ tmy_capability2keyword(capability)); | |
305 | ++ if (mode == 1 && tmy_quota()) | |
306 | ++ tmy_add_capability_acl(capability, domain, NULL, 0); | |
307 | ++ return 0; | |
308 | ++} | |
309 | ++ | |
310 | ++/** | |
311 | ++ * tmy_add_capability_policy - add or delete capability policy. | |
312 | ++ * @data: a line to parse. | |
313 | ++ * @domain: pointer to "struct domain_info". | |
314 | ++ * @cond: pointer to "struct condition_list". May be NULL. | |
315 | ++ * @is_delete: is this delete request? | |
316 | ++ * | |
317 | ++ * Returns zero on success. | |
318 | ++ * Returns nonzero on failure. | |
319 | ++ */ | |
320 | ++int tmy_add_capability_policy(char *data, struct domain_info *domain, | |
321 | ++ const struct condition_list *cond, | |
322 | ++ const bool is_delete) | |
323 | ++{ | |
324 | ++ unsigned int capability; | |
325 | ++ for (capability = 0; capability < TMY_MAX_CAPABILITY_INDEX; | |
326 | ++ capability++) { | |
327 | ++ if (strcmp(data, capability_control_array[capability].keyword)) | |
328 | ++ continue; | |
329 | ++ return tmy_add_capability_acl(capability, domain, | |
330 | ++ cond, is_delete); | |
331 | ++ } | |
332 | ++ return -EINVAL; | |
333 | ++} |
@@ -0,0 +1,973 @@ | ||
1 | +LSM wrapper functions for TOMOYO Linux's access control. | |
2 | + | |
3 | +********** IMPORTANT WARNING : KNOWN BUGS ********** | |
4 | +According to lockdep, there is a possibility of deadlock when TOMOYO tries to | |
5 | +acquire namespace_sem for reading if an inode's mutex is already held. | |
6 | + | |
7 | +There is probably only one clean solution that can avoid this deadlock: | |
8 | +Pass "struct vfsmount" to LSM hooks as AppArmor proposes | |
9 | +so that TOMOYO needn't to walk through process's namespace list. | |
10 | + | |
11 | +See http://lkml.org/lkml/2007/11/5/388 and http://lkml.org/lkml/2007/11/7/292 | |
12 | +for details. | |
13 | + | |
14 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
15 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
16 | + security/tomoyo/tomoyo.c | 952 +++++++++++++++++++++++++++++++++++++++++++++++ | |
17 | + 1 file changed, 952 insertions(+) | |
18 | + | |
19 | +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 | |
20 | ++++ linux-2.6.23/security/tomoyo/tomoyo.c 2007-11-05 16:55:38.000000000 +0900 | |
21 | +@@ -0,0 +1,952 @@ | |
22 | ++/* | |
23 | ++ * security/tomoyo/tomoyo.c | |
24 | ++ * | |
25 | ++ * LSM hooks for TOMOYO Linux. | |
26 | ++ */ | |
27 | ++ | |
28 | ++#include "tomoyo.h" | |
29 | ++#include "realpath.h" | |
30 | ++ | |
31 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) | |
32 | ++static inline struct iphdr *ip_hdr(const struct sk_buff *skb) | |
33 | ++{ | |
34 | ++ return skb->nh.iph; | |
35 | ++} | |
36 | ++static inline struct udphdr *udp_hdr(const struct sk_buff *skb) | |
37 | ++{ | |
38 | ++ return skb->h.uh; | |
39 | ++} | |
40 | ++static inline struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb) | |
41 | ++{ | |
42 | ++ return skb->nh.ipv6h; | |
43 | ++} | |
44 | ++#endif | |
45 | ++ | |
46 | ++#define MAX_SOCK_ADDR 128 /* net/socket.c */ | |
47 | ++ | |
48 | ++LIST_HEAD(domain_list); | |
49 | ++ | |
50 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) | |
51 | ++static struct kmem_cache *tmy_cachep; | |
52 | ++#else | |
53 | ++static kmem_cache_t *tmy_cachep; | |
54 | ++#endif | |
55 | ++ | |
56 | ++static int tmy_task_alloc_security(struct task_struct *p) | |
57 | ++{ | |
58 | ++ struct tmy_security *ptr = kmem_cache_alloc(tmy_cachep, GFP_KERNEL); | |
59 | ++ | |
60 | ++ if (!ptr) | |
61 | ++ return -ENOMEM; | |
62 | ++ memcpy(ptr, TMY_SECURITY, sizeof(*ptr)); | |
63 | ++ p->security = ptr; | |
64 | ++ return 0; | |
65 | ++} | |
66 | ++ | |
67 | ++static void tmy_task_free_security(struct task_struct *p) | |
68 | ++{ | |
69 | ++ kmem_cache_free(tmy_cachep, p->security); | |
70 | ++} | |
71 | ++ | |
72 | ++static int tmy_bprm_alloc_security(struct linux_binprm *bprm) | |
73 | ++{ | |
74 | ++ TMY_SECURITY->prev_domain = TMY_SECURITY->domain; | |
75 | ++ return 0; | |
76 | ++} | |
77 | ++ | |
78 | ++static int tmy_bprm_check_security(struct linux_binprm *bprm) | |
79 | ++{ | |
80 | ++ struct domain_info *next_domain = NULL; | |
81 | ++ int retval = 0; | |
82 | ++ | |
83 | ++ tmy_load_policy(bprm->filename); | |
84 | ++ | |
85 | ++ /* | |
86 | ++ * TMY_CHECK_READ_FOR_OPEN_EXEC bit indicates whether this function is | |
87 | ++ * called by do_execve() or not. | |
88 | ++ * If called by do_execve(), I do domain transition. | |
89 | ++ */ | |
90 | ++ if (!(TMY_SECURITY->flags | |
91 | ++ & TMY_CHECK_READ_FOR_OPEN_EXEC)) { | |
92 | ++ retval = tmy_find_next_domain(bprm, &next_domain); | |
93 | ++ if (retval == 0) { | |
94 | ++ TMY_SECURITY->domain = next_domain; | |
95 | ++ TMY_SECURITY->flags |= | |
96 | ++ TMY_CHECK_READ_FOR_OPEN_EXEC; | |
97 | ++ } | |
98 | ++ } | |
99 | ++ | |
100 | ++ return retval; | |
101 | ++} | |
102 | ++ | |
103 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) | |
104 | ++static void tomoyo_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) | |
105 | ++{ | |
106 | ++ TMY_SECURITY->prev_domain = TMY_SECURITY->domain; | |
107 | ++} | |
108 | ++#else | |
109 | ++static void tmy_bprm_post_apply_creds(struct linux_binprm *bprm) | |
110 | ++{ | |
111 | ++ TMY_SECURITY->prev_domain = TMY_SECURITY->domain; | |
112 | ++} | |
113 | ++#endif | |
114 | ++ | |
115 | ++static void tmy_bprm_free_security(struct linux_binprm *bprm) | |
116 | ++{ | |
117 | ++ TMY_SECURITY->domain = TMY_SECURITY->prev_domain; | |
118 | ++ TMY_SECURITY->flags &= ~TMY_CHECK_READ_FOR_OPEN_EXEC; | |
119 | ++} | |
120 | ++ | |
121 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) | |
122 | ++static int tmy_sysctl(struct ctl_table *table, int op) | |
123 | ++{ | |
124 | ++ int error; | |
125 | ++ char *name; | |
126 | ++ | |
127 | ++ if ((op & 6) == 0) | |
128 | ++ return 0; | |
129 | ++ | |
130 | ++ name = sysctlpath_from_table(table); | |
131 | ++ if (!name) | |
132 | ++ return -ENOMEM; | |
133 | ++ | |
134 | ++ error = tmy_file_perm(name, op & 6, "sysctl"); | |
135 | ++ tmy_free(name); | |
136 | ++ | |
137 | ++ return error; | |
138 | ++} | |
139 | ++#endif | |
140 | ++ | |
141 | ++static int tmy_inode_permission(struct inode *inode, | |
142 | ++ int mask, | |
143 | ++ struct nameidata *nd) | |
144 | ++{ | |
145 | ++ int flag = 0; | |
146 | ++ | |
147 | ++ if (S_ISDIR(inode->i_mode)) /* ignore because inode is directory */ | |
148 | ++ return 0; | |
149 | ++ if (!nd || !nd->dentry || !nd->mnt) | |
150 | ++ return 0; | |
151 | ++ /* | |
152 | ++ * If called by other than do_execve(), I check for read permission of | |
153 | ++ * interpreter. | |
154 | ++ * Unlike DAC, I don't check for read permission of pathname passed to | |
155 | ++ * do_execve(). | |
156 | ++ * TOMOYO Linux checks for program's execute permission and | |
157 | ++ * interpreter's read permission. | |
158 | ++ */ | |
159 | ++ if ((mask == MAY_EXEC) && | |
160 | ++ (TMY_SECURITY->flags & TMY_CHECK_READ_FOR_OPEN_EXEC)) | |
161 | ++ mask = MAY_READ; | |
162 | ++ if ((mask == MAY_EXEC) || (mask == 0)) | |
163 | ++ return 0; | |
164 | ++ | |
165 | ++ if (mask == (MAY_READ | MAY_EXEC)) | |
166 | ++ flag |= O_RDONLY + 1; | |
167 | ++ else { | |
168 | ++ if (mask & MAY_READ) | |
169 | ++ flag |= O_RDONLY + 1; | |
170 | ++ if (mask & MAY_WRITE) | |
171 | ++ flag |= O_WRONLY + 1; | |
172 | ++ if ((mask & MAY_APPEND)) | |
173 | ++ flag |= O_APPEND; | |
174 | ++ } | |
175 | ++ | |
176 | ++ return tmy_open_perm(nd->dentry, nd->mnt, flag); | |
177 | ++} | |
178 | ++ | |
179 | ++static int tmy_do_single_write_perm(int operation, struct dentry *dentry) | |
180 | ++{ | |
181 | ++ int err = 0; | |
182 | ++ struct task_struct *task = current; | |
183 | ++ struct seq_file m; | |
184 | ++ loff_t pos = 0; | |
185 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
186 | ++ struct namespace *namespace = NULL; | |
187 | ++#else | |
188 | ++ struct mnt_namespace *namespace = NULL; | |
189 | ++#endif | |
190 | ++ if (!dentry) | |
191 | ++ return 0; | |
192 | ++ if (!sbin_init_started) | |
193 | ++ return 0; | |
194 | ++ memset(&m, 0, sizeof(m)); | |
195 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
196 | ++ namespace = task->namespace; | |
197 | ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
198 | ++ if (task->nsproxy) | |
199 | ++ namespace = task->nsproxy->namespace; | |
200 | ++#else | |
201 | ++ if (task->nsproxy) | |
202 | ++ namespace = task->nsproxy->mnt_ns; | |
203 | ++#endif | |
204 | ++ if (!namespace) | |
205 | ++ return 0; | |
206 | ++ m.private = namespace; | |
207 | ++ while (!err) { | |
208 | ++ struct vfsmount *mnt; | |
209 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) | |
210 | ++ mnt = mounts_op.start(&m, &pos); | |
211 | ++ if (!mnt) { | |
212 | ++ mounts_op.stop(&m, NULL); | |
213 | ++ break; | |
214 | ++ } | |
215 | ++#else | |
216 | ++ struct list_head *p; | |
217 | ++ p = mounts_op.start(&m, &pos); | |
218 | ++ if (!p) { | |
219 | ++ mounts_op.stop(&m, NULL); | |
220 | ++ break; | |
221 | ++ } | |
222 | ++ mnt = list_entry(p, struct vfsmount, mnt_list); | |
223 | ++#endif | |
224 | ++ mntget(mnt); | |
225 | ++ mounts_op.stop(&m, NULL); | |
226 | ++ pos++; | |
227 | ++ if (mnt->mnt_root->d_sb == dentry->d_sb) | |
228 | ++ err = tmy_single_write_perm(operation, dentry, mnt); | |
229 | ++ mntput(mnt); | |
230 | ++ } | |
231 | ++ return err; | |
232 | ++} | |
233 | ++ | |
234 | ++static int tmy_inode_setattr(struct dentry *dentry, struct iattr *iattr) | |
235 | ++{ | |
236 | ++ int err = 0; | |
237 | ++ unsigned int ia_valid = iattr->ia_valid; | |
238 | ++ if (ia_valid & ATTR_MODE) | |
239 | ++ err = tmy_capable(TMY_SYS_CHMOD); | |
240 | ++ if (!err && (ia_valid & (ATTR_UID | ATTR_GID))) | |
241 | ++ err = tmy_capable(TMY_SYS_CHOWN); | |
242 | ++ if (err) | |
243 | ++ return err; | |
244 | ++ if (ia_valid & ATTR_SIZE) | |
245 | ++ return tmy_do_single_write_perm(TMY_TYPE_TRUNCATE_ACL, dentry); | |
246 | ++ return 0; | |
247 | ++} | |
248 | ++ | |
249 | ++static int tmy_inode_create(struct inode *dir, struct dentry *dentry, int mode) | |
250 | ++{ | |
251 | ++ return tmy_do_single_write_perm(TMY_TYPE_CREATE_ACL, dentry); | |
252 | ++} | |
253 | ++ | |
254 | ++static int tmy_inode_unlink(struct inode *dir, struct dentry *dentry) | |
255 | ++{ | |
256 | ++ const int err = tmy_capable(TMY_SYS_UNLINK); | |
257 | ++ if (err) | |
258 | ++ return err; | |
259 | ++ return tmy_do_single_write_perm(TMY_TYPE_UNLINK_ACL, dentry); | |
260 | ++} | |
261 | ++ | |
262 | ++static int tmy_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode) | |
263 | ++{ | |
264 | ++ return tmy_do_single_write_perm(TMY_TYPE_MKDIR_ACL, dentry); | |
265 | ++} | |
266 | ++ | |
267 | ++static int tmy_inode_rmdir(struct inode *dir, struct dentry *dentry) | |
268 | ++{ | |
269 | ++ return tmy_do_single_write_perm(TMY_TYPE_RMDIR_ACL, dentry); | |
270 | ++} | |
271 | ++ | |
272 | ++static int tmy_inode_symlink(struct inode *dir, | |
273 | ++ struct dentry *dentry, | |
274 | ++ const char *old_name) | |
275 | ++{ | |
276 | ++ const int err = tmy_capable(TMY_SYS_SYMLINK); | |
277 | ++ if (err) | |
278 | ++ return err; | |
279 | ++ return tmy_do_single_write_perm(TMY_TYPE_SYMLINK_ACL, dentry); | |
280 | ++} | |
281 | ++ | |
282 | ++static int tmy_inode_mknod(struct inode *inode, | |
283 | ++ struct dentry *dentry, | |
284 | ++ int mode, | |
285 | ++ dev_t dev) | |
286 | ++{ | |
287 | ++ int err; | |
288 | ++ if (S_ISCHR(mode)) { | |
289 | ++ err = tmy_capable(TMY_CREATE_CHAR_DEV); | |
290 | ++ if (err) | |
291 | ++ return err; | |
292 | ++ return tmy_do_single_write_perm(TMY_TYPE_MKCHAR_ACL, dentry); | |
293 | ++ } | |
294 | ++ if (S_ISBLK(mode)) { | |
295 | ++ err = tmy_capable(TMY_CREATE_BLOCK_DEV); | |
296 | ++ if (err) | |
297 | ++ return err; | |
298 | ++ return tmy_do_single_write_perm(TMY_TYPE_MKBLOCK_ACL, dentry); | |
299 | ++ } | |
300 | ++ if (S_ISFIFO(mode)) { | |
301 | ++ err = tmy_capable(TMY_CREATE_FIFO); | |
302 | ++ if (err) | |
303 | ++ return err; | |
304 | ++ return tmy_do_single_write_perm(TMY_TYPE_MKFIFO_ACL, dentry); | |
305 | ++ } | |
306 | ++ if (S_ISSOCK(mode)) { | |
307 | ++ err = tmy_capable(TMY_CREATE_UNIX_SOCKET); | |
308 | ++ if (err) | |
309 | ++ return err; | |
310 | ++ return tmy_do_single_write_perm(TMY_TYPE_MKSOCK_ACL, dentry); | |
311 | ++ } | |
312 | ++ | |
313 | ++ return 0; | |
314 | ++} | |
315 | ++ | |
316 | ++static int tmy_do_double_write_perm(int operation, | |
317 | ++ struct dentry *old_dentry, | |
318 | ++ struct dentry *new_dentry) | |
319 | ++{ | |
320 | ++ int err = 0; | |
321 | ++ struct task_struct *task = current; | |
322 | ++ struct seq_file m; | |
323 | ++ loff_t pos = 0; | |
324 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
325 | ++ struct namespace *namespace = NULL; | |
326 | ++#else | |
327 | ++ struct mnt_namespace *namespace = NULL; | |
328 | ++#endif | |
329 | ++ if (!old_dentry || !new_dentry) | |
330 | ++ return 0; | |
331 | ++ if (!sbin_init_started) | |
332 | ++ return 0; | |
333 | ++ memset(&m, 0, sizeof(m)); | |
334 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) | |
335 | ++ namespace = task->namespace; | |
336 | ++#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
337 | ++ if (task->nsproxy) | |
338 | ++ namespace = task->nsproxy->namespace; | |
339 | ++#else | |
340 | ++ if (task->nsproxy) | |
341 | ++ namespace = task->nsproxy->mnt_ns; | |
342 | ++#endif | |
343 | ++ if (!namespace) | |
344 | ++ return 0; | |
345 | ++ m.private = namespace; | |
346 | ++ while (!err) { | |
347 | ++ struct vfsmount *mnt; | |
348 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) | |
349 | ++ mnt = mounts_op.start(&m, &pos); | |
350 | ++ if (!mnt) { | |
351 | ++ mounts_op.stop(&m, NULL); | |
352 | ++ break; | |
353 | ++ } | |
354 | ++#else | |
355 | ++ struct list_head *p; | |
356 | ++ p = mounts_op.start(&m, &pos); | |
357 | ++ if (!p) { | |
358 | ++ mounts_op.stop(&m, NULL); | |
359 | ++ break; | |
360 | ++ } | |
361 | ++ mnt = list_entry(p, struct vfsmount, mnt_list); | |
362 | ++#endif | |
363 | ++ mntget(mnt); | |
364 | ++ mounts_op.stop(&m, NULL); | |
365 | ++ pos++; | |
366 | ++ if (mnt->mnt_root->d_sb == old_dentry->d_sb) | |
367 | ++ err = tmy_double_write_perm(operation, old_dentry, mnt, | |
368 | ++ new_dentry, mnt); | |
369 | ++ mntput(mnt); | |
370 | ++ } | |
371 | ++ return err; | |
372 | ++} | |
373 | ++ | |
374 | ++static int tmy_inode_link(struct dentry *old_dentry, | |
375 | ++ struct inode *inode, | |
376 | ++ struct dentry *new_dentry) | |
377 | ++{ | |
378 | ++ const int err = tmy_capable(TMY_SYS_LINK); | |
379 | ++ if (err) | |
380 | ++ return err; | |
381 | ++ return tmy_do_double_write_perm(TMY_TYPE_LINK_ACL, | |
382 | ++ old_dentry, new_dentry); | |
383 | ++} | |
384 | ++ | |
385 | ++static int tmy_inode_rename(struct inode *old_inode, | |
386 | ++ struct dentry *old_dentry, | |
387 | ++ struct inode *new_inode, | |
388 | ++ struct dentry *new_dentry) | |
389 | ++{ | |
390 | ++ const int err = tmy_capable(TMY_SYS_RENAME); | |
391 | ++ if (err) | |
392 | ++ return err; | |
393 | ++ return tmy_do_double_write_perm(TMY_TYPE_RENAME_ACL, | |
394 | ++ old_dentry, | |
395 | ++ new_dentry); | |
396 | ++} | |
397 | ++ | |
398 | ++static int tmy_file_fcntl(struct file *file, | |
399 | ++ unsigned int cmd, | |
400 | ++ unsigned long arg) | |
401 | ++{ | |
402 | ++ if (!(arg & O_APPEND)) | |
403 | ++ return tmy_rewrite_perm(file); | |
404 | ++ return 0; | |
405 | ++} | |
406 | ++ | |
407 | ++static int tmy_file_ioctl(struct file *file, unsigned int cmd, | |
408 | ++ unsigned long arg) | |
409 | ++{ | |
410 | ++ switch (cmd) { | |
411 | ++ case FIOCLEX: | |
412 | ++ case FIONCLEX: | |
413 | ++ case FIONBIO: | |
414 | ++ case FIOASYNC: | |
415 | ++ case FIOQSIZE: | |
416 | ++ return 0; | |
417 | ++ case FIBMAP: | |
418 | ++ case FIGETBSZ: | |
419 | ++ case FIONREAD: | |
420 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) | |
421 | ++ if (S_ISREG(file->f_dentry->d_inode->i_mode)) | |
422 | ++ return 0; | |
423 | ++#else | |
424 | ++ if (S_ISREG(file->f_path.dentry->d_inode->i_mode)) | |
425 | ++ return 0; | |
426 | ++#endif | |
427 | ++ } | |
428 | ++ return tmy_capable(TMY_SYS_IOCTL); | |
429 | ++} | |
430 | ++ | |
431 | ++static int tmy_socket_listen(struct socket *sock, int backlog) | |
432 | ++{ | |
433 | ++ char addr[MAX_SOCK_ADDR]; | |
434 | ++ int addr_len; | |
435 | ++ int error; | |
436 | ++ | |
437 | ++ /* I don't check if called by kernel process. */ | |
438 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
439 | ++ return 0; | |
440 | ++ | |
441 | ++ if (sock->type != SOCK_STREAM) | |
442 | ++ return 0; | |
443 | ++ if (sock->sk->sk_family != PF_INET && sock->sk->sk_family != PF_INET6) | |
444 | ++ return 0; | |
445 | ++ | |
446 | ++ error = tmy_capable(TMY_INET_STREAM_SOCKET_LISTEN); | |
447 | ++ if (error) | |
448 | ++ return error; | |
449 | ++ | |
450 | ++ if (sock->ops->getname(sock, (struct sockaddr *) addr, &addr_len, 0)) | |
451 | ++ return -EPERM; | |
452 | ++ | |
453 | ++ switch (((struct sockaddr *) addr)->sa_family) { | |
454 | ++ struct sockaddr_in6 *in6; | |
455 | ++ struct sockaddr_in *in; | |
456 | ++ | |
457 | ++ case AF_INET6: | |
458 | ++ in6 = (struct sockaddr_in6 *) addr; | |
459 | ++ error = tmy_network_listen_acl(1, in6->sin6_addr.s6_addr, | |
460 | ++ in6->sin6_port); | |
461 | ++ break; | |
462 | ++ case AF_INET: | |
463 | ++ in = (struct sockaddr_in *) addr; | |
464 | ++ error = tmy_network_listen_acl(0, (u8 *) &in->sin_addr, | |
465 | ++ in->sin_port); | |
466 | ++ break; | |
467 | ++ } | |
468 | ++ | |
469 | ++ return error; | |
470 | ++} | |
471 | ++ | |
472 | ++static int tmy_socket_create(int family, int type, int protocol, int kern) | |
473 | ++{ | |
474 | ++ int error = 0; | |
475 | ++ if (kern) | |
476 | ++ return 0; | |
477 | ++ if (family == PF_INET || family == PF_INET6) { | |
478 | ++ switch (type) { | |
479 | ++ case SOCK_STREAM: | |
480 | ++ return tmy_capable(TMY_INET_STREAM_SOCKET_CREATE); | |
481 | ++ break; | |
482 | ++ case SOCK_DGRAM: | |
483 | ++ return tmy_capable(TMY_USE_INET_DGRAM_SOCKET); | |
484 | ++ break; | |
485 | ++ case SOCK_RAW: | |
486 | ++ return tmy_capable(TMY_USE_INET_RAW_SOCKET); | |
487 | ++ break; | |
488 | ++ } | |
489 | ++ } else if (family == PF_PACKET) { | |
490 | ++ return tmy_capable(TMY_USE_PACKET_SOCKET); | |
491 | ++ } else if (family == PF_ROUTE) { | |
492 | ++ return tmy_capable(TMY_USE_ROUTE_SOCKET); | |
493 | ++ } | |
494 | ++ return error; | |
495 | ++} | |
496 | ++ | |
497 | ++static int tmy_socket_connect(struct socket *sock, | |
498 | ++ struct sockaddr *addr, | |
499 | ++ int addr_len0) | |
500 | ++{ | |
501 | ++ unsigned int addr_len = (unsigned int) addr_len0; | |
502 | ++ int error = 0; | |
503 | ++ const unsigned int type = sock->type; | |
504 | ++ | |
505 | ++ /* I don't check if called by kernel process. */ | |
506 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
507 | ++ return 0; | |
508 | ++ | |
509 | ++ if (type == SOCK_STREAM) { | |
510 | ++ error = tmy_capable(TMY_INET_STREAM_SOCKET_CONNECT); | |
511 | ++ if (error) | |
512 | ++ return error; | |
513 | ++ } | |
514 | ++ | |
515 | ++ if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) | |
516 | ++ return 0; | |
517 | ++ | |
518 | ++ switch (addr->sa_family) { | |
519 | ++ struct sockaddr_in6 *in6; | |
520 | ++ struct sockaddr_in *in; | |
521 | ++ | |
522 | ++ case AF_INET6: | |
523 | ++ if (addr_len < SIN6_LEN_RFC2133) | |
524 | ++ break; | |
525 | ++ | |
526 | ++ in6 = (struct sockaddr_in6 *) addr; | |
527 | ++ if (type != SOCK_RAW) | |
528 | ++ error = tmy_network_connect_acl(1, type, | |
529 | ++ in6->sin6_addr.s6_addr, | |
530 | ++ in6->sin6_port); | |
531 | ++ else { | |
532 | ++ const u16 port = htons(sock->sk->sk_protocol); | |
533 | ++ | |
534 | ++ error = tmy_network_connect_acl(1, SOCK_RAW, | |
535 | ++ in6->sin6_addr.s6_addr, | |
536 | ++ port); | |
537 | ++ } | |
538 | ++ break; | |
539 | ++ | |
540 | ++ case AF_INET: | |
541 | ++ if (addr_len < sizeof(struct sockaddr_in)) | |
542 | ++ break; | |
543 | ++ | |
544 | ++ in = (struct sockaddr_in *) addr; | |
545 | ++ if (type != SOCK_RAW) | |
546 | ++ error = tmy_network_connect_acl(0, type, | |
547 | ++ (u8 *) &in->sin_addr, | |
548 | ++ in->sin_port); | |
549 | ++ else { | |
550 | ++ const u16 port = htons(sock->sk->sk_protocol); | |
551 | ++ | |
552 | ++ error = tmy_network_connect_acl(0, SOCK_RAW, | |
553 | ++ (u8 *) &in->sin_addr, | |
554 | ++ port); | |
555 | ++ } | |
556 | ++ break; | |
557 | ++ } | |
558 | ++ | |
559 | ++ return error; | |
560 | ++} | |
561 | ++ | |
562 | ++static int tmy_socket_bind(struct socket *sock, | |
563 | ++ struct sockaddr *addr, | |
564 | ++ int addr_len0) | |
565 | ++{ | |
566 | ++ unsigned int addr_len = (unsigned int) addr_len0; | |
567 | ++ int error = 0; | |
568 | ++ const unsigned int type = sock->type; | |
569 | ++ | |
570 | ++ /* I don't check if called by kernel process. */ | |
571 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
572 | ++ return 0; | |
573 | ++ | |
574 | ++ if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) | |
575 | ++ return error; | |
576 | ++ | |
577 | ++ switch (addr->sa_family) { | |
578 | ++ struct sockaddr_in6 *in6; | |
579 | ++ struct sockaddr_in *in; | |
580 | ++ | |
581 | ++ case AF_INET6: | |
582 | ++ if (addr_len < SIN6_LEN_RFC2133) | |
583 | ++ break; | |
584 | ++ | |
585 | ++ in6 = ((struct sockaddr_in6 *) addr); | |
586 | ++ if (type != SOCK_RAW) | |
587 | ++ error = tmy_network_bind_acl(1, type, | |
588 | ++ in6->sin6_addr.s6_addr, | |
589 | ++ in6->sin6_port); | |
590 | ++ else { | |
591 | ++ const u16 port = htons(sock->sk->sk_protocol); | |
592 | ++ | |
593 | ++ error = tmy_network_bind_acl(1, SOCK_RAW, | |
594 | ++ in6->sin6_addr.s6_addr, | |
595 | ++ port); | |
596 | ++ } | |
597 | ++ break; | |
598 | ++ | |
599 | ++ case AF_INET: | |
600 | ++ if (addr_len < sizeof(struct sockaddr_in)) | |
601 | ++ break; | |
602 | ++ | |
603 | ++ in = (struct sockaddr_in *) addr; | |
604 | ++ if (type != SOCK_RAW) | |
605 | ++ error = tmy_network_bind_acl(0, type, | |
606 | ++ (u8 *) &in->sin_addr, | |
607 | ++ in->sin_port); | |
608 | ++ else { | |
609 | ++ const u16 port = htons(sock->sk->sk_protocol); | |
610 | ++ | |
611 | ++ error = tmy_network_bind_acl(0, SOCK_RAW, | |
612 | ++ (u8 *) &in->sin_addr, | |
613 | ++ port); | |
614 | ++ } | |
615 | ++ break; | |
616 | ++ } | |
617 | ++ | |
618 | ++ return error; | |
619 | ++} | |
620 | ++ | |
621 | ++static int tmy_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) | |
622 | ++{ | |
623 | ++ int error = 0; | |
624 | ++ const int type = sock->type; | |
625 | ++ struct sockaddr *addr = (struct sockaddr *) msg->msg_name; | |
626 | ++ const unsigned int addr_len = msg->msg_namelen; | |
627 | ++ | |
628 | ++ /* I don't check if called by kernel process. */ | |
629 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
630 | ++ return 0; | |
631 | ++ | |
632 | ++ if (!addr || (type != SOCK_DGRAM && type != SOCK_RAW)) | |
633 | ++ return error; | |
634 | ++ | |
635 | ++ switch (addr->sa_family) { | |
636 | ++ struct sockaddr_in6 *in6; | |
637 | ++ struct sockaddr_in *in; | |
638 | ++ u16 port; | |
639 | ++ | |
640 | ++ case AF_INET6: | |
641 | ++ if (addr_len < SIN6_LEN_RFC2133) | |
642 | ++ break; | |
643 | ++ | |
644 | ++ in6 = (struct sockaddr_in6 *) addr; | |
645 | ++ port = htons(sock->sk->sk_protocol); | |
646 | ++ error = tmy_network_sendmsg_acl(1, type, in6->sin6_addr.s6_addr, | |
647 | ++ type == SOCK_DGRAM ? | |
648 | ++ in6->sin6_port : port); | |
649 | ++ break; | |
650 | ++ | |
651 | ++ case AF_INET: | |
652 | ++ if (addr_len < sizeof(struct sockaddr_in)) | |
653 | ++ break; | |
654 | ++ | |
655 | ++ in = (struct sockaddr_in *) addr; | |
656 | ++ port = htons(sock->sk->sk_protocol); | |
657 | ++ error = tmy_network_sendmsg_acl(0, type, (u8 *) &in->sin_addr, | |
658 | ++ type == SOCK_DGRAM ? | |
659 | ++ in->sin_port : port); | |
660 | ++ break; | |
661 | ++ } | |
662 | ++ | |
663 | ++ return error; | |
664 | ++} | |
665 | ++ | |
666 | ++#ifdef TMY_LSM_EXPANSION | |
667 | ++static int tmy_socket_post_accept(struct socket *sock, struct socket *newsock) | |
668 | ++{ | |
669 | ++ int error = 0; | |
670 | ++ int addr_len; | |
671 | ++ char addr[MAX_SOCK_ADDR]; | |
672 | ++ struct sockaddr *sockaddr = (struct sockaddr *) addr; | |
673 | ++ | |
674 | ++ /* I don't check if called by kernel process. */ | |
675 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
676 | ++ return 0; | |
677 | ++ | |
678 | ++ if ((newsock->sk->sk_family != PF_INET) && | |
679 | ++ (newsock->sk->sk_family != PF_INET6)) | |
680 | ++ return error; | |
681 | ++ | |
682 | ++ if (newsock->ops->getname(newsock, sockaddr, &addr_len, 2) == 0) { | |
683 | ++ switch (sockaddr->sa_family) { | |
684 | ++ struct sockaddr_in6 *in6; | |
685 | ++ struct sockaddr_in *in; | |
686 | ++ | |
687 | ++ case AF_INET6: | |
688 | ++ in6 = (struct sockaddr_in6 *) addr; | |
689 | ++ error = tmy_network_accept_acl(1, | |
690 | ++ in6->sin6_addr.s6_addr, | |
691 | ++ in6->sin6_port); | |
692 | ++ break; | |
693 | ++ | |
694 | ++ case AF_INET: | |
695 | ++ in = (struct sockaddr_in *) addr; | |
696 | ++ error = tmy_network_accept_acl(0, (u8 *) &in->sin_addr, | |
697 | ++ in->sin_port); | |
698 | ++ break; | |
699 | ++ } | |
700 | ++ } else | |
701 | ++ error = -EPERM; | |
702 | ++ | |
703 | ++ if (error) | |
704 | ++ return -ECONNABORTED; | |
705 | ++ return error; | |
706 | ++} | |
707 | ++ | |
708 | ++static int tmy_post_recv_datagram(struct sock *sk, | |
709 | ++ struct sk_buff *skb, | |
710 | ++ unsigned int flags) | |
711 | ++{ | |
712 | ++ int error = 0; | |
713 | ++ const unsigned int type = sk->sk_type; | |
714 | ++ | |
715 | ++ /* skb_recv_datagram() didn't dequeue. */ | |
716 | ++ if (!skb) | |
717 | ++ return 0; | |
718 | ++ | |
719 | ++ /* skb_recv_datagram() can be called from interrupt context. */ | |
720 | ++ if (in_interrupt()) | |
721 | ++ return 0; | |
722 | ++ /* I don't check if called by kernel process. */ | |
723 | ++ if (segment_eq(get_fs(), KERNEL_DS)) | |
724 | ++ return 0; | |
725 | ++ | |
726 | ++ if (type != SOCK_DGRAM && type != SOCK_RAW) | |
727 | ++ return 0; | |
728 | ++ | |
729 | ++ switch (sk->sk_family) { | |
730 | ++ struct sockaddr_in6 sin6; | |
731 | ++ struct sockaddr_in sin; | |
732 | ++ u16 port; | |
733 | ++ | |
734 | ++ case AF_INET6: | |
735 | ++ | |
736 | ++ if (type == SOCK_DGRAM) { | |
737 | ++ /* UDP IPv6 */ | |
738 | ++ sin6.sin6_family = AF_INET6; | |
739 | ++ sin6.sin6_port = udp_hdr(skb)->source; | |
740 | ++ | |
741 | ++ if (skb->protocol == htons(ETH_P_IP)) | |
742 | ++ ipv6_addr_set(&sin6.sin6_addr, 0, 0, | |
743 | ++ htonl(0xffff), | |
744 | ++ ip_hdr(skb)->saddr); | |
745 | ++ else | |
746 | ++ ipv6_addr_copy(&sin6.sin6_addr, | |
747 | ++ &ipv6_hdr(skb)->saddr); | |
748 | ++ | |
749 | ++ port = sin6.sin6_port; | |
750 | ++ } else { | |
751 | ++ /* RAW IPv6 */ | |
752 | ++ sin6.sin6_family = AF_INET6; | |
753 | ++ sin6.sin6_port = 0; | |
754 | ++ ipv6_addr_copy(&sin6.sin6_addr, &ipv6_hdr(skb)->saddr); | |
755 | ++ | |
756 | ++ port = htons(sk->sk_protocol); | |
757 | ++ } | |
758 | ++ | |
759 | ++ error = tmy_network_recvmsg_acl(1, type, | |
760 | ++ sin6.sin6_addr.s6_addr, port); | |
761 | ++ | |
762 | ++ break; | |
763 | ++ | |
764 | ++ case AF_INET: | |
765 | ++ | |
766 | ++ if (type == SOCK_DGRAM) { | |
767 | ++ /* UDP IPv4 */ | |
768 | ++ sin.sin_family = AF_INET; | |
769 | ++ sin.sin_port = udp_hdr(skb)->source; | |
770 | ++ sin.sin_addr.s_addr = ip_hdr(skb)->saddr; | |
771 | ++ | |
772 | ++ port = sin.sin_port; | |
773 | ++ } else { | |
774 | ++ /* RAW IPv4 */ | |
775 | ++ sin.sin_family = AF_INET; | |
776 | ++ sin.sin_addr.s_addr = ip_hdr(skb)->saddr; | |
777 | ++ sin.sin_port = 0; | |
778 | ++ | |
779 | ++ port = htons(sk->sk_protocol); | |
780 | ++ } | |
781 | ++ | |
782 | ++ error = tmy_network_recvmsg_acl(0, type, | |
783 | ++ (u8 *) &sin.sin_addr, port); | |
784 | ++ | |
785 | ++ break; | |
786 | ++ | |
787 | ++ } | |
788 | ++ | |
789 | ++ if (error) | |
790 | ++ error = -EAGAIN; | |
791 | ++ return error; | |
792 | ++} | |
793 | ++#endif | |
794 | ++ | |
795 | ++static int tmy_sb_mount(char *dev_name, | |
796 | ++ struct nameidata *nd, | |
797 | ++ char *type, | |
798 | ++ unsigned long flags, | |
799 | ++ void *data) | |
800 | ++{ | |
801 | ++ char *buf; | |
802 | ++ char *dir_name; | |
803 | ++ int error; | |
804 | ++ | |
805 | ++ error = tmy_capable(TMY_SYS_MOUNT); | |
806 | ++ if (error) | |
807 | ++ return error; | |
808 | ++ | |
809 | ++ buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | |
810 | ++ if (!buf) | |
811 | ++ return -ENOMEM; | |
812 | ++ | |
813 | ++ dir_name = d_path(nd->dentry, nd->mnt, buf, PAGE_SIZE); | |
814 | ++ | |
815 | ++ if (IS_ERR(dir_name)) | |
816 | ++ error = PTR_ERR(dir_name); | |
817 | ++ else | |
818 | ++ error = tmy_mount_perm(dev_name, dir_name, type, flags); | |
819 | ++ | |
820 | ++ if (!error && (flags & MS_REMOUNT) == 0) | |
821 | ++ error = tmy_conceal_mount(nd); | |
822 | ++ | |
823 | ++ kfree(buf); | |
824 | ++ return error; | |
825 | ++} | |
826 | ++ | |
827 | ++static int tmy_sb_umount(struct vfsmount *mnt, int flags) | |
828 | ++{ | |
829 | ++ const int err = tmy_capable(TMY_SYS_UMOUNT); | |
830 | ++ if (err) | |
831 | ++ return err; | |
832 | ++ return tmy_umount_perm(mnt); | |
833 | ++} | |
834 | ++ | |
835 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) | |
836 | ++static int tmy_settime(struct timespec *ts, struct timezone *tz) | |
837 | ++{ | |
838 | ++ return tmy_capable(TMY_SYS_SETTIME); | |
839 | ++} | |
840 | ++#endif | |
841 | ++ | |
842 | ++static int tmy_sb_pivotroot(struct nameidata *old_nd, struct nameidata *new_nd) | |
843 | ++{ | |
844 | ++ const int err = tmy_capable(TMY_SYS_PIVOT_ROOT); | |
845 | ++ if (err) | |
846 | ++ return err; | |
847 | ++ return tmy_pivot_root_perm(old_nd, new_nd); | |
848 | ++} | |
849 | ++ | |
850 | ++static int tmy_task_capable(struct task_struct *tsk, int cap) | |
851 | ++{ | |
852 | ++ int err = 0; | |
853 | ++ if (cap == CAP_SYS_CHROOT) | |
854 | ++ err = tmy_capable(TMY_SYS_CHROOT); | |
855 | ++ else if (cap == CAP_SYS_TTY_CONFIG) | |
856 | ++ err = tmy_capable(TMY_SYS_VHANGUP); | |
857 | ++ else if (cap == CAP_SYS_BOOT) | |
858 | ++ err = tmy_capable(TMY_SYS_REBOOT); | |
859 | ++ else if (cap == CAP_SYS_TIME) | |
860 | ++ err = tmy_capable(TMY_SYS_SETTIME); | |
861 | ++ if (err) | |
862 | ++ return err; | |
863 | ++ if (cap_raised (tsk->cap_effective, cap)) | |
864 | ++ return 0; | |
865 | ++ return -EPERM; | |
866 | ++} | |
867 | ++ | |
868 | ++#ifdef TMY_LSM_EXPANSION | |
869 | ++static int tmy_task_kill_unlocked(int pid, int sig) | |
870 | ++{ | |
871 | ++ const int err = tmy_capable(TMY_SYS_KILL); | |
872 | ++ if (err) | |
873 | ++ return err; | |
874 | ++ return tmy_signal_acl(sig, pid); | |
875 | ++} | |
876 | ++ | |
877 | ++static int tmy_task_tkill_unlocked(int pid, int sig) | |
878 | ++{ | |
879 | ++ const int err = tmy_capable(TMY_SYS_KILL); | |
880 | ++ if (err) | |
881 | ++ return err; | |
882 | ++ return tmy_signal_acl(sig, pid); | |
883 | ++} | |
884 | ++ | |
885 | ++static int tmy_task_tgkill_unlocked(int tgid, int pid, int sig) | |
886 | ++{ | |
887 | ++ const int err = tmy_capable(TMY_SYS_KILL); | |
888 | ++ if (err) | |
889 | ++ return err; | |
890 | ++ return tmy_signal_acl(sig, pid); | |
891 | ++} | |
892 | ++#endif | |
893 | ++ | |
894 | ++static struct security_operations tomoyo_security_ops = { | |
895 | ++ .task_alloc_security = tmy_task_alloc_security, | |
896 | ++ .task_free_security = tmy_task_free_security, | |
897 | ++ .bprm_alloc_security = tmy_bprm_alloc_security, | |
898 | ++ .bprm_check_security = tmy_bprm_check_security, | |
899 | ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) | |
900 | ++ .bprm_apply_creds = tomoyo_bprm_apply_creds, | |
901 | ++#else | |
902 | ++ .bprm_post_apply_creds = tmy_bprm_post_apply_creds, | |
903 | ++#endif | |
904 | ++ .bprm_free_security = tmy_bprm_free_security, | |
905 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 21) | |
906 | ++ .sysctl = tmy_sysctl, | |
907 | ++#endif | |
908 | ++ .inode_permission = tmy_inode_permission, | |
909 | ++ .inode_setattr = tmy_inode_setattr, | |
910 | ++ .inode_create = tmy_inode_create, | |
911 | ++ .inode_unlink = tmy_inode_unlink, | |
912 | ++ .inode_mkdir = tmy_inode_mkdir, | |
913 | ++ .inode_rmdir = tmy_inode_rmdir, | |
914 | ++ .inode_symlink = tmy_inode_symlink, | |
915 | ++ .inode_mknod = tmy_inode_mknod, | |
916 | ++ .inode_link = tmy_inode_link, | |
917 | ++ .inode_rename = tmy_inode_rename, | |
918 | ++ .file_fcntl = tmy_file_fcntl, | |
919 | ++ .file_ioctl = tmy_file_ioctl, | |
920 | ++ .socket_listen = tmy_socket_listen, | |
921 | ++ .socket_create = tmy_socket_create, | |
922 | ++ .socket_connect = tmy_socket_connect, | |
923 | ++ .socket_bind = tmy_socket_bind, | |
924 | ++ .socket_sendmsg = tmy_socket_sendmsg, | |
925 | ++ .sb_mount = tmy_sb_mount, | |
926 | ++ .sb_umount = tmy_sb_umount, | |
927 | ++ .sb_pivotroot = tmy_sb_pivotroot, | |
928 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 10) | |
929 | ++ .settime = tmy_settime, | |
930 | ++#endif | |
931 | ++ .capable = tmy_task_capable, | |
932 | ++#ifdef TMY_LSM_EXPANSION | |
933 | ++ .socket_post_accept = tmy_socket_post_accept, | |
934 | ++ .post_recv_datagram = tmy_post_recv_datagram, | |
935 | ++ .task_kill_unlocked = tmy_task_kill_unlocked, | |
936 | ++ .task_tkill_unlocked = tmy_task_tkill_unlocked, | |
937 | ++ .task_tgkill_unlocked = tmy_task_tgkill_unlocked, | |
938 | ++#endif | |
939 | ++}; | |
940 | ++ | |
941 | ++static int __init tmy_init(void) | |
942 | ++{ | |
943 | ++ | |
944 | ++ /* register ourselves with the security framework */ | |
945 | ++ if (register_security(&tomoyo_security_ops)) | |
946 | ++ panic("Failure registering TOMOYO Linux"); | |
947 | ++ | |
948 | ++ printk(KERN_INFO "TOMOYO Linux initialized\n"); | |
949 | ++ | |
950 | ++ INIT_LIST_HEAD(&KERNEL_DOMAIN.list); | |
951 | ++ INIT_LIST_HEAD(&KERNEL_DOMAIN.acl_info_list); | |
952 | ++ KERNEL_DOMAIN.domainname = tmy_save_name(TMY_ROOT_NAME); | |
953 | ++ list_add_tail_mb(&KERNEL_DOMAIN.list, &domain_list); | |
954 | ++ | |
955 | ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) | |
956 | ++ tmy_cachep = kmem_cache_create("tomoyo_security", | |
957 | ++ sizeof(struct tmy_security), | |
958 | ++ 0, SLAB_PANIC, NULL); | |
959 | ++#else | |
960 | ++ tmy_cachep = kmem_cache_create("tomoyo_security", | |
961 | ++ sizeof(struct tmy_security), | |
962 | ++ 0, SLAB_PANIC, NULL, NULL); | |
963 | ++#endif | |
964 | ++ | |
965 | ++ init_task.security = kmem_cache_alloc(tmy_cachep, GFP_KERNEL); | |
966 | ++ ((struct tmy_security *) init_task.security)->domain = &KERNEL_DOMAIN; | |
967 | ++ ((struct tmy_security *) init_task.security)->prev_domain = NULL; | |
968 | ++ ((struct tmy_security *) init_task.security)->flags = 0; | |
969 | ++ | |
970 | ++ return 0; | |
971 | ++} | |
972 | ++ | |
973 | ++security_initcall(tmy_init); |