1 |
kumaneko |
581 |
/* |
2 |
|
|
* fs/tomoyo_env.c |
3 |
|
|
* |
4 |
|
|
* Implementation of the Domain-Based Mandatory Access Control. |
5 |
|
|
* |
6 |
kumaneko |
2030 |
* Copyright (C) 2005-2009 NTT DATA CORPORATION |
7 |
kumaneko |
581 |
* |
8 |
kumaneko |
2519 |
* Version: 1.6.8-pre 2009/05/08 |
9 |
kumaneko |
581 |
* |
10 |
|
|
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
11 |
|
|
* See README.ccs for ChangeLog. |
12 |
|
|
* |
13 |
|
|
*/ |
14 |
|
|
|
15 |
|
|
#include <linux/ccs_common.h> |
16 |
|
|
#include <linux/tomoyo.h> |
17 |
|
|
#include <linux/realpath.h> |
18 |
|
|
|
19 |
kumaneko |
1052 |
/** |
20 |
kumaneko |
2002 |
* ccs_audit_env_log - Audit environment variable name log. |
21 |
kumaneko |
1052 |
* |
22 |
kumaneko |
1657 |
* @r: Pointer to "struct ccs_request_info". |
23 |
kumaneko |
1052 |
* @env: The name of environment variable. |
24 |
|
|
* @is_granted: True if this is a granted log. |
25 |
|
|
* |
26 |
|
|
* Returns 0 on success, negative value otherwise. |
27 |
|
|
*/ |
28 |
kumaneko |
2002 |
static int ccs_audit_env_log(struct ccs_request_info *r, const char *env, |
29 |
|
|
const bool is_granted) |
30 |
kumaneko |
581 |
{ |
31 |
kumaneko |
1657 |
return ccs_write_audit_log(is_granted, r, KEYWORD_ALLOW_ENV "%s\n", |
32 |
|
|
env); |
33 |
kumaneko |
581 |
} |
34 |
|
|
|
35 |
kumaneko |
2002 |
/* The list for "struct ccs_globally_usable_env_entry". */ |
36 |
kumaneko |
2540 |
LIST_HEAD(ccs_globally_usable_env_list); |
37 |
kumaneko |
581 |
|
38 |
kumaneko |
1052 |
/** |
39 |
kumaneko |
2002 |
* ccs_update_globally_usable_env_entry - Update "struct ccs_globally_usable_env_entry" list. |
40 |
kumaneko |
1052 |
* |
41 |
|
|
* @env: The name of environment variable. |
42 |
|
|
* @is_delete: True if it is a delete request. |
43 |
|
|
* |
44 |
|
|
* Returns 0 on success, negative value otherwise. |
45 |
|
|
*/ |
46 |
kumaneko |
2002 |
static int ccs_update_globally_usable_env_entry(const char *env, |
47 |
|
|
const bool is_delete) |
48 |
kumaneko |
581 |
{ |
49 |
kumaneko |
2540 |
struct ccs_globally_usable_env_entry *entry = NULL; |
50 |
kumaneko |
2002 |
struct ccs_globally_usable_env_entry *ptr; |
51 |
|
|
const struct ccs_path_info *saved_env; |
52 |
kumaneko |
2540 |
int error = is_delete ? -ENOENT : -ENOMEM; |
53 |
kumaneko |
1064 |
if (!ccs_is_correct_path(env, 0, 0, 0, __func__) || strchr(env, '=')) |
54 |
kumaneko |
1052 |
return -EINVAL; |
55 |
kumaneko |
2540 |
saved_env = ccs_get_name(env); |
56 |
kumaneko |
1052 |
if (!saved_env) |
57 |
|
|
return -ENOMEM; |
58 |
kumaneko |
2540 |
if (!is_delete) |
59 |
|
|
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
60 |
|
|
/***** WRITER SECTION START *****/ |
61 |
|
|
down_write(&ccs_policy_lock); |
62 |
|
|
list_for_each_entry(ptr, &ccs_globally_usable_env_list, list) { |
63 |
kumaneko |
1064 |
if (ptr->env != saved_env) |
64 |
|
|
continue; |
65 |
|
|
ptr->is_deleted = is_delete; |
66 |
|
|
error = 0; |
67 |
kumaneko |
2540 |
break; |
68 |
kumaneko |
581 |
} |
69 |
kumaneko |
2540 |
if (!is_delete && error && ccs_memory_ok(entry)) { |
70 |
|
|
entry->env = saved_env; |
71 |
|
|
saved_env = NULL; |
72 |
|
|
list_add_tail(&entry->list, &ccs_globally_usable_env_list); |
73 |
|
|
entry = NULL; |
74 |
|
|
error = 0; |
75 |
kumaneko |
581 |
} |
76 |
kumaneko |
2540 |
up_write(&ccs_policy_lock); |
77 |
|
|
/***** WRITER SECTION END *****/ |
78 |
|
|
ccs_put_name(saved_env); |
79 |
|
|
kfree(entry); |
80 |
kumaneko |
1064 |
ccs_update_counter(CCS_UPDATES_COUNTER_EXCEPTION_POLICY); |
81 |
kumaneko |
581 |
return error; |
82 |
|
|
} |
83 |
|
|
|
84 |
kumaneko |
1052 |
/** |
85 |
kumaneko |
2002 |
* ccs_is_globally_usable_env - Check whether the given environment variable is acceptable for all domains. |
86 |
kumaneko |
1052 |
* |
87 |
|
|
* @env: The name of environment variable. |
88 |
|
|
* |
89 |
|
|
* Returns true if @env is globally permitted environment variable's name, |
90 |
|
|
* false otherwise. |
91 |
|
|
*/ |
92 |
kumaneko |
2002 |
static bool ccs_is_globally_usable_env(const struct ccs_path_info *env) |
93 |
kumaneko |
581 |
{ |
94 |
kumaneko |
2002 |
struct ccs_globally_usable_env_entry *ptr; |
95 |
kumaneko |
2540 |
bool found = false; |
96 |
|
|
/***** READER SECTION START *****/ |
97 |
|
|
down_read(&ccs_policy_lock); |
98 |
|
|
list_for_each_entry(ptr, &ccs_globally_usable_env_list, list) { |
99 |
|
|
if (ptr->is_deleted || !ccs_path_matches_pattern(env, ptr->env)) |
100 |
|
|
continue; |
101 |
|
|
found = true; |
102 |
|
|
break; |
103 |
kumaneko |
581 |
} |
104 |
kumaneko |
2540 |
up_read(&ccs_policy_lock); |
105 |
|
|
/***** READER SECTION END *****/ |
106 |
|
|
return found; |
107 |
kumaneko |
581 |
} |
108 |
|
|
|
109 |
kumaneko |
1052 |
/** |
110 |
kumaneko |
2002 |
* ccs_write_globally_usable_env_policy - Write "struct ccs_globally_usable_env_entry" list. |
111 |
kumaneko |
1052 |
* |
112 |
|
|
* @data: String to parse. |
113 |
|
|
* @is_delete: True if it is a delete request. |
114 |
|
|
* |
115 |
|
|
* Returns 0 on success, negative value otherwise. |
116 |
|
|
*/ |
117 |
|
|
int ccs_write_globally_usable_env_policy(char *data, const bool is_delete) |
118 |
kumaneko |
581 |
{ |
119 |
kumaneko |
2002 |
return ccs_update_globally_usable_env_entry(data, is_delete); |
120 |
kumaneko |
581 |
} |
121 |
|
|
|
122 |
kumaneko |
1052 |
/** |
123 |
kumaneko |
2002 |
* ccs_read_globally_usable_env_policy - Read "struct ccs_globally_usable_env_entry" list. |
124 |
kumaneko |
1052 |
* |
125 |
|
|
* @head: Pointer to "struct ccs_io_buffer". |
126 |
|
|
* |
127 |
|
|
* Returns 0 on success, false otherwise. |
128 |
|
|
*/ |
129 |
|
|
bool ccs_read_globally_usable_env_policy(struct ccs_io_buffer *head) |
130 |
kumaneko |
581 |
{ |
131 |
kumaneko |
2540 |
struct list_head *pos; |
132 |
|
|
bool done = true; |
133 |
|
|
/***** READER SECTION START *****/ |
134 |
|
|
down_read(&ccs_policy_lock); |
135 |
|
|
list_for_each_cookie(pos, head->read_var2.u.list, |
136 |
kumaneko |
2002 |
&ccs_globally_usable_env_list) { |
137 |
|
|
struct ccs_globally_usable_env_entry *ptr; |
138 |
kumaneko |
2540 |
ptr = list_entry(pos, struct ccs_globally_usable_env_entry, |
139 |
kumaneko |
2002 |
list); |
140 |
kumaneko |
1052 |
if (ptr->is_deleted) |
141 |
|
|
continue; |
142 |
kumaneko |
2540 |
done = ccs_io_printf(head, KEYWORD_ALLOW_ENV "%s\n", |
143 |
|
|
ptr->env->name); |
144 |
|
|
if (!done) |
145 |
|
|
break; |
146 |
kumaneko |
581 |
} |
147 |
kumaneko |
2540 |
up_read(&ccs_policy_lock); |
148 |
|
|
/***** READER SECTION END *****/ |
149 |
|
|
return done; |
150 |
kumaneko |
581 |
} |
151 |
|
|
|
152 |
kumaneko |
1052 |
/** |
153 |
kumaneko |
2002 |
* ccs_update_env_entry - Update "struct ccs_env_acl_record" list. |
154 |
kumaneko |
1052 |
* |
155 |
|
|
* @env: The name of environment variable. |
156 |
kumaneko |
2282 |
* @domain: Pointer to "struct ccs_domain_info". |
157 |
kumaneko |
2002 |
* @condition: Pointer to "struct ccs_condition_list". May be NULL. |
158 |
kumaneko |
1052 |
* @is_delete: True if it is a delete request. |
159 |
|
|
* |
160 |
|
|
* Returns 0 on success, negative value otherwise. |
161 |
|
|
*/ |
162 |
kumaneko |
2282 |
static int ccs_update_env_entry(const char *env, struct ccs_domain_info *domain, |
163 |
kumaneko |
2002 |
const struct ccs_condition_list *condition, |
164 |
|
|
const bool is_delete) |
165 |
kumaneko |
581 |
{ |
166 |
kumaneko |
2540 |
struct ccs_env_acl_record *entry = NULL; |
167 |
kumaneko |
2002 |
struct ccs_acl_info *ptr; |
168 |
|
|
const struct ccs_path_info *saved_env; |
169 |
kumaneko |
2540 |
int error = is_delete ? -ENOENT : -ENOMEM; |
170 |
kumaneko |
1064 |
if (!ccs_is_correct_path(env, 0, 0, 0, __func__) || strchr(env, '=')) |
171 |
kumaneko |
1052 |
return -EINVAL; |
172 |
kumaneko |
2540 |
saved_env = ccs_get_name(env); |
173 |
kumaneko |
1052 |
if (!saved_env) |
174 |
kumaneko |
2544 |
goto out; |
175 |
kumaneko |
1052 |
if (is_delete) |
176 |
|
|
goto delete; |
177 |
kumaneko |
2540 |
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
178 |
|
|
/***** WRITER SECTION START *****/ |
179 |
|
|
down_write(&ccs_policy_lock); |
180 |
|
|
list_for_each_entry(ptr, &domain->acl_info_list, list) { |
181 |
|
|
struct ccs_env_acl_record *acl; |
182 |
kumaneko |
1064 |
if (ccs_acl_type1(ptr) != TYPE_ENV_ACL) |
183 |
kumaneko |
1052 |
continue; |
184 |
kumaneko |
2544 |
if (ptr->cond != condition) |
185 |
kumaneko |
1052 |
continue; |
186 |
kumaneko |
2002 |
acl = container_of(ptr, struct ccs_env_acl_record, head); |
187 |
kumaneko |
1052 |
if (acl->env != saved_env) |
188 |
|
|
continue; |
189 |
|
|
error = ccs_add_domain_acl(NULL, ptr); |
190 |
kumaneko |
2540 |
break; |
191 |
kumaneko |
581 |
} |
192 |
kumaneko |
2540 |
if (error && ccs_memory_ok(entry)) { |
193 |
|
|
entry->head.type = TYPE_ENV_ACL; |
194 |
|
|
entry->head.cond = condition; |
195 |
|
|
entry->env = saved_env; |
196 |
|
|
saved_env = NULL; |
197 |
|
|
error = ccs_add_domain_acl(domain, &entry->head); |
198 |
|
|
entry = NULL; |
199 |
|
|
} |
200 |
|
|
up_write(&ccs_policy_lock); |
201 |
|
|
/***** WRITER SECTION END *****/ |
202 |
kumaneko |
1052 |
goto out; |
203 |
|
|
delete: |
204 |
kumaneko |
2540 |
/***** WRITER SECTION START *****/ |
205 |
|
|
down_write(&ccs_policy_lock); |
206 |
|
|
list_for_each_entry(ptr, &domain->acl_info_list, list) { |
207 |
|
|
struct ccs_env_acl_record *acl; |
208 |
kumaneko |
1064 |
if (ccs_acl_type2(ptr) != TYPE_ENV_ACL) |
209 |
kumaneko |
1052 |
continue; |
210 |
kumaneko |
2544 |
if (ptr->cond != condition) |
211 |
kumaneko |
1052 |
continue; |
212 |
kumaneko |
2002 |
acl = container_of(ptr, struct ccs_env_acl_record, head); |
213 |
kumaneko |
1052 |
if (acl->env != saved_env) |
214 |
|
|
continue; |
215 |
|
|
error = ccs_del_domain_acl(ptr); |
216 |
|
|
break; |
217 |
|
|
} |
218 |
kumaneko |
2540 |
up_write(&ccs_policy_lock); |
219 |
|
|
/***** WRITER SECTION END *****/ |
220 |
kumaneko |
1052 |
out: |
221 |
kumaneko |
2540 |
ccs_put_name(saved_env); |
222 |
|
|
kfree(entry); |
223 |
kumaneko |
581 |
return error; |
224 |
|
|
} |
225 |
|
|
|
226 |
kumaneko |
1052 |
/** |
227 |
kumaneko |
2002 |
* ccs_check_env_acl - Check permission for environment variable's name. |
228 |
kumaneko |
1052 |
* |
229 |
kumaneko |
1657 |
* @r: Pointer to "struct ccs_request_info". |
230 |
kumaneko |
1052 |
* @environ: The name of environment variable. |
231 |
|
|
* |
232 |
|
|
* Returns 0 on success, negative value otherwise. |
233 |
|
|
*/ |
234 |
kumaneko |
2002 |
static int ccs_check_env_acl(struct ccs_request_info *r, const char *environ) |
235 |
kumaneko |
581 |
{ |
236 |
kumaneko |
2540 |
const struct ccs_domain_info *domain = r->cookie.u.domain; |
237 |
kumaneko |
581 |
int error = -EPERM; |
238 |
kumaneko |
2002 |
struct ccs_acl_info *ptr; |
239 |
|
|
struct ccs_path_info env; |
240 |
kumaneko |
1052 |
env.name = environ; |
241 |
|
|
ccs_fill_path_info(&env); |
242 |
kumaneko |
2540 |
down_read(&ccs_policy_lock); |
243 |
|
|
list_for_each_entry(ptr, &domain->acl_info_list, list) { |
244 |
kumaneko |
2002 |
struct ccs_env_acl_record *acl; |
245 |
kumaneko |
1064 |
if (ccs_acl_type2(ptr) != TYPE_ENV_ACL) |
246 |
kumaneko |
1052 |
continue; |
247 |
kumaneko |
2002 |
acl = container_of(ptr, struct ccs_env_acl_record, head); |
248 |
kumaneko |
1657 |
if (!ccs_check_condition(r, ptr) || |
249 |
kumaneko |
1052 |
!ccs_path_matches_pattern(&env, acl->env)) |
250 |
|
|
continue; |
251 |
kumaneko |
2544 |
r->condition_cookie.u.cond = ptr->cond; |
252 |
kumaneko |
856 |
error = 0; |
253 |
|
|
break; |
254 |
kumaneko |
581 |
} |
255 |
kumaneko |
2540 |
up_read(&ccs_policy_lock); |
256 |
|
|
if (error && !domain->ignore_global_allow_env && |
257 |
kumaneko |
2002 |
ccs_is_globally_usable_env(&env)) |
258 |
kumaneko |
1052 |
error = 0; |
259 |
kumaneko |
581 |
return error; |
260 |
|
|
} |
261 |
|
|
|
262 |
kumaneko |
1052 |
/** |
263 |
|
|
* ccs_check_env_perm - Check permission for environment variable's name. |
264 |
|
|
* |
265 |
kumaneko |
1657 |
* @r: Pointer to "struct ccs_request_info". |
266 |
kumaneko |
1052 |
* @env: The name of environment variable. |
267 |
|
|
* |
268 |
|
|
* Returns 0 on success, negative value otherwise. |
269 |
|
|
*/ |
270 |
kumaneko |
1657 |
int ccs_check_env_perm(struct ccs_request_info *r, const char *env) |
271 |
kumaneko |
581 |
{ |
272 |
|
|
int error = 0; |
273 |
kumaneko |
1657 |
const bool is_enforce = (r->mode == 3); |
274 |
|
|
if (!ccs_can_sleep()) |
275 |
|
|
return 0; |
276 |
kumaneko |
1052 |
if (!env || !*env) |
277 |
|
|
return 0; |
278 |
kumaneko |
1561 |
retry: |
279 |
kumaneko |
2002 |
error = ccs_check_env_acl(r, env); |
280 |
|
|
ccs_audit_env_log(r, env, !error); |
281 |
kumaneko |
1052 |
if (!error) |
282 |
|
|
return 0; |
283 |
kumaneko |
2540 |
if (ccs_verbose_mode(r->cookie.u.domain)) |
284 |
kumaneko |
1052 |
printk(KERN_WARNING "TOMOYO-%s: Environ %s denied for %s\n", |
285 |
kumaneko |
1657 |
ccs_get_msg(is_enforce), env, |
286 |
kumaneko |
2540 |
ccs_get_last_name(r->cookie.u.domain)); |
287 |
kumaneko |
1561 |
if (is_enforce) { |
288 |
kumaneko |
1657 |
error = ccs_check_supervisor(r, KEYWORD_ALLOW_ENV "%s\n", env); |
289 |
kumaneko |
1781 |
if (error == 1) |
290 |
kumaneko |
1561 |
goto retry; |
291 |
|
|
return error; |
292 |
|
|
} |
293 |
kumaneko |
2540 |
if (r->mode == 1 && ccs_domain_quota_ok(r->cookie.u.domain)) |
294 |
|
|
ccs_update_env_entry(env, r->cookie.u.domain, |
295 |
|
|
ccs_handler_cond(), false); |
296 |
kumaneko |
856 |
return 0; |
297 |
kumaneko |
581 |
} |
298 |
|
|
|
299 |
kumaneko |
1052 |
/** |
300 |
kumaneko |
2002 |
* ccs_write_env_policy - Write "struct ccs_env_acl_record" list. |
301 |
kumaneko |
1052 |
* |
302 |
|
|
* @data: String to parse. |
303 |
kumaneko |
2282 |
* @domain: Pointer to "struct ccs_domain_info". |
304 |
kumaneko |
2002 |
* @condition: Pointer to "struct ccs_condition_list". May be NULL. |
305 |
kumaneko |
1052 |
* @is_delete: True if it is a delete request. |
306 |
|
|
* |
307 |
|
|
* Returns 0 on success, negative value otherwise. |
308 |
|
|
*/ |
309 |
kumaneko |
2282 |
int ccs_write_env_policy(char *data, struct ccs_domain_info *domain, |
310 |
kumaneko |
2002 |
const struct ccs_condition_list *condition, |
311 |
kumaneko |
1052 |
const bool is_delete) |
312 |
kumaneko |
581 |
{ |
313 |
kumaneko |
2002 |
return ccs_update_env_entry(data, domain, condition, is_delete); |
314 |
kumaneko |
581 |
} |