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.6.8 2009/05/28 |
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/ccs_common.h> |
16 |
#include <linux/sakura.h> |
17 |
#include <linux/realpath.h> |
18 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
19 |
#include <linux/namei.h> |
20 |
#else |
21 |
#include <linux/fs.h> |
22 |
#endif |
23 |
|
24 |
/* The list for "struct ccs_pivot_root_entry". */ |
25 |
LIST_HEAD(ccs_pivot_root_list); |
26 |
|
27 |
/** |
28 |
* ccs_update_pivot_root_acl - Update "struct ccs_pivot_root_entry" list. |
29 |
* |
30 |
* @old_root: The name of old root directory. |
31 |
* @new_root: The name of new root directory. |
32 |
* @is_delete: True if it is a delete request. |
33 |
* |
34 |
* Returns 0 on success, negative value otherwise. |
35 |
*/ |
36 |
static int ccs_update_pivot_root_acl(const char *old_root, const char *new_root, |
37 |
const bool is_delete) |
38 |
{ |
39 |
struct ccs_pivot_root_entry *entry = NULL; |
40 |
struct ccs_pivot_root_entry *ptr; |
41 |
const struct ccs_path_info *saved_old_root; |
42 |
const struct ccs_path_info *saved_new_root; |
43 |
int error = is_delete ? -ENOENT : -ENOMEM; |
44 |
if (!ccs_is_correct_path(old_root, 1, 0, 1) || |
45 |
!ccs_is_correct_path(new_root, 1, 0, 1)) |
46 |
return -EINVAL; |
47 |
saved_old_root = ccs_get_name(old_root); |
48 |
saved_new_root = ccs_get_name(new_root); |
49 |
if (!saved_old_root || !saved_new_root) |
50 |
goto out; |
51 |
if (!is_delete) |
52 |
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
53 |
mutex_lock(&ccs_policy_lock); |
54 |
list_for_each_entry_rcu(ptr, &ccs_pivot_root_list, list) { |
55 |
if (ptr->old_root != saved_old_root || |
56 |
ptr->new_root != saved_new_root) |
57 |
continue; |
58 |
ptr->is_deleted = is_delete; |
59 |
error = 0; |
60 |
break; |
61 |
} |
62 |
if (!is_delete && error && ccs_memory_ok(entry)) { |
63 |
entry->old_root = saved_old_root; |
64 |
saved_old_root = NULL; |
65 |
entry->new_root = saved_new_root; |
66 |
saved_new_root = NULL; |
67 |
list_add_tail_rcu(&entry->list, &ccs_pivot_root_list); |
68 |
entry = NULL; |
69 |
error = 0; |
70 |
} |
71 |
mutex_unlock(&ccs_policy_lock); |
72 |
if (!is_delete && !error) |
73 |
printk(KERN_CONT "%sAllow pivot_root(%s, %s)\n", ccs_log_level, |
74 |
new_root, old_root); |
75 |
out: |
76 |
ccs_put_name(saved_old_root); |
77 |
ccs_put_name(saved_new_root); |
78 |
kfree(entry); |
79 |
ccs_update_counter(CCS_UPDATES_COUNTER_SYSTEM_POLICY); |
80 |
return error; |
81 |
} |
82 |
|
83 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) |
84 |
#define PATH_or_NAMEIDATA path |
85 |
#else |
86 |
#define PATH_or_NAMEIDATA nameidata |
87 |
#endif |
88 |
/** |
89 |
* ccs_check_pivot_root_permission - Check permission for pivot_root(). |
90 |
* |
91 |
* @old_path: Pointer to "struct path" (for 2.6.27 and later). |
92 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
93 |
* @new_path: Pointer to "struct path" (for 2.6.27 and later). |
94 |
* Pointer to "struct nameidata" (for 2.6.26 and earlier). |
95 |
* |
96 |
* Returns 0 on success, negative value otherwise. |
97 |
* |
98 |
* Caller holds srcu_read_lock(&ccs_ss). |
99 |
*/ |
100 |
int ccs_check_pivot_root_permission(struct PATH_or_NAMEIDATA *old_path, |
101 |
struct PATH_or_NAMEIDATA *new_path) |
102 |
{ |
103 |
struct ccs_request_info r; |
104 |
int error; |
105 |
char *old_root; |
106 |
char *new_root; |
107 |
if (!ccs_can_sleep()) |
108 |
return 0; |
109 |
ccs_init_request_info(&r, NULL, CCS_RESTRICT_PIVOT_ROOT); |
110 |
if (!r.mode) { |
111 |
error = 0; |
112 |
goto done; |
113 |
} |
114 |
retry: |
115 |
error = -EPERM; |
116 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) |
117 |
old_root = ccs_realpath_from_dentry(old_path->path.dentry, |
118 |
old_path->path.mnt); |
119 |
new_root = ccs_realpath_from_dentry(new_path->path.dentry, |
120 |
new_path->path.mnt); |
121 |
#else |
122 |
old_root = ccs_realpath_from_dentry(old_path->dentry, old_path->mnt); |
123 |
new_root = ccs_realpath_from_dentry(new_path->dentry, new_path->mnt); |
124 |
#endif |
125 |
if (old_root && new_root) { |
126 |
struct ccs_path_info old_root_dir; |
127 |
struct ccs_path_info new_root_dir; |
128 |
old_root_dir.name = old_root; |
129 |
ccs_fill_path_info(&old_root_dir); |
130 |
new_root_dir.name = new_root; |
131 |
ccs_fill_path_info(&new_root_dir); |
132 |
if (old_root_dir.is_dir && new_root_dir.is_dir) { |
133 |
struct ccs_pivot_root_entry *ptr; |
134 |
list_for_each_entry_rcu(ptr, &ccs_pivot_root_list, |
135 |
list) { |
136 |
if (ptr->is_deleted) |
137 |
continue; |
138 |
if (!ccs_path_matches_pattern(&old_root_dir, |
139 |
ptr->old_root) || |
140 |
!ccs_path_matches_pattern(&new_root_dir, |
141 |
ptr->new_root)) |
142 |
continue; |
143 |
error = 0; |
144 |
break; |
145 |
} |
146 |
} |
147 |
} |
148 |
if (error) { |
149 |
const bool is_enforce = (r.mode == 3); |
150 |
const char *exename = ccs_get_exe(); |
151 |
printk(KERN_WARNING "SAKURA-%s: pivot_root %s %s " |
152 |
"(pid=%d:exe=%s): Permission denied.\n", |
153 |
ccs_get_msg(is_enforce), new_root, old_root, |
154 |
(pid_t) sys_getpid(), exename); |
155 |
if (is_enforce) |
156 |
error = ccs_check_supervisor(&r, "# %s is requesting\n" |
157 |
"pivot_root %s %s\n", |
158 |
exename, new_root, |
159 |
old_root); |
160 |
else |
161 |
error = 0; |
162 |
if (exename) |
163 |
ccs_free(exename); |
164 |
if (r.mode == 1 && old_root && new_root) |
165 |
ccs_update_pivot_root_acl(old_root, new_root, false); |
166 |
} |
167 |
ccs_free(old_root); |
168 |
ccs_free(new_root); |
169 |
if (error == 1) |
170 |
goto retry; |
171 |
done: |
172 |
ccs_exit_request_info(&r); |
173 |
return error; |
174 |
} |
175 |
|
176 |
/** |
177 |
* ccs_write_pivot_root_policy - Write "struct ccs_pivot_root_entry" list. |
178 |
* |
179 |
* @data: String to parse. |
180 |
* @is_delete: True if it is a delete request. |
181 |
* |
182 |
* Returns 0 on success, negative value otherwise. |
183 |
*/ |
184 |
int ccs_write_pivot_root_policy(char *data, const bool is_delete) |
185 |
{ |
186 |
char *cp = strchr(data, ' '); |
187 |
if (!cp) |
188 |
return -EINVAL; |
189 |
*cp++ = '\0'; |
190 |
return ccs_update_pivot_root_acl(cp, data, is_delete); |
191 |
} |
192 |
|
193 |
/** |
194 |
* ccs_read_pivot_root_policy - Read "struct ccs_pivot_root_entry" list. |
195 |
* |
196 |
* @head: Pointer to "struct ccs_io_buffer". |
197 |
* |
198 |
* Returns true on success, false otherwise. |
199 |
* |
200 |
* Caller holds srcu_read_lock(&ccs_ss). |
201 |
*/ |
202 |
bool ccs_read_pivot_root_policy(struct ccs_io_buffer *head) |
203 |
{ |
204 |
struct list_head *pos; |
205 |
bool done = true; |
206 |
list_for_each_cookie(pos, head->read_var2, &ccs_pivot_root_list) { |
207 |
struct ccs_pivot_root_entry *ptr; |
208 |
ptr = list_entry(pos, struct ccs_pivot_root_entry, list); |
209 |
if (ptr->is_deleted) |
210 |
continue; |
211 |
done = ccs_io_printf(head, KEYWORD_ALLOW_PIVOT_ROOT "%s %s\n", |
212 |
ptr->new_root->name, ptr->old_root->name); |
213 |
if (!done) |
214 |
break; |
215 |
} |
216 |
return done; |
217 |
} |