1 |
kumaneko |
111 |
/* |
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 |
kumaneko |
708 |
* Version: 1.5.2-pre 2007/11/19 |
9 |
kumaneko |
111 |
* |
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 |
kumaneko |
652 |
extern struct mutex domain_acl_lock; |
23 |
kumaneko |
111 |
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 |
kumaneko |
141 |
[TOMOYO_SYS_PIVOT_ROOT] = { "SYS_PIVOT_ROOT", 0, "sys_pivot_root()" }, |
60 |
kumaneko |
111 |
}; |
61 |
|
|
|
62 |
kumaneko |
214 |
struct profile { |
63 |
kumaneko |
111 |
unsigned char value[TOMOYO_MAX_CAPABILITY_INDEX]; |
64 |
kumaneko |
214 |
}; |
65 |
kumaneko |
111 |
|
66 |
kumaneko |
214 |
static struct profile *profile_ptr[MAX_PROFILES]; |
67 |
kumaneko |
111 |
|
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 |
kumaneko |
121 |
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 |
kumaneko |
111 |
} |
90 |
|
|
|
91 |
|
|
/* Check whether the given capability control is enforce mode. */ |
92 |
kumaneko |
621 |
static bool CheckCapabilityEnforce(const unsigned int index) |
93 |
kumaneko |
111 |
{ |
94 |
|
|
return CheckCapabilityFlags(index) == 3; |
95 |
|
|
} |
96 |
|
|
|
97 |
kumaneko |
461 |
/* Check whether the given capability control is learning mode. */ |
98 |
kumaneko |
621 |
static bool CheckCapabilityAccept(const unsigned int index, struct domain_info * const domain) |
99 |
kumaneko |
111 |
{ |
100 |
kumaneko |
512 |
if (CheckCapabilityFlags(index) != 1) return 0; |
101 |
|
|
return CheckDomainQuota(domain); |
102 |
kumaneko |
111 |
} |
103 |
|
|
|
104 |
kumaneko |
214 |
static struct profile *FindOrAssignNewProfile(const unsigned int profile) |
105 |
kumaneko |
111 |
{ |
106 |
kumaneko |
652 |
static DEFINE_MUTEX(profile_lock); |
107 |
kumaneko |
214 |
struct profile *ptr = NULL; |
108 |
kumaneko |
652 |
mutex_lock(&profile_lock); |
109 |
kumaneko |
111 |
if (profile < MAX_PROFILES && (ptr = profile_ptr[profile]) == NULL) { |
110 |
kumaneko |
214 |
if ((ptr = alloc_element(sizeof(*ptr))) != NULL) { |
111 |
kumaneko |
111 |
int i; |
112 |
|
|
for (i = 0; i < TOMOYO_MAX_CAPABILITY_INDEX; i++) ptr->value[i] = capability_control_array[i].current_value; |
113 |
kumaneko |
708 |
mb(); /* Avoid out-of-order execution. */ |
114 |
kumaneko |
111 |
profile_ptr[profile] = ptr; |
115 |
|
|
} |
116 |
|
|
} |
117 |
kumaneko |
652 |
mutex_unlock(&profile_lock); |
118 |
kumaneko |
111 |
return ptr; |
119 |
|
|
} |
120 |
|
|
|
121 |
|
|
int SetCapabilityStatus(const char *data, unsigned int value, const unsigned int profile) |
122 |
|
|
{ |
123 |
|
|
int i; |
124 |
kumaneko |
214 |
struct profile *ptr = FindOrAssignNewProfile(profile); |
125 |
kumaneko |
111 |
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 |
kumaneko |
214 |
int ReadCapabilityStatus(struct io_buffer *head) |
136 |
kumaneko |
111 |
{ |
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 |
kumaneko |
214 |
const struct profile *profile = profile_ptr[i]; |
141 |
kumaneko |
111 |
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 |
kumaneko |
621 |
static int AuditCapabilityLog(const unsigned int capability, const bool is_granted) |
151 |
kumaneko |
111 |
{ |
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 |
kumaneko |
621 |
static int AddCapabilityACL(const unsigned int capability, struct domain_info *domain, const struct condition_list *condition, const bool is_delete) |
163 |
kumaneko |
111 |
{ |
164 |
|
|
struct acl_info *ptr; |
165 |
kumaneko |
708 |
struct capability_acl_record *acl; |
166 |
kumaneko |
111 |
int error = -ENOMEM; |
167 |
|
|
const u16 hash = capability; |
168 |
|
|
if (!domain) return -EINVAL; |
169 |
kumaneko |
652 |
mutex_lock(&domain_acl_lock); |
170 |
kumaneko |
514 |
if (!is_delete) { |
171 |
kumaneko |
722 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
172 |
kumaneko |
712 |
acl = container_of(ptr, struct capability_acl_record, head); |
173 |
kumaneko |
708 |
if (ptr->type == TYPE_CAPABILITY_ACL && acl->capability == hash && ptr->cond == condition) { |
174 |
kumaneko |
111 |
ptr->is_deleted = 0; |
175 |
|
|
/* Found. Nothing to do. */ |
176 |
|
|
error = 0; |
177 |
kumaneko |
708 |
goto out; |
178 |
kumaneko |
111 |
} |
179 |
|
|
} |
180 |
kumaneko |
708 |
/* Not found. Append it to the tail. */ |
181 |
|
|
if ((acl = alloc_element(sizeof(*acl))) == NULL) goto out; |
182 |
|
|
acl->head.type = TYPE_CAPABILITY_ACL; |
183 |
|
|
acl->capability = hash; |
184 |
|
|
acl->head.cond = condition; |
185 |
|
|
error = AddDomainACL(domain, &acl->head); |
186 |
kumaneko |
111 |
} else { |
187 |
|
|
error = -ENOENT; |
188 |
kumaneko |
722 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
189 |
kumaneko |
712 |
acl = container_of(ptr, struct capability_acl_record, head); |
190 |
kumaneko |
708 |
if (ptr->type != TYPE_CAPABILITY_ACL || ptr->is_deleted || acl->capability != hash || ptr->cond != condition) continue; |
191 |
kumaneko |
111 |
error = DelDomainACL(ptr); |
192 |
|
|
break; |
193 |
|
|
} |
194 |
|
|
} |
195 |
kumaneko |
708 |
out: ; |
196 |
kumaneko |
652 |
mutex_unlock(&domain_acl_lock); |
197 |
kumaneko |
111 |
return error; |
198 |
|
|
} |
199 |
|
|
|
200 |
|
|
int CheckCapabilityACL(const unsigned int capability) |
201 |
|
|
{ |
202 |
|
|
struct domain_info * const domain = current->domain_info; |
203 |
|
|
struct acl_info *ptr; |
204 |
kumaneko |
621 |
const bool is_enforce = CheckCapabilityEnforce(capability); |
205 |
kumaneko |
111 |
const u16 hash = capability; |
206 |
|
|
if (!CheckCapabilityFlags(capability)) return 0; |
207 |
kumaneko |
722 |
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
208 |
kumaneko |
708 |
struct capability_acl_record *acl; |
209 |
kumaneko |
712 |
acl = container_of(ptr, struct capability_acl_record, head); |
210 |
kumaneko |
708 |
if (ptr->type != TYPE_CAPABILITY_ACL || ptr->is_deleted || acl->capability != hash || CheckCondition(ptr->cond, NULL)) continue; |
211 |
kumaneko |
111 |
AuditCapabilityLog(capability, 1); |
212 |
|
|
return 0; |
213 |
|
|
} |
214 |
|
|
if (TomoyoVerboseMode()) { |
215 |
|
|
printk("TOMOYO-%s: %s denied for %s\n", GetMSG(is_enforce), capability2name(capability), GetLastName(domain)); |
216 |
|
|
} |
217 |
|
|
AuditCapabilityLog(capability, 0); |
218 |
|
|
if (is_enforce) return CheckSupervisor("%s\n" KEYWORD_ALLOW_CAPABILITY "%s\n", domain->domainname->name, capability2keyword(capability)); |
219 |
kumaneko |
514 |
if (CheckCapabilityAccept(capability, domain)) AddCapabilityACL(capability, domain, NULL, 0); |
220 |
kumaneko |
111 |
return 0; |
221 |
|
|
} |
222 |
kumaneko |
223 |
EXPORT_SYMBOL(CheckCapabilityACL); |
223 |
kumaneko |
111 |
|
224 |
kumaneko |
621 |
int AddCapabilityPolicy(char *data, struct domain_info *domain, const struct condition_list *condition, const bool is_delete) |
225 |
kumaneko |
111 |
{ |
226 |
|
|
unsigned int capability; |
227 |
|
|
for (capability = 0; capability < TOMOYO_MAX_CAPABILITY_INDEX; capability++) { |
228 |
|
|
if (strcmp(data, capability_control_array[capability].keyword) == 0) { |
229 |
kumaneko |
514 |
return AddCapabilityACL(capability, domain, condition, is_delete); |
230 |
kumaneko |
111 |
} |
231 |
|
|
} |
232 |
|
|
return -EINVAL; |
233 |
|
|
} |
234 |
|
|
|
235 |
|
|
/***** TOMOYO Linux end. *****/ |