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