1 |
/* |
2 |
* fs/sakura_pivot.c |
3 |
* |
4 |
* Implementation of the Domain-Free Mandatory Access Control. |
5 |
* |
6 |
* Copyright (C) 2005-2009 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.7.0-pre 2009/07/03 |
9 |
* |
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/version.h> |
16 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
17 |
#include <linux/dcache.h> |
18 |
#include <linux/namei.h> |
19 |
#else |
20 |
#include <linux/fs.h> |
21 |
#endif |
22 |
#include <linux/ccs_common.h> |
23 |
#include <linux/sakura.h> |
24 |
|
25 |
/** |
26 |
* ccs_audit_pivot_root_log - Audit pivot_root log. |
27 |
* |
28 |
* @r: Pointer to "struct ccs_request_info". |
29 |
* @new_root: New root directory. |
30 |
* @old_root: Old root directory. |
31 |
* @is_granted: True if this is a granted log. |
32 |
* |
33 |
* Returns 0 on success, negative value otherwise. |
34 |
*/ |
35 |
static int ccs_audit_pivot_root_log(struct ccs_request_info *r, |
36 |
const char *new_root, |
37 |
const char *old_root, |
38 |
const bool is_granted) |
39 |
{ |
40 |
if (!is_granted && ccs_verbose_mode(r->domain)) |
41 |
printk(KERN_WARNING "SAKURA-%s: pivot_root %s %s " |
42 |
"denied for %s\n", ccs_get_msg(r->mode == 3), new_root, |
43 |
old_root, ccs_get_last_name(r->domain)); |
44 |
return ccs_write_audit_log(is_granted, r, KEYWORD_ALLOW_PIVOT_ROOT |
45 |
"%s %s\n", new_root, old_root); |
46 |
} |
47 |
|
48 |
/** |
49 |
* ccs_update_pivot_root_acl - Update "struct ccs_pivot_root_acl_record" list. |
50 |
* |
51 |
* @old_root: The name of old root directory. |
52 |
* @new_root: The name of new root directory. |
53 |
* @is_delete: True if it is a delete request. |
54 |
* |
55 |
* Returns 0 on success, negative value otherwise. |
56 |
*/ |
57 |
static int ccs_update_pivot_root_acl(const char *old_root, const char *new_root, |
58 |
struct ccs_domain_info *domain, |
59 |
struct ccs_condition *condition, |
60 |
const bool is_delete) |
61 |
{ |
62 |
struct ccs_pivot_root_acl_record *entry = NULL; |
63 |
struct ccs_acl_info *ptr; |
64 |
const struct ccs_path_info *saved_old_root; |
65 |
const struct ccs_path_info *saved_new_root; |
66 |
int error = is_delete ? -ENOENT : -ENOMEM; |
67 |
if (!ccs_is_correct_path(old_root, 1, 0, 1) || |
68 |
!ccs_is_correct_path(new_root, 1, 0, 1)) |
69 |
return -EINVAL; |
70 |
saved_old_root = ccs_get_name(old_root); |
71 |
saved_new_root = ccs_get_name(new_root); |
72 |
if (!saved_old_root || !saved_new_root) |
73 |
goto out; |
74 |
if (!is_delete) |
75 |
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
76 |
mutex_lock(&ccs_policy_lock); |
77 |
list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { |
78 |
struct ccs_pivot_root_acl_record *acl; |
79 |
if (ccs_acl_type1(ptr) != TYPE_PIVOT_ROOT_ACL) |
80 |
continue; |
81 |
if (ptr->cond != condition) |
82 |
continue; |
83 |
acl = container_of(ptr, struct ccs_pivot_root_acl_record, head); |
84 |
if (acl->old_root != saved_old_root || |
85 |
acl->new_root != saved_new_root) |
86 |
continue; |
87 |
if (is_delete) |
88 |
error = ccs_del_domain_acl(ptr); |
89 |
else |
90 |
error = ccs_add_domain_acl(NULL, ptr); |
91 |
break; |
92 |
} |
93 |
if (!is_delete && error && ccs_memory_ok(entry, sizeof(*entry))) { |
94 |
entry->head.type = TYPE_PIVOT_ROOT_ACL; |
95 |
entry->head.cond = condition; |
96 |
entry->old_root = saved_old_root; |
97 |
saved_old_root = NULL; |
98 |
entry->new_root = saved_new_root; |
99 |
saved_new_root = NULL; |
100 |
error = ccs_add_domain_acl(domain, &entry->head); |
101 |
entry = NULL; |
102 |
error = 0; |
103 |
} |
104 |
mutex_unlock(&ccs_policy_lock); |
105 |
out: |
106 |
ccs_put_name(saved_old_root); |
107 |
ccs_put_name(saved_new_root); |
108 |
kfree(entry); |
109 |
return error; |
110 |
} |
111 |
|
112 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) |
113 |
#define PATH_or_NAMEIDATA path |
114 |
#else |
115 |
#define PATH_or_NAMEIDATA nameidata |
116 |
#endif |
117 |
/** |
118 |
* ccs_check_pivot_root_permission2 - Check permission for pivot_root(). |
119 |
* |
120 |
* @old_path: Pointer to "struct path" (for 2.6.27 and later). |
121 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
122 |
* @new_path: Pointer to "struct path" (for 2.6.27 and later). |
123 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
124 |
* |
125 |
* Returns 0 on success, negative value otherwise. |
126 |
* |
127 |
* Caller holds ccs_read_lock(). |
128 |
*/ |
129 |
static int ccs_check_pivot_root_permission2(struct PATH_or_NAMEIDATA *old_path, |
130 |
struct PATH_or_NAMEIDATA *new_path) |
131 |
{ |
132 |
struct ccs_request_info r; |
133 |
int error; |
134 |
char *old_root; |
135 |
char *new_root; |
136 |
struct ccs_path_info old_root_dir; |
137 |
struct ccs_path_info new_root_dir; |
138 |
bool is_enforce; |
139 |
ccs_check_read_lock(); |
140 |
if (!ccs_can_sleep()) |
141 |
return 0; |
142 |
ccs_init_request_info(&r, NULL, CCS_MAC_FOR_NAMESPACE); |
143 |
is_enforce = (r.mode == 3); |
144 |
if (!r.mode) |
145 |
return 0; |
146 |
retry: |
147 |
error = -EPERM; |
148 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) |
149 |
old_root = ccs_realpath_from_dentry(old_path->path.dentry, |
150 |
old_path->path.mnt); |
151 |
new_root = ccs_realpath_from_dentry(new_path->path.dentry, |
152 |
new_path->path.mnt); |
153 |
#else |
154 |
old_root = ccs_realpath_from_dentry(old_path->dentry, old_path->mnt); |
155 |
new_root = ccs_realpath_from_dentry(new_path->dentry, new_path->mnt); |
156 |
#endif |
157 |
if (!old_root || !new_root) |
158 |
goto out; |
159 |
old_root_dir.name = old_root; |
160 |
ccs_fill_path_info(&old_root_dir); |
161 |
new_root_dir.name = new_root; |
162 |
ccs_fill_path_info(&new_root_dir); |
163 |
if (old_root_dir.is_dir && new_root_dir.is_dir) { |
164 |
struct ccs_acl_info *ptr; |
165 |
list_for_each_entry_rcu(ptr, &r.domain->acl_info_list, list) { |
166 |
struct ccs_pivot_root_acl_record *acl; |
167 |
if (ccs_acl_type2(ptr) != TYPE_PIVOT_ROOT_ACL) |
168 |
continue; |
169 |
acl = container_of(ptr, |
170 |
struct ccs_pivot_root_acl_record, |
171 |
head); |
172 |
if (!ccs_path_matches_pattern(&old_root_dir, |
173 |
acl->old_root) || |
174 |
!ccs_path_matches_pattern(&new_root_dir, |
175 |
acl->new_root) || |
176 |
!ccs_check_condition(&r, ptr)) |
177 |
continue; |
178 |
r.cond = ptr->cond; |
179 |
error = 0; |
180 |
break; |
181 |
} |
182 |
} |
183 |
ccs_audit_pivot_root_log(&r, new_root, old_root, !error); |
184 |
if (!error) |
185 |
goto out; |
186 |
if (is_enforce) |
187 |
error = ccs_check_supervisor(&r, KEYWORD_ALLOW_PIVOT_ROOT |
188 |
"%s %s\n", new_root, old_root); |
189 |
else if (ccs_domain_quota_ok(&r)) |
190 |
ccs_update_pivot_root_acl(old_root, new_root, r.domain, NULL, |
191 |
false); |
192 |
out: |
193 |
kfree(old_root); |
194 |
kfree(new_root); |
195 |
if (error == 1) |
196 |
goto retry; |
197 |
if (!is_enforce) |
198 |
error = 0; |
199 |
return error; |
200 |
} |
201 |
|
202 |
/** |
203 |
* ccs_check_pivot_root_permission - Check permission for pivot_root(). |
204 |
* |
205 |
* @old_path: Pointer to "struct path" (for 2.6.27 and later). |
206 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
207 |
* @new_path: Pointer to "struct path" (for 2.6.27 and later). |
208 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
209 |
* |
210 |
* Returns 0 on success, negative value otherwise. |
211 |
*/ |
212 |
int ccs_check_pivot_root_permission(struct PATH_or_NAMEIDATA *old_path, |
213 |
struct PATH_or_NAMEIDATA *new_path) |
214 |
{ |
215 |
const int idx = ccs_read_lock(); |
216 |
const int error = ccs_check_pivot_root_permission2(old_path, new_path); |
217 |
ccs_read_unlock(idx); |
218 |
return error; |
219 |
} |
220 |
|
221 |
/** |
222 |
* ccs_write_pivot_root_policy - Write "struct ccs_pivot_root_acl_record" list. |
223 |
* |
224 |
* @data: String to parse. |
225 |
* @domain: Pointer to "struct ccs_domain_info". |
226 |
* @condition: Pointer to "struct ccs_condition". May be NULL. |
227 |
* @is_delete: True if it is a delete request. |
228 |
* |
229 |
* Returns 0 on success, negative value otherwise. |
230 |
*/ |
231 |
int ccs_write_pivot_root_policy(char *data, struct ccs_domain_info *domain, |
232 |
struct ccs_condition *condition, |
233 |
const bool is_delete) |
234 |
{ |
235 |
char *w[2]; |
236 |
if (!ccs_tokenize(data, w, sizeof(w)) || !w[1][0]) |
237 |
return -EINVAL; |
238 |
return ccs_update_pivot_root_acl(w[1], w[0], domain, condition, |
239 |
is_delete); |
240 |
} |