1 |
/* |
2 |
* fs/tomoyo_capability.c |
3 |
* |
4 |
* Implementation of the Domain-Based Mandatory Access Control. |
5 |
* |
6 |
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.5.0 2007/09/20 |
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 semaphore domain_acl_lock; |
23 |
extern int sbin_init_started; |
24 |
|
25 |
static struct { |
26 |
const char *keyword; |
27 |
unsigned int current_value; |
28 |
const char *capability_name; |
29 |
} capability_control_array[TOMOYO_MAX_CAPABILITY_INDEX] = { /* domain_policy.txt */ |
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 |
}; |
61 |
|
62 |
struct profile { |
63 |
unsigned char value[TOMOYO_MAX_CAPABILITY_INDEX]; |
64 |
}; |
65 |
|
66 |
static struct profile *profile_ptr[MAX_PROFILES]; |
67 |
|
68 |
/************************* UTILITY FUNCTIONS *************************/ |
69 |
|
70 |
const char *capability2keyword(const unsigned int capability) |
71 |
{ |
72 |
return capability < TOMOYO_MAX_CAPABILITY_INDEX ? capability_control_array[capability].keyword : NULL; |
73 |
} |
74 |
|
75 |
static const char *capability2name(const unsigned int capability) |
76 |
{ |
77 |
return capability < TOMOYO_MAX_CAPABILITY_INDEX ? capability_control_array[capability].capability_name : NULL; |
78 |
} |
79 |
|
80 |
/* Check whether the given capability control is enabled. */ |
81 |
static unsigned int CheckCapabilityFlags(const unsigned int index) |
82 |
{ |
83 |
const u8 profile = current->domain_info->profile; |
84 |
return sbin_init_started && index < TOMOYO_MAX_CAPABILITY_INDEX |
85 |
#if MAX_PROFILES != 256 |
86 |
&& profile < MAX_PROFILES |
87 |
#endif |
88 |
&& profile_ptr[profile] ? profile_ptr[profile]->value[index] : 0; |
89 |
} |
90 |
|
91 |
/* Check whether the given capability control is enforce mode. */ |
92 |
static u8 CheckCapabilityEnforce(const unsigned int index) |
93 |
{ |
94 |
return CheckCapabilityFlags(index) == 3; |
95 |
} |
96 |
|
97 |
/* Check whether the given capability control is learning mode. */ |
98 |
static u8 CheckCapabilityAccept(const unsigned int index, struct domain_info * const domain) |
99 |
{ |
100 |
if (CheckCapabilityFlags(index) != 1) return 0; |
101 |
return CheckDomainQuota(domain); |
102 |
} |
103 |
|
104 |
static struct profile *FindOrAssignNewProfile(const unsigned int profile) |
105 |
{ |
106 |
static DECLARE_MUTEX(profile_lock); |
107 |
struct profile *ptr = NULL; |
108 |
down(&profile_lock); |
109 |
if (profile < MAX_PROFILES && (ptr = profile_ptr[profile]) == NULL) { |
110 |
if ((ptr = alloc_element(sizeof(*ptr))) != NULL) { |
111 |
int i; |
112 |
for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) ptr->value[i] = capability_control_array[i].current_value; |
113 |
mb(); /* Instead of using spinlock. */ |
114 |
profile_ptr[profile] = ptr; |
115 |
} |
116 |
} |
117 |
up(&profile_lock); |
118 |
return ptr; |
119 |
} |
120 |
|
121 |
int SetCapabilityStatus(const char *data, unsigned int value, const unsigned int profile) |
122 |
{ |
123 |
int i; |
124 |
struct profile *ptr = FindOrAssignNewProfile(profile); |
125 |
if (!ptr) return -EINVAL; |
126 |
for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) { |
127 |
if (strcmp(data, capability_control_array[i].keyword)) continue; |
128 |
if (value > 3) value = 3; |
129 |
ptr->value[i] = value; |
130 |
return 0; |
131 |
} |
132 |
return -EINVAL; |
133 |
} |
134 |
|
135 |
int ReadCapabilityStatus(struct io_buffer *head) |
136 |
{ |
137 |
int step; |
138 |
for (step = head->read_step; step < MAX_PROFILES * TOMOYO_MAX_CAPABILITY_INDEX; step++) { |
139 |
const int i = step / TOMOYO_MAX_CAPABILITY_INDEX, j = step % TOMOYO_MAX_CAPABILITY_INDEX; |
140 |
const struct profile *profile = profile_ptr[i]; |
141 |
head->read_step = step; |
142 |
if (!profile) continue; |
143 |
if (io_printf(head, "%u-" KEYWORD_MAC_FOR_CAPABILITY "%s=%u\n", i, capability_control_array[j].keyword, profile->value[j])) break; |
144 |
} |
145 |
return step < MAX_PROFILES * TOMOYO_MAX_CAPABILITY_INDEX ? -ENOMEM : 0; |
146 |
} |
147 |
|
148 |
/************************* AUDIT FUNCTIONS *************************/ |
149 |
|
150 |
static int AuditCapabilityLog(const unsigned int capability, const u8 is_granted) |
151 |
{ |
152 |
char *buf; |
153 |
int len = 64; |
154 |
if (CanSaveAuditLog(is_granted) < 0) return -ENOMEM; |
155 |
if ((buf = InitAuditLog(&len)) == NULL) return -ENOMEM; |
156 |
snprintf(buf + strlen(buf), len - strlen(buf) - 1, KEYWORD_ALLOW_CAPABILITY "%s\n", capability2keyword(capability)); |
157 |
return WriteAuditLog(buf, is_granted); |
158 |
} |
159 |
|
160 |
/************************* CAPABILITY ACL HANDLER *************************/ |
161 |
|
162 |
static int AddCapabilityACL(const unsigned int capability, struct domain_info *domain, const struct condition_list *condition, const u8 is_delete) |
163 |
{ |
164 |
struct acl_info *ptr; |
165 |
int error = -ENOMEM; |
166 |
const u16 hash = capability; |
167 |
if (!domain) return -EINVAL; |
168 |
down(&domain_acl_lock); |
169 |
if (!is_delete) { |
170 |
if ((ptr = domain->first_acl_ptr) == NULL) goto first_entry; |
171 |
while (1) { |
172 |
struct capability_acl_record *new_ptr = (struct capability_acl_record *) ptr; |
173 |
if (ptr->type == TYPE_CAPABILITY_ACL && new_ptr->capability == hash && ptr->cond == condition) { |
174 |
ptr->is_deleted = 0; |
175 |
/* Found. Nothing to do. */ |
176 |
error = 0; |
177 |
break; |
178 |
} |
179 |
if (ptr->next) { |
180 |
ptr = ptr->next; |
181 |
continue; |
182 |
} |
183 |
first_entry: ; |
184 |
/* Not found. Append it to the tail. */ |
185 |
if ((new_ptr = alloc_element(sizeof(*new_ptr))) == NULL) break; |
186 |
new_ptr->head.type = TYPE_CAPABILITY_ACL; |
187 |
new_ptr->capability = hash; |
188 |
new_ptr->head.cond = condition; |
189 |
error = AddDomainACL(ptr, domain, (struct acl_info *) new_ptr); |
190 |
break; |
191 |
} |
192 |
} else { |
193 |
error = -ENOENT; |
194 |
for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) { |
195 |
struct capability_acl_record *ptr2 = (struct capability_acl_record *) ptr; |
196 |
if (ptr->type != TYPE_CAPABILITY_ACL || ptr->is_deleted || ptr2->capability != hash || ptr->cond != condition) continue; |
197 |
error = DelDomainACL(ptr); |
198 |
break; |
199 |
} |
200 |
} |
201 |
up(&domain_acl_lock); |
202 |
return error; |
203 |
} |
204 |
|
205 |
int CheckCapabilityACL(const unsigned int capability) |
206 |
{ |
207 |
struct domain_info * const domain = current->domain_info; |
208 |
struct acl_info *ptr; |
209 |
const u8 is_enforce = CheckCapabilityEnforce(capability); |
210 |
const u16 hash = capability; |
211 |
if (!CheckCapabilityFlags(capability)) return 0; |
212 |
for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) { |
213 |
struct capability_acl_record *ptr2 = (struct capability_acl_record *) ptr; |
214 |
if (ptr->type != TYPE_CAPABILITY_ACL || ptr->is_deleted || ptr2->capability != hash || CheckCondition(ptr->cond, NULL)) continue; |
215 |
AuditCapabilityLog(capability, 1); |
216 |
return 0; |
217 |
} |
218 |
if (TomoyoVerboseMode()) { |
219 |
printk("TOMOYO-%s: %s denied for %s\n", GetMSG(is_enforce), capability2name(capability), GetLastName(domain)); |
220 |
} |
221 |
AuditCapabilityLog(capability, 0); |
222 |
if (is_enforce) return CheckSupervisor("%s\n" KEYWORD_ALLOW_CAPABILITY "%s\n", domain->domainname->name, capability2keyword(capability)); |
223 |
if (CheckCapabilityAccept(capability, domain)) AddCapabilityACL(capability, domain, NULL, 0); |
224 |
return 0; |
225 |
} |
226 |
EXPORT_SYMBOL(CheckCapabilityACL); |
227 |
|
228 |
int AddCapabilityPolicy(char *data, struct domain_info *domain, const u8 is_delete) |
229 |
{ |
230 |
unsigned int capability; |
231 |
const struct condition_list *condition = NULL; |
232 |
char *cp = FindConditionPart(data); |
233 |
if (cp && (condition = FindOrAssignNewCondition(cp)) == NULL) return -EINVAL; |
234 |
for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX; capability++) { |
235 |
if (strcmp(data, capability_control_array[capability].keyword) == 0) { |
236 |
return AddCapabilityACL(capability, domain, condition, is_delete); |
237 |
} |
238 |
} |
239 |
return -EINVAL; |
240 |
} |
241 |
|
242 |
/***** TOMOYO Linux end. *****/ |