1 |
/* |
2 |
* fs/tomoyo_capability.c |
3 |
* |
4 |
* Implementation of the Domain-Based Mandatory Access Control. |
5 |
* |
6 |
* Copyright (C) 2005-2008 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.5.3-pre 2008/01/15 |
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 |
/***** TOMOYO Linux start. *****/ |
15 |
|
16 |
#include <linux/ccs_common.h> |
17 |
#include <linux/tomoyo.h> |
18 |
#include <linux/realpath.h> |
19 |
|
20 |
/************************* VARIABLES *************************/ |
21 |
|
22 |
extern struct mutex domain_acl_lock; |
23 |
extern bool sbin_init_started; |
24 |
|
25 |
static struct { |
26 |
const char *keyword; |
27 |
u8 current_value; |
28 |
const char *capability_name; |
29 |
} capability_control_array[TOMOYO_MAX_CAPABILITY_INDEX] = { /* domain_policy.conf */ |
30 |
[TOMOYO_INET_STREAM_SOCKET_CREATE] = { "inet_tcp_create", 0, "socket(PF_INET, SOCK_STREAM)" }, |
31 |
[TOMOYO_INET_STREAM_SOCKET_LISTEN] = { "inet_tcp_listen", 0, "listen(PF_INET, SOCK_STREAM)" }, |
32 |
[TOMOYO_INET_STREAM_SOCKET_CONNECT] = { "inet_tcp_connect", 0, "connect(PF_INET, SOCK_STREAM)" }, |
33 |
[TOMOYO_USE_INET_DGRAM_SOCKET] = { "use_inet_udp", 0, "socket(PF_INET, SOCK_DGRAM)" }, |
34 |
[TOMOYO_USE_INET_RAW_SOCKET] = { "use_inet_ip", 0, "socket(PF_INET, SOCK_RAW)" }, |
35 |
[TOMOYO_USE_ROUTE_SOCKET] = { "use_route", 0, "socket(PF_ROUTE)" }, |
36 |
[TOMOYO_USE_PACKET_SOCKET] = { "use_packet", 0, "socket(PF_PACKET)" }, |
37 |
[TOMOYO_SYS_MOUNT] = { "SYS_MOUNT", 0, "sys_mount()" }, |
38 |
[TOMOYO_SYS_UMOUNT] = { "SYS_UMOUNT", 0, "sys_umount()" }, |
39 |
[TOMOYO_SYS_REBOOT] = { "SYS_REBOOT", 0, "sys_reboot()" }, |
40 |
[TOMOYO_SYS_CHROOT] = { "SYS_CHROOT", 0, "sys_chroot()" }, |
41 |
[TOMOYO_SYS_KILL] = { "SYS_KILL", 0, "sys_kill()" }, |
42 |
[TOMOYO_SYS_VHANGUP] = { "SYS_VHANGUP", 0, "sys_vhangup()" }, |
43 |
[TOMOYO_SYS_SETTIME] = { "SYS_TIME", 0, "sys_settimeofday()" }, |
44 |
[TOMOYO_SYS_NICE] = { "SYS_NICE", 0, "sys_nice()" }, |
45 |
[TOMOYO_SYS_SETHOSTNAME] = { "SYS_SETHOSTNAME", 0, "sys_sethostname()" }, |
46 |
[TOMOYO_USE_KERNEL_MODULE] = { "use_kernel_module", 0, "kernel_module" }, |
47 |
[TOMOYO_CREATE_FIFO] = { "create_fifo", 0, "mknod(FIFO)" }, |
48 |
[TOMOYO_CREATE_BLOCK_DEV] = { "create_block_dev", 0, "mknod(BDEV)" }, |
49 |
[TOMOYO_CREATE_CHAR_DEV] = { "create_char_dev", 0, "mknod(CDEV)" }, |
50 |
[TOMOYO_CREATE_UNIX_SOCKET] = { "create_unix_socket", 0, "mknod(SOCKET)" }, |
51 |
[TOMOYO_SYS_LINK] = { "SYS_LINK", 0, "sys_link()" }, |
52 |
[TOMOYO_SYS_SYMLINK] = { "SYS_SYMLINK", 0, "sys_symlink()" }, |
53 |
[TOMOYO_SYS_RENAME] = { "SYS_RENAME", 0, "sys_rename()" }, |
54 |
[TOMOYO_SYS_UNLINK] = { "SYS_UNLINK", 0, "sys_unlink()" }, |
55 |
[TOMOYO_SYS_CHMOD] = { "SYS_CHMOD", 0, "sys_chmod()" }, |
56 |
[TOMOYO_SYS_CHOWN] = { "SYS_CHOWN", 0, "sys_chown()" }, |
57 |
[TOMOYO_SYS_IOCTL] = { "SYS_IOCTL", 0, "sys_ioctl()" }, |
58 |
[TOMOYO_SYS_KEXEC_LOAD] = { "SYS_KEXEC_LOAD", 0, "sys_kexec_load()" }, |
59 |
[TOMOYO_SYS_PIVOT_ROOT] = { "SYS_PIVOT_ROOT", 0, "sys_pivot_root()" }, |
60 |
[TOMOYO_SYS_PTRACE] = { "SYS_PTRACE", 0, "sys_ptrace()" }, |
61 |
}; |
62 |
|
63 |
struct profile { |
64 |
unsigned char value[TOMOYO_MAX_CAPABILITY_INDEX]; |
65 |
}; |
66 |
|
67 |
static struct profile *profile_ptr[MAX_PROFILES]; |
68 |
|
69 |
/************************* UTILITY FUNCTIONS *************************/ |
70 |
|
71 |
const char *cap_operation2keyword(const u8 operation) |
72 |
{ |
73 |
return operation < TOMOYO_MAX_CAPABILITY_INDEX ? capability_control_array[operation].keyword : NULL; |
74 |
} |
75 |
|
76 |
static const char *cap_operation2name(const u8 operation) |
77 |
{ |
78 |
return operation < TOMOYO_MAX_CAPABILITY_INDEX ? capability_control_array[operation].capability_name : NULL; |
79 |
} |
80 |
|
81 |
/* Check whether the given capability control is enabled. */ |
82 |
static u8 CheckCapabilityFlags(const u8 index) |
83 |
{ |
84 |
const u8 profile = current->domain_info->profile; |
85 |
return sbin_init_started && index < TOMOYO_MAX_CAPABILITY_INDEX |
86 |
#if MAX_PROFILES != 256 |
87 |
&& profile < MAX_PROFILES |
88 |
#endif |
89 |
&& profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0; |
90 |
} |
91 |
|
92 |
static struct profile *FindOrAssignNewProfile(const u8 profile) |
93 |
{ |
94 |
static DEFINE_MUTEX(profile_lock); |
95 |
struct profile *ptr = NULL; |
96 |
mutex_lock(&profile_lock); |
97 |
if ( |
98 |
#if MAX_PROFILES != 256 |
99 |
profile < MAX_PROFILES && |
100 |
#endif |
101 |
(ptr = profile_ptr[profile]) == NULL) { |
102 |
if ((ptr = alloc_element(sizeof(*ptr))) != NULL) { |
103 |
u8 i; |
104 |
for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) ptr->value[i] = capability_control_array[i].current_value; |
105 |
mb(); /* Avoid out-of-order execution. */ |
106 |
profile_ptr[profile] = ptr; |
107 |
} |
108 |
} |
109 |
mutex_unlock(&profile_lock); |
110 |
return ptr; |
111 |
} |
112 |
|
113 |
int SetCapabilityStatus(const char *data, u8 value, const u8 profile) |
114 |
{ |
115 |
u8 i; |
116 |
struct profile *ptr = FindOrAssignNewProfile(profile); |
117 |
if (!ptr) return -EINVAL; |
118 |
for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) { |
119 |
if (strcmp(data, capability_control_array[i].keyword)) continue; |
120 |
if (value > 3) value = 3; |
121 |
ptr->value[i] = value; |
122 |
return 0; |
123 |
} |
124 |
return -EINVAL; |
125 |
} |
126 |
|
127 |
int ReadCapabilityStatus(struct io_buffer *head) |
128 |
{ |
129 |
int step; |
130 |
for (step = head->read_step; step < MAX_PROFILES * TOMOYO_MAX_CAPABILITY_INDEX; step++) { |
131 |
const int i = step / TOMOYO_MAX_CAPABILITY_INDEX, j = step % TOMOYO_MAX_CAPABILITY_INDEX; |
132 |
const struct profile *profile = profile_ptr[i]; |
133 |
head->read_step = step; |
134 |
if (!profile) continue; |
135 |
if (io_printf(head, "%u-" KEYWORD_MAC_FOR_CAPABILITY "%s=%u\n", i, capability_control_array[j].keyword, profile->value[j])) break; |
136 |
} |
137 |
return step < MAX_PROFILES * TOMOYO_MAX_CAPABILITY_INDEX ? -ENOMEM : 0; |
138 |
} |
139 |
|
140 |
/************************* AUDIT FUNCTIONS *************************/ |
141 |
|
142 |
static int AuditCapabilityLog(const u8 operation, const bool is_granted, const u8 profile, const u8 mode) |
143 |
{ |
144 |
char *buf; |
145 |
int len = 64; |
146 |
if (CanSaveAuditLog(is_granted) < 0) return -ENOMEM; |
147 |
if ((buf = InitAuditLog(&len, profile, mode)) == NULL) return -ENOMEM; |
148 |
snprintf(buf + strlen(buf), len - strlen(buf) - 1, KEYWORD_ALLOW_CAPABILITY "%s\n", cap_operation2keyword(operation)); |
149 |
return WriteAuditLog(buf, is_granted); |
150 |
} |
151 |
|
152 |
/************************* CAPABILITY ACL HANDLER *************************/ |
153 |
|
154 |
static int AddCapabilityACL(const u8 operation, struct domain_info *domain, const struct condition_list *condition, const bool is_delete) |
155 |
{ |
156 |
struct acl_info *ptr; |
157 |
struct capability_acl_record *acl; |
158 |
struct capability_acl_record_with_condition *p; |
159 |
int error = -ENOMEM; |
160 |
if (!domain) return -EINVAL; |
161 |
mutex_lock(&domain_acl_lock); |
162 |
if (!is_delete) { |
163 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
164 |
switch (ptr->type) { |
165 |
case TYPE_CAPABILITY_ACL: |
166 |
if (condition) continue; |
167 |
acl = container_of(ptr, struct capability_acl_record, head); |
168 |
break; |
169 |
case TYPE_CAPABILITY_ACL_WITH_CONDITION: |
170 |
p = container_of(ptr, struct capability_acl_record_with_condition, record.head); |
171 |
if (p->condition != condition) continue; |
172 |
acl = &p->record; |
173 |
break; |
174 |
default: |
175 |
continue; |
176 |
} |
177 |
if (acl->operation != operation) continue; |
178 |
acl->is_deleted = 0; |
179 |
/* Found. Nothing to do. */ |
180 |
error = 0; |
181 |
goto out; |
182 |
} |
183 |
/* Not found. Append it to the tail. */ |
184 |
if (condition) { |
185 |
if ((p = alloc_element(sizeof(*p))) == NULL) goto out; |
186 |
acl = &p->record; |
187 |
p->condition = condition; |
188 |
acl->head.type = TYPE_CAPABILITY_ACL_WITH_CONDITION; |
189 |
} else { |
190 |
if ((acl = alloc_element(sizeof(*acl))) == NULL) goto out; |
191 |
acl->head.type = TYPE_CAPABILITY_ACL; |
192 |
} |
193 |
acl->operation = operation; |
194 |
error = AddDomainACL(domain, &acl->head); |
195 |
} else { |
196 |
error = -ENOENT; |
197 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
198 |
switch (ptr->type) { |
199 |
case TYPE_CAPABILITY_ACL: |
200 |
if (condition) continue; |
201 |
acl = container_of(ptr, struct capability_acl_record, head); |
202 |
break; |
203 |
case TYPE_CAPABILITY_ACL_WITH_CONDITION: |
204 |
p = container_of(ptr, struct capability_acl_record_with_condition, record.head); |
205 |
if (p->condition != condition) continue; |
206 |
acl = &p->record; |
207 |
break; |
208 |
default: |
209 |
continue; |
210 |
} |
211 |
if (acl->is_deleted || acl->operation != operation) continue; |
212 |
acl->is_deleted = 1; |
213 |
error = DelDomainACL(); |
214 |
break; |
215 |
} |
216 |
} |
217 |
out: ; |
218 |
mutex_unlock(&domain_acl_lock); |
219 |
return error; |
220 |
} |
221 |
|
222 |
int CheckCapabilityACL(const u8 operation) |
223 |
{ |
224 |
struct domain_info * const domain = current->domain_info; |
225 |
struct acl_info *ptr; |
226 |
const u8 profile = current->domain_info->profile; |
227 |
const u8 mode = CheckCapabilityFlags(operation); |
228 |
const bool is_enforce = (mode == 3); |
229 |
bool found = 0; |
230 |
if (!mode) return 0; |
231 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
232 |
struct capability_acl_record *acl; |
233 |
struct capability_acl_record_with_condition *p; |
234 |
const struct condition_list *cond; |
235 |
switch (ptr->type) { |
236 |
default: |
237 |
continue; |
238 |
case TYPE_CAPABILITY_ACL: |
239 |
acl = container_of(ptr, struct capability_acl_record, head); |
240 |
cond = NULL; |
241 |
break; |
242 |
case TYPE_CAPABILITY_ACL_WITH_CONDITION: |
243 |
p = container_of(ptr, struct capability_acl_record_with_condition, record.head); |
244 |
acl = &p->record; |
245 |
cond = p->condition; |
246 |
break; |
247 |
} |
248 |
if (acl->is_deleted || acl->operation != operation || !CheckCondition(cond, NULL)) continue; |
249 |
found = 1; |
250 |
break; |
251 |
} |
252 |
AuditCapabilityLog(operation, found, profile, mode); |
253 |
if (found) return 0; |
254 |
if (TomoyoVerboseMode()) { |
255 |
printk("TOMOYO-%s: %s denied for %s\n", GetMSG(is_enforce), cap_operation2name(operation), GetLastName(domain)); |
256 |
} |
257 |
if (is_enforce) return CheckSupervisor("%s\n" KEYWORD_ALLOW_CAPABILITY "%s\n", domain->domainname->name, cap_operation2keyword(operation)); |
258 |
else if (mode == 1 && CheckDomainQuota(domain)) AddCapabilityACL(operation, domain, NULL, 0); |
259 |
return 0; |
260 |
} |
261 |
EXPORT_SYMBOL(CheckCapabilityACL); |
262 |
|
263 |
int AddCapabilityPolicy(char *data, struct domain_info *domain, const struct condition_list *condition, const bool is_delete) |
264 |
{ |
265 |
u8 capability; |
266 |
for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX; capability++) { |
267 |
if (strcmp(data, capability_control_array[capability].keyword) == 0) { |
268 |
return AddCapabilityACL(capability, domain, condition, is_delete); |
269 |
} |
270 |
} |
271 |
return -EINVAL; |
272 |
} |
273 |
|
274 |
/***** TOMOYO Linux end. *****/ |