1 | /**************************************************************** |
---|
2 | * acm_simple_type_enforcement_hooks.c |
---|
3 | * |
---|
4 | * Copyright (C) 2005 IBM Corporation |
---|
5 | * |
---|
6 | * Author: |
---|
7 | * Reiner Sailer <sailer@watson.ibm.com> |
---|
8 | * |
---|
9 | * Contributors: |
---|
10 | * Stefan Berger <stefanb@watson.ibm.com> |
---|
11 | * support for network order binary policies |
---|
12 | * |
---|
13 | * This program is free software; you can redistribute it and/or |
---|
14 | * modify it under the terms of the GNU General Public License as |
---|
15 | * published by the Free Software Foundation, version 2 of the |
---|
16 | * License. |
---|
17 | * |
---|
18 | * sHype Simple Type Enforcement for Xen |
---|
19 | * STE allows to control which domains can setup sharing |
---|
20 | * (eventchannels right now) with which other domains. Hooks |
---|
21 | * are defined and called throughout Xen when domains bind to |
---|
22 | * shared resources (setup eventchannels) and a domain is allowed |
---|
23 | * to setup sharing with another domain if and only if both domains |
---|
24 | * share at least on common type. |
---|
25 | * |
---|
26 | */ |
---|
27 | |
---|
28 | #include <xen/lib.h> |
---|
29 | #include <asm/types.h> |
---|
30 | #include <asm/current.h> |
---|
31 | #include <acm/acm_hooks.h> |
---|
32 | #include <asm/atomic.h> |
---|
33 | #include <acm/acm_endian.h> |
---|
34 | #include <acm/acm_core.h> |
---|
35 | |
---|
36 | ssidref_t dom0_ste_ssidref = 0x0001; |
---|
37 | |
---|
38 | /* local cache structures for STE policy */ |
---|
39 | struct ste_binary_policy ste_bin_pol; |
---|
40 | |
---|
41 | static inline int have_common_type (ssidref_t ref1, ssidref_t ref2) { |
---|
42 | int i; |
---|
43 | for(i=0; i< ste_bin_pol.max_types; i++) |
---|
44 | if ( ste_bin_pol.ssidrefs[ref1*ste_bin_pol.max_types + i] && |
---|
45 | ste_bin_pol.ssidrefs[ref2*ste_bin_pol.max_types + i]) { |
---|
46 | printkd("%s: common type #%02x.\n", __func__, i); |
---|
47 | return 1; |
---|
48 | } |
---|
49 | return 0; |
---|
50 | } |
---|
51 | |
---|
52 | /* Helper function: return = (subj and obj share a common type) */ |
---|
53 | static int share_common_type(struct domain *subj, struct domain *obj) |
---|
54 | { |
---|
55 | ssidref_t ref_s, ref_o; |
---|
56 | int ret; |
---|
57 | |
---|
58 | if ((subj == NULL) || (obj == NULL) || (subj->ssid == NULL) || (obj->ssid == NULL)) |
---|
59 | return 0; |
---|
60 | read_lock(&acm_bin_pol_rwlock); |
---|
61 | /* lookup the policy-local ssids */ |
---|
62 | ref_s = ((struct ste_ssid *)(GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
63 | (struct acm_ssid_domain *)subj->ssid)))->ste_ssidref; |
---|
64 | ref_o = ((struct ste_ssid *)(GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
65 | (struct acm_ssid_domain *)obj->ssid)))->ste_ssidref; |
---|
66 | /* check whether subj and obj share a common ste type */ |
---|
67 | ret = have_common_type(ref_s, ref_o); |
---|
68 | read_unlock(&acm_bin_pol_rwlock); |
---|
69 | return ret; |
---|
70 | } |
---|
71 | |
---|
72 | /* |
---|
73 | * Initializing STE policy (will be filled by policy partition |
---|
74 | * using setpolicy command) |
---|
75 | */ |
---|
76 | int acm_init_ste_policy(void) |
---|
77 | { |
---|
78 | /* minimal startup policy; policy write-locked already */ |
---|
79 | ste_bin_pol.max_types = 1; |
---|
80 | ste_bin_pol.max_ssidrefs = 1 + dom0_ste_ssidref; |
---|
81 | ste_bin_pol.ssidrefs = |
---|
82 | (domaintype_t *)xmalloc_array(domaintype_t, |
---|
83 | ste_bin_pol.max_types * |
---|
84 | ste_bin_pol.max_ssidrefs); |
---|
85 | |
---|
86 | if (ste_bin_pol.ssidrefs == NULL) |
---|
87 | return ACM_INIT_SSID_ERROR; |
---|
88 | |
---|
89 | memset(ste_bin_pol.ssidrefs, 0, sizeof(domaintype_t) * |
---|
90 | ste_bin_pol.max_types * |
---|
91 | ste_bin_pol.max_ssidrefs); |
---|
92 | |
---|
93 | /* initialize state so that dom0 can start up and communicate with itself */ |
---|
94 | ste_bin_pol.ssidrefs[ste_bin_pol.max_types * dom0_ste_ssidref] = 1; |
---|
95 | |
---|
96 | /* init stats */ |
---|
97 | atomic_set(&(ste_bin_pol.ec_eval_count), 0); |
---|
98 | atomic_set(&(ste_bin_pol.ec_denied_count), 0); |
---|
99 | atomic_set(&(ste_bin_pol.ec_cachehit_count), 0); |
---|
100 | atomic_set(&(ste_bin_pol.gt_eval_count), 0); |
---|
101 | atomic_set(&(ste_bin_pol.gt_denied_count), 0); |
---|
102 | atomic_set(&(ste_bin_pol.gt_cachehit_count), 0); |
---|
103 | return ACM_OK; |
---|
104 | } |
---|
105 | |
---|
106 | |
---|
107 | /* ste initialization function hooks */ |
---|
108 | static int |
---|
109 | ste_init_domain_ssid(void **ste_ssid, ssidref_t ssidref) |
---|
110 | { |
---|
111 | int i; |
---|
112 | struct ste_ssid *ste_ssidp = xmalloc(struct ste_ssid); |
---|
113 | traceprintk("%s.\n", __func__); |
---|
114 | |
---|
115 | if (ste_ssidp == NULL) |
---|
116 | return ACM_INIT_SSID_ERROR; |
---|
117 | |
---|
118 | /* get policy-local ssid reference */ |
---|
119 | ste_ssidp->ste_ssidref = GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref); |
---|
120 | if ((ste_ssidp->ste_ssidref >= ste_bin_pol.max_ssidrefs) || |
---|
121 | (ste_ssidp->ste_ssidref == ACM_DEFAULT_LOCAL_SSID)) { |
---|
122 | printkd("%s: ERROR ste_ssidref (%x) undefined or unset (0).\n", |
---|
123 | __func__, ste_ssidp->ste_ssidref); |
---|
124 | xfree(ste_ssidp); |
---|
125 | return ACM_INIT_SSID_ERROR; |
---|
126 | } |
---|
127 | /* clean ste cache */ |
---|
128 | for (i=0; i<ACM_TE_CACHE_SIZE; i++) |
---|
129 | ste_ssidp->ste_cache[i].valid = ACM_STE_free; |
---|
130 | |
---|
131 | (*ste_ssid) = ste_ssidp; |
---|
132 | printkd("%s: determined ste_ssidref to %x.\n", |
---|
133 | __func__, ste_ssidp->ste_ssidref); |
---|
134 | return ACM_OK; |
---|
135 | } |
---|
136 | |
---|
137 | |
---|
138 | static void |
---|
139 | ste_free_domain_ssid(void *ste_ssid) |
---|
140 | { |
---|
141 | traceprintk("%s.\n", __func__); |
---|
142 | xfree(ste_ssid); |
---|
143 | return; |
---|
144 | } |
---|
145 | |
---|
146 | /* dump type enforcement cache; policy read-locked already */ |
---|
147 | static int |
---|
148 | ste_dump_policy(u8 *buf, u32 buf_size) { |
---|
149 | struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf; |
---|
150 | int ret = 0; |
---|
151 | |
---|
152 | if (buf_size < sizeof(struct acm_ste_policy_buffer)) |
---|
153 | return -EINVAL; |
---|
154 | |
---|
155 | ste_buf->ste_max_types = cpu_to_be32(ste_bin_pol.max_types); |
---|
156 | ste_buf->ste_max_ssidrefs = cpu_to_be32(ste_bin_pol.max_ssidrefs); |
---|
157 | ste_buf->policy_code = cpu_to_be32(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY); |
---|
158 | ste_buf->ste_ssid_offset = cpu_to_be32(sizeof(struct acm_ste_policy_buffer)); |
---|
159 | ret = be32_to_cpu(ste_buf->ste_ssid_offset) + |
---|
160 | sizeof(domaintype_t)*ste_bin_pol.max_ssidrefs*ste_bin_pol.max_types; |
---|
161 | |
---|
162 | ret = (ret + 7) & ~7; |
---|
163 | |
---|
164 | if (buf_size < ret) |
---|
165 | return -EINVAL; |
---|
166 | |
---|
167 | /* now copy buffer over */ |
---|
168 | arrcpy(buf + be32_to_cpu(ste_buf->ste_ssid_offset), |
---|
169 | ste_bin_pol.ssidrefs, |
---|
170 | sizeof(domaintype_t), |
---|
171 | ste_bin_pol.max_ssidrefs*ste_bin_pol.max_types); |
---|
172 | |
---|
173 | return ret; |
---|
174 | } |
---|
175 | |
---|
176 | /* ste_init_state is called when a policy is changed to detect violations (return != 0). |
---|
177 | * from a security point of view, we simulate that all running domains are re-started and |
---|
178 | * all sharing decisions are replayed to detect violations or current sharing behavior |
---|
179 | * (right now: event_channels, future: also grant_tables) |
---|
180 | */ |
---|
181 | static int |
---|
182 | ste_init_state(struct acm_sized_buffer *errors) |
---|
183 | { |
---|
184 | int violation = 1; |
---|
185 | struct ste_ssid *ste_ssid, *ste_rssid; |
---|
186 | ssidref_t ste_ssidref, ste_rssidref; |
---|
187 | struct domain *d, *rdom; |
---|
188 | domid_t rdomid; |
---|
189 | struct active_grant_entry *act; |
---|
190 | int port, i; |
---|
191 | |
---|
192 | rcu_read_lock(&domlist_read_lock); |
---|
193 | read_lock(&ssid_list_rwlock); |
---|
194 | /* go through all domains and adjust policy as if this domain was started now */ |
---|
195 | for_each_domain ( d ) |
---|
196 | { |
---|
197 | struct evtchn *ports; |
---|
198 | unsigned int bucket; |
---|
199 | ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
200 | (struct acm_ssid_domain *)d->ssid); |
---|
201 | ste_ssidref = ste_ssid->ste_ssidref; |
---|
202 | traceprintk("%s: validating policy for eventch domain %x (ste-Ref=%x).\n", |
---|
203 | __func__, d->domain_id, ste_ssidref); |
---|
204 | /* a) check for event channel conflicts */ |
---|
205 | for (bucket = 0; bucket < NR_EVTCHN_BUCKETS; bucket++) { |
---|
206 | spin_lock(&d->evtchn_lock); |
---|
207 | ports = d->evtchn[bucket]; |
---|
208 | if (ports == NULL) { |
---|
209 | spin_unlock(&d->evtchn_lock); |
---|
210 | break; |
---|
211 | } |
---|
212 | |
---|
213 | for (port=0; port < EVTCHNS_PER_BUCKET; port++) { |
---|
214 | if (ports[port].state == ECS_INTERDOMAIN) { |
---|
215 | rdom = ports[port].u.interdomain.remote_dom; |
---|
216 | rdomid = rdom->domain_id; |
---|
217 | } else { |
---|
218 | continue; /* port unused */ |
---|
219 | } |
---|
220 | |
---|
221 | /* rdom now has remote domain */ |
---|
222 | ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
223 | (struct acm_ssid_domain *)(rdom->ssid)); |
---|
224 | ste_rssidref = ste_rssid->ste_ssidref; |
---|
225 | traceprintk("%s: eventch: domain %x (ssidref %x) --> " |
---|
226 | "domain %x (rssidref %x) used (port %x).\n", |
---|
227 | __func__, d->domain_id, ste_ssidref, |
---|
228 | rdom->domain_id, ste_rssidref, port); |
---|
229 | /* check whether on subj->ssid, obj->ssid share a common type*/ |
---|
230 | if (!have_common_type(ste_ssidref, ste_rssidref)) { |
---|
231 | printkd("%s: Policy violation in event channel domain " |
---|
232 | "%x -> domain %x.\n", |
---|
233 | __func__, d->domain_id, rdomid); |
---|
234 | spin_unlock(&d->evtchn_lock); |
---|
235 | |
---|
236 | acm_array_append_tuple(errors, |
---|
237 | ACM_EVTCHN_SHARING_VIOLATION, |
---|
238 | d->domain_id << 16 | rdomid); |
---|
239 | goto out; |
---|
240 | } |
---|
241 | } |
---|
242 | spin_unlock(&d->evtchn_lock); |
---|
243 | } |
---|
244 | |
---|
245 | |
---|
246 | /* b) check for grant table conflicts on shared pages */ |
---|
247 | spin_lock(&d->grant_table->lock); |
---|
248 | for ( i = 0; i < nr_active_grant_frames(d->grant_table); i++ ) { |
---|
249 | #define APP (PAGE_SIZE / sizeof(struct active_grant_entry)) |
---|
250 | act = &d->grant_table->active[i/APP][i%APP]; |
---|
251 | if ( act->pin != 0 ) { |
---|
252 | printkd("%s: grant dom (%hu) SHARED (%d) pin (%d) " |
---|
253 | "dom:(%hu) frame:(%lx)\n", |
---|
254 | __func__, d->domain_id, i, act->pin, |
---|
255 | act->domid, (unsigned long)act->frame); |
---|
256 | rdomid = act->domid; |
---|
257 | if ((rdom = rcu_lock_domain_by_id(rdomid)) == NULL) { |
---|
258 | spin_unlock(&d->grant_table->lock); |
---|
259 | printkd("%s: domain not found ERROR!\n", __func__); |
---|
260 | |
---|
261 | acm_array_append_tuple(errors, |
---|
262 | ACM_DOMAIN_LOOKUP, |
---|
263 | rdomid); |
---|
264 | goto out; |
---|
265 | } |
---|
266 | /* rdom now has remote domain */ |
---|
267 | ste_rssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
268 | (struct acm_ssid_domain *)(rdom->ssid)); |
---|
269 | ste_rssidref = ste_rssid->ste_ssidref; |
---|
270 | rcu_unlock_domain(rdom); |
---|
271 | if (!have_common_type(ste_ssidref, ste_rssidref)) { |
---|
272 | spin_unlock(&d->grant_table->lock); |
---|
273 | printkd("%s: Policy violation in grant table " |
---|
274 | "sharing domain %x -> domain %x.\n", |
---|
275 | __func__, d->domain_id, rdomid); |
---|
276 | |
---|
277 | acm_array_append_tuple(errors, |
---|
278 | ACM_GNTTAB_SHARING_VIOLATION, |
---|
279 | d->domain_id << 16 | rdomid); |
---|
280 | goto out; |
---|
281 | } |
---|
282 | } |
---|
283 | } |
---|
284 | spin_unlock(&d->grant_table->lock); |
---|
285 | } |
---|
286 | violation = 0; |
---|
287 | out: |
---|
288 | read_unlock(&ssid_list_rwlock); |
---|
289 | rcu_read_unlock(&domlist_read_lock); |
---|
290 | return violation; |
---|
291 | /* returning "violation != 0" means that existing sharing between domains would not |
---|
292 | * have been allowed if the new policy had been enforced before the sharing; for ste, |
---|
293 | * this means that there are at least 2 domains that have established sharing through |
---|
294 | * event-channels or grant-tables but these two domains don't have no longer a common |
---|
295 | * type in their typesets referenced by their ssidrefs */ |
---|
296 | } |
---|
297 | |
---|
298 | |
---|
299 | /* |
---|
300 | * Call ste_init_state with the current policy. |
---|
301 | */ |
---|
302 | int |
---|
303 | do_ste_init_state_curr(struct acm_sized_buffer *errors) |
---|
304 | { |
---|
305 | return ste_init_state(errors); |
---|
306 | } |
---|
307 | |
---|
308 | |
---|
309 | /* set new policy; policy write-locked already */ |
---|
310 | static int |
---|
311 | _ste_update_policy(u8 *buf, u32 buf_size, int test_only, |
---|
312 | struct acm_sized_buffer *errors) |
---|
313 | { |
---|
314 | int rc = -EFAULT; |
---|
315 | struct acm_ste_policy_buffer *ste_buf = (struct acm_ste_policy_buffer *)buf; |
---|
316 | void *ssidrefsbuf; |
---|
317 | struct ste_ssid *ste_ssid; |
---|
318 | struct acm_ssid_domain *rawssid; |
---|
319 | int i; |
---|
320 | |
---|
321 | |
---|
322 | /* 1. create and copy-in new ssidrefs buffer */ |
---|
323 | ssidrefsbuf = xmalloc_array(u8, sizeof(domaintype_t)*ste_buf->ste_max_types*ste_buf->ste_max_ssidrefs); |
---|
324 | if (ssidrefsbuf == NULL) { |
---|
325 | return -ENOMEM; |
---|
326 | } |
---|
327 | if (ste_buf->ste_ssid_offset + sizeof(domaintype_t) * ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types > buf_size) |
---|
328 | goto error_free; |
---|
329 | |
---|
330 | arrcpy(ssidrefsbuf, |
---|
331 | buf + ste_buf->ste_ssid_offset, |
---|
332 | sizeof(domaintype_t), |
---|
333 | ste_buf->ste_max_ssidrefs*ste_buf->ste_max_types); |
---|
334 | |
---|
335 | |
---|
336 | /* 3. in test mode: re-calculate sharing decisions based on running domains; |
---|
337 | * this can fail if new policy is conflicting with sharing of running domains |
---|
338 | * now: reject violating new policy; future: adjust sharing through revoking sharing */ |
---|
339 | |
---|
340 | if (test_only) { |
---|
341 | /* temporarily replace old policy with new one for the testing */ |
---|
342 | struct ste_binary_policy orig_ste_bin_pol = ste_bin_pol; |
---|
343 | ste_bin_pol.max_types = ste_buf->ste_max_types; |
---|
344 | ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs; |
---|
345 | ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf; |
---|
346 | |
---|
347 | if (ste_init_state(NULL)) { |
---|
348 | /* new policy conflicts with sharing of running domains */ |
---|
349 | printk("%s: New policy conflicts with running domains. " |
---|
350 | "Policy load aborted.\n", __func__); |
---|
351 | } else { |
---|
352 | rc = ACM_OK; |
---|
353 | } |
---|
354 | /* revert changes, no matter whether testing was successful or not */ |
---|
355 | ste_bin_pol = orig_ste_bin_pol; |
---|
356 | goto error_free; |
---|
357 | } |
---|
358 | |
---|
359 | /* 3. replace old policy (activate new policy) */ |
---|
360 | ste_bin_pol.max_types = ste_buf->ste_max_types; |
---|
361 | ste_bin_pol.max_ssidrefs = ste_buf->ste_max_ssidrefs; |
---|
362 | xfree(ste_bin_pol.ssidrefs); |
---|
363 | ste_bin_pol.ssidrefs = (domaintype_t *)ssidrefsbuf; |
---|
364 | |
---|
365 | /* clear all ste caches */ |
---|
366 | read_lock(&ssid_list_rwlock); |
---|
367 | |
---|
368 | for_each_acmssid( rawssid ) { |
---|
369 | ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid); |
---|
370 | for (i=0; i<ACM_TE_CACHE_SIZE; i++) |
---|
371 | ste_ssid->ste_cache[i].valid = ACM_STE_free; |
---|
372 | } |
---|
373 | |
---|
374 | read_unlock(&ssid_list_rwlock); |
---|
375 | |
---|
376 | return ACM_OK; |
---|
377 | |
---|
378 | error_free: |
---|
379 | if (!test_only) printk("%s: ERROR setting policy.\n", __func__); |
---|
380 | xfree(ssidrefsbuf); |
---|
381 | return rc; |
---|
382 | } |
---|
383 | |
---|
384 | static int |
---|
385 | ste_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy, |
---|
386 | struct acm_sized_buffer *errors) |
---|
387 | { |
---|
388 | struct acm_ste_policy_buffer *ste_buf = |
---|
389 | (struct acm_ste_policy_buffer *)buf; |
---|
390 | |
---|
391 | if (buf_size < sizeof(struct acm_ste_policy_buffer)) |
---|
392 | return -EINVAL; |
---|
393 | |
---|
394 | /* Convert endianess of policy */ |
---|
395 | ste_buf->policy_code = be32_to_cpu(ste_buf->policy_code); |
---|
396 | ste_buf->policy_version = be32_to_cpu(ste_buf->policy_version); |
---|
397 | ste_buf->ste_max_types = be32_to_cpu(ste_buf->ste_max_types); |
---|
398 | ste_buf->ste_max_ssidrefs = be32_to_cpu(ste_buf->ste_max_ssidrefs); |
---|
399 | ste_buf->ste_ssid_offset = be32_to_cpu(ste_buf->ste_ssid_offset); |
---|
400 | |
---|
401 | /* policy type and version checks */ |
---|
402 | if ((ste_buf->policy_code != ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY) || |
---|
403 | (ste_buf->policy_version != ACM_STE_VERSION)) |
---|
404 | return -EINVAL; |
---|
405 | |
---|
406 | /* during boot dom0_chwall_ssidref is set */ |
---|
407 | if (is_bootpolicy && (dom0_ste_ssidref >= ste_buf->ste_max_ssidrefs)) |
---|
408 | return -EINVAL; |
---|
409 | |
---|
410 | return _ste_update_policy(buf, buf_size, 1, errors); |
---|
411 | } |
---|
412 | |
---|
413 | static int |
---|
414 | ste_set_policy(u8 *buf, u32 buf_size) |
---|
415 | { |
---|
416 | return _ste_update_policy(buf, buf_size, 0, NULL); |
---|
417 | } |
---|
418 | |
---|
419 | static int |
---|
420 | ste_dump_stats(u8 *buf, u16 buf_len) |
---|
421 | { |
---|
422 | struct acm_ste_stats_buffer stats; |
---|
423 | |
---|
424 | /* now send the hook counts to user space */ |
---|
425 | stats.ec_eval_count = cpu_to_be32(atomic_read(&ste_bin_pol.ec_eval_count)); |
---|
426 | stats.gt_eval_count = cpu_to_be32(atomic_read(&ste_bin_pol.gt_eval_count)); |
---|
427 | stats.ec_denied_count = cpu_to_be32(atomic_read(&ste_bin_pol.ec_denied_count)); |
---|
428 | stats.gt_denied_count = cpu_to_be32(atomic_read(&ste_bin_pol.gt_denied_count)); |
---|
429 | stats.ec_cachehit_count = cpu_to_be32(atomic_read(&ste_bin_pol.ec_cachehit_count)); |
---|
430 | stats.gt_cachehit_count = cpu_to_be32(atomic_read(&ste_bin_pol.gt_cachehit_count)); |
---|
431 | |
---|
432 | if (buf_len < sizeof(struct acm_ste_stats_buffer)) |
---|
433 | return -ENOMEM; |
---|
434 | |
---|
435 | memcpy(buf, &stats, sizeof(struct acm_ste_stats_buffer)); |
---|
436 | return sizeof(struct acm_ste_stats_buffer); |
---|
437 | } |
---|
438 | |
---|
439 | static int |
---|
440 | ste_dump_ssid_types(ssidref_t ssidref, u8 *buf, u16 len) |
---|
441 | { |
---|
442 | int i; |
---|
443 | |
---|
444 | /* fill in buffer */ |
---|
445 | if (ste_bin_pol.max_types > len) |
---|
446 | return -EFAULT; |
---|
447 | |
---|
448 | if (ssidref >= ste_bin_pol.max_ssidrefs) |
---|
449 | return -EFAULT; |
---|
450 | |
---|
451 | /* read types for chwall ssidref */ |
---|
452 | for(i=0; i< ste_bin_pol.max_types; i++) { |
---|
453 | if (ste_bin_pol.ssidrefs[ssidref * ste_bin_pol.max_types + i]) |
---|
454 | buf[i] = 1; |
---|
455 | else |
---|
456 | buf[i] = 0; |
---|
457 | } |
---|
458 | return ste_bin_pol.max_types; |
---|
459 | } |
---|
460 | |
---|
461 | /* we need to go through this before calling the hooks, |
---|
462 | * returns 1 == cache hit */ |
---|
463 | static int inline |
---|
464 | check_cache(struct domain *dom, domid_t rdom) { |
---|
465 | struct ste_ssid *ste_ssid; |
---|
466 | int i; |
---|
467 | |
---|
468 | printkd("checking cache: %x --> %x.\n", dom->domain_id, rdom); |
---|
469 | |
---|
470 | if (dom->ssid == NULL) |
---|
471 | return 0; |
---|
472 | ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
473 | (struct acm_ssid_domain *)(dom->ssid)); |
---|
474 | |
---|
475 | for(i=0; i< ACM_TE_CACHE_SIZE; i++) { |
---|
476 | if ((ste_ssid->ste_cache[i].valid == ACM_STE_valid) && |
---|
477 | (ste_ssid->ste_cache[i].id == rdom)) { |
---|
478 | printkd("cache hit (entry %x, id= %x!\n", i, ste_ssid->ste_cache[i].id); |
---|
479 | return 1; |
---|
480 | } |
---|
481 | } |
---|
482 | return 0; |
---|
483 | } |
---|
484 | |
---|
485 | |
---|
486 | /* we only get here if there is NO entry yet; no duplication check! */ |
---|
487 | static void inline |
---|
488 | cache_result(struct domain *subj, struct domain *obj) { |
---|
489 | struct ste_ssid *ste_ssid; |
---|
490 | int i; |
---|
491 | printkd("caching from doms: %x --> %x.\n", subj->domain_id, obj->domain_id); |
---|
492 | if (subj->ssid == NULL) |
---|
493 | return; |
---|
494 | ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, |
---|
495 | (struct acm_ssid_domain *)(subj)->ssid); |
---|
496 | for(i=0; i< ACM_TE_CACHE_SIZE; i++) |
---|
497 | if (ste_ssid->ste_cache[i].valid == ACM_STE_free) |
---|
498 | break; |
---|
499 | if (i< ACM_TE_CACHE_SIZE) { |
---|
500 | ste_ssid->ste_cache[i].valid = ACM_STE_valid; |
---|
501 | ste_ssid->ste_cache[i].id = obj->domain_id; |
---|
502 | } else |
---|
503 | printk ("Cache of dom %x is full!\n", subj->domain_id); |
---|
504 | } |
---|
505 | |
---|
506 | /* deletes entries for domain 'id' from all caches (re-use) */ |
---|
507 | static void inline |
---|
508 | clean_id_from_cache(domid_t id) |
---|
509 | { |
---|
510 | struct ste_ssid *ste_ssid; |
---|
511 | int i; |
---|
512 | struct acm_ssid_domain *rawssid; |
---|
513 | |
---|
514 | printkd("deleting cache for dom %x.\n", id); |
---|
515 | read_lock(&ssid_list_rwlock); |
---|
516 | /* look through caches of all domains */ |
---|
517 | |
---|
518 | for_each_acmssid ( rawssid ) { |
---|
519 | |
---|
520 | ste_ssid = GET_SSIDP(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, rawssid); |
---|
521 | if (!ste_ssid) { |
---|
522 | printk("%s: deleting ID from cache ERROR (no ste_ssid)!\n", |
---|
523 | __func__); |
---|
524 | goto out; |
---|
525 | } |
---|
526 | for (i=0; i<ACM_TE_CACHE_SIZE; i++) |
---|
527 | if ((ste_ssid->ste_cache[i].valid == ACM_STE_valid) && |
---|
528 | (ste_ssid->ste_cache[i].id == id)) |
---|
529 | ste_ssid->ste_cache[i].valid = ACM_STE_free; |
---|
530 | } |
---|
531 | |
---|
532 | out: |
---|
533 | read_unlock(&ssid_list_rwlock); |
---|
534 | } |
---|
535 | |
---|
536 | /*************************** |
---|
537 | * Authorization functions |
---|
538 | **************************/ |
---|
539 | static int |
---|
540 | ste_pre_domain_create(void *subject_ssid, ssidref_t ssidref) |
---|
541 | { |
---|
542 | /* check for ssidref in range for policy */ |
---|
543 | ssidref_t ste_ssidref; |
---|
544 | traceprintk("%s.\n", __func__); |
---|
545 | |
---|
546 | read_lock(&acm_bin_pol_rwlock); |
---|
547 | ste_ssidref = GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref); |
---|
548 | if (ste_ssidref == ACM_DEFAULT_LOCAL_SSID) { |
---|
549 | printk("%s: ERROR STE SSID is NOT SET but policy enforced.\n", __func__); |
---|
550 | read_unlock(&acm_bin_pol_rwlock); |
---|
551 | return ACM_ACCESS_DENIED; /* catching and indicating config error */ |
---|
552 | } |
---|
553 | if (ste_ssidref >= ste_bin_pol.max_ssidrefs) { |
---|
554 | printk("%s: ERROR ste_ssidref > max(%x).\n", |
---|
555 | __func__, ste_bin_pol.max_ssidrefs-1); |
---|
556 | read_unlock(&acm_bin_pol_rwlock); |
---|
557 | return ACM_ACCESS_DENIED; |
---|
558 | } |
---|
559 | read_unlock(&acm_bin_pol_rwlock); |
---|
560 | return ACM_ACCESS_PERMITTED; |
---|
561 | } |
---|
562 | |
---|
563 | static int |
---|
564 | ste_domain_create(void *subject_ssid, ssidref_t ssidref, domid_t domid) |
---|
565 | { |
---|
566 | return ste_pre_domain_create(subject_ssid, ssidref); |
---|
567 | } |
---|
568 | |
---|
569 | |
---|
570 | static void |
---|
571 | ste_domain_destroy(void *subject_ssid, struct domain *d) |
---|
572 | { |
---|
573 | /* clean all cache entries for destroyed domain (might be re-used) */ |
---|
574 | clean_id_from_cache(d->domain_id); |
---|
575 | } |
---|
576 | |
---|
577 | /* -------- EVENTCHANNEL OPERATIONS -----------*/ |
---|
578 | static int |
---|
579 | ste_pre_eventchannel_unbound(domid_t id1, domid_t id2) { |
---|
580 | struct domain *subj, *obj; |
---|
581 | int ret; |
---|
582 | traceprintk("%s: dom%x-->dom%x.\n", __func__, |
---|
583 | (id1 == DOMID_SELF) ? current->domain->domain_id : id1, |
---|
584 | (id2 == DOMID_SELF) ? current->domain->domain_id : id2); |
---|
585 | |
---|
586 | if (id1 == DOMID_SELF) id1 = current->domain->domain_id; |
---|
587 | if (id2 == DOMID_SELF) id2 = current->domain->domain_id; |
---|
588 | |
---|
589 | subj = rcu_lock_domain_by_id(id1); |
---|
590 | obj = rcu_lock_domain_by_id(id2); |
---|
591 | if ((subj == NULL) || (obj == NULL)) { |
---|
592 | ret = ACM_ACCESS_DENIED; |
---|
593 | goto out; |
---|
594 | } |
---|
595 | /* cache check late */ |
---|
596 | if (check_cache(subj, obj->domain_id)) { |
---|
597 | atomic_inc(&ste_bin_pol.ec_cachehit_count); |
---|
598 | ret = ACM_ACCESS_PERMITTED; |
---|
599 | goto out; |
---|
600 | } |
---|
601 | atomic_inc(&ste_bin_pol.ec_eval_count); |
---|
602 | |
---|
603 | if (share_common_type(subj, obj)) { |
---|
604 | cache_result(subj, obj); |
---|
605 | ret = ACM_ACCESS_PERMITTED; |
---|
606 | } else { |
---|
607 | atomic_inc(&ste_bin_pol.ec_denied_count); |
---|
608 | ret = ACM_ACCESS_DENIED; |
---|
609 | } |
---|
610 | out: |
---|
611 | if (obj != NULL) |
---|
612 | rcu_unlock_domain(obj); |
---|
613 | if (subj != NULL) |
---|
614 | rcu_unlock_domain(subj); |
---|
615 | return ret; |
---|
616 | } |
---|
617 | |
---|
618 | static int |
---|
619 | ste_pre_eventchannel_interdomain(domid_t id) |
---|
620 | { |
---|
621 | struct domain *subj=NULL, *obj=NULL; |
---|
622 | int ret; |
---|
623 | |
---|
624 | traceprintk("%s: dom%x-->dom%x.\n", __func__, |
---|
625 | current->domain->domain_id, |
---|
626 | (id == DOMID_SELF) ? current->domain->domain_id : id); |
---|
627 | |
---|
628 | /* following is a bit longer but ensures that we |
---|
629 | * "put" only domains that we where "find"-ing |
---|
630 | */ |
---|
631 | if (id == DOMID_SELF) id = current->domain->domain_id; |
---|
632 | |
---|
633 | subj = current->domain; |
---|
634 | obj = rcu_lock_domain_by_id(id); |
---|
635 | if (obj == NULL) { |
---|
636 | ret = ACM_ACCESS_DENIED; |
---|
637 | goto out; |
---|
638 | } |
---|
639 | |
---|
640 | /* cache check late, but evtchn is not on performance critical path */ |
---|
641 | if (check_cache(subj, obj->domain_id)) { |
---|
642 | atomic_inc(&ste_bin_pol.ec_cachehit_count); |
---|
643 | ret = ACM_ACCESS_PERMITTED; |
---|
644 | goto out; |
---|
645 | } |
---|
646 | |
---|
647 | atomic_inc(&ste_bin_pol.ec_eval_count); |
---|
648 | |
---|
649 | if (share_common_type(subj, obj)) { |
---|
650 | cache_result(subj, obj); |
---|
651 | ret = ACM_ACCESS_PERMITTED; |
---|
652 | } else { |
---|
653 | atomic_inc(&ste_bin_pol.ec_denied_count); |
---|
654 | ret = ACM_ACCESS_DENIED; |
---|
655 | } |
---|
656 | out: |
---|
657 | if (obj != NULL) |
---|
658 | rcu_unlock_domain(obj); |
---|
659 | return ret; |
---|
660 | } |
---|
661 | |
---|
662 | /* -------- SHARED MEMORY OPERATIONS -----------*/ |
---|
663 | |
---|
664 | static int |
---|
665 | ste_pre_grant_map_ref (domid_t id) { |
---|
666 | struct domain *obj, *subj; |
---|
667 | int ret; |
---|
668 | traceprintk("%s: dom%x-->dom%x.\n", __func__, |
---|
669 | current->domain->domain_id, id); |
---|
670 | |
---|
671 | if (check_cache(current->domain, id)) { |
---|
672 | atomic_inc(&ste_bin_pol.gt_cachehit_count); |
---|
673 | return ACM_ACCESS_PERMITTED; |
---|
674 | } |
---|
675 | atomic_inc(&ste_bin_pol.gt_eval_count); |
---|
676 | subj = current->domain; |
---|
677 | obj = rcu_lock_domain_by_id(id); |
---|
678 | |
---|
679 | if (share_common_type(subj, obj)) { |
---|
680 | cache_result(subj, obj); |
---|
681 | ret = ACM_ACCESS_PERMITTED; |
---|
682 | } else { |
---|
683 | atomic_inc(&ste_bin_pol.gt_denied_count); |
---|
684 | printkd("%s: ACCESS DENIED!\n", __func__); |
---|
685 | ret = ACM_ACCESS_DENIED; |
---|
686 | } |
---|
687 | if (obj != NULL) |
---|
688 | rcu_unlock_domain(obj); |
---|
689 | return ret; |
---|
690 | } |
---|
691 | |
---|
692 | |
---|
693 | /* since setting up grant tables involves some implicit information |
---|
694 | flow from the creating domain to the domain that is setup, we |
---|
695 | check types in addition to the general authorization */ |
---|
696 | static int |
---|
697 | ste_pre_grant_setup (domid_t id) { |
---|
698 | struct domain *obj, *subj; |
---|
699 | int ret; |
---|
700 | traceprintk("%s: dom%x-->dom%x.\n", __func__, |
---|
701 | current->domain->domain_id, id); |
---|
702 | |
---|
703 | if (check_cache(current->domain, id)) { |
---|
704 | atomic_inc(&ste_bin_pol.gt_cachehit_count); |
---|
705 | return ACM_ACCESS_PERMITTED; |
---|
706 | } |
---|
707 | atomic_inc(&ste_bin_pol.gt_eval_count); |
---|
708 | /* a) check authorization (eventually use specific capabilities) */ |
---|
709 | if (!IS_PRIV(current->domain)) { |
---|
710 | printk("%s: Grant table management authorization denied ERROR!\n", __func__); |
---|
711 | return ACM_ACCESS_DENIED; |
---|
712 | } |
---|
713 | /* b) check types */ |
---|
714 | subj = current->domain; |
---|
715 | obj = rcu_lock_domain_by_id(id); |
---|
716 | |
---|
717 | if (share_common_type(subj, obj)) { |
---|
718 | cache_result(subj, obj); |
---|
719 | ret = ACM_ACCESS_PERMITTED; |
---|
720 | } else { |
---|
721 | atomic_inc(&ste_bin_pol.gt_denied_count); |
---|
722 | ret = ACM_ACCESS_DENIED; |
---|
723 | } |
---|
724 | if (obj != NULL) |
---|
725 | rcu_unlock_domain(obj); |
---|
726 | return ret; |
---|
727 | } |
---|
728 | |
---|
729 | /* -------- DOMAIN-Requested Decision hooks -----------*/ |
---|
730 | |
---|
731 | static int |
---|
732 | ste_sharing(ssidref_t ssidref1, ssidref_t ssidref2) { |
---|
733 | if (have_common_type ( |
---|
734 | GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref1), |
---|
735 | GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref2) |
---|
736 | )) |
---|
737 | return ACM_ACCESS_PERMITTED; |
---|
738 | else |
---|
739 | return ACM_ACCESS_DENIED; |
---|
740 | } |
---|
741 | |
---|
742 | |
---|
743 | /* now define the hook structure similarly to LSM */ |
---|
744 | struct acm_operations acm_simple_type_enforcement_ops = { |
---|
745 | |
---|
746 | /* policy management services */ |
---|
747 | .init_domain_ssid = ste_init_domain_ssid, |
---|
748 | .free_domain_ssid = ste_free_domain_ssid, |
---|
749 | .dump_binary_policy = ste_dump_policy, |
---|
750 | .test_binary_policy = ste_test_policy, |
---|
751 | .set_binary_policy = ste_set_policy, |
---|
752 | .dump_statistics = ste_dump_stats, |
---|
753 | .dump_ssid_types = ste_dump_ssid_types, |
---|
754 | |
---|
755 | /* domain management control hooks */ |
---|
756 | .domain_create = ste_domain_create, |
---|
757 | .domain_destroy = ste_domain_destroy, |
---|
758 | |
---|
759 | /* event channel control hooks */ |
---|
760 | .pre_eventchannel_unbound = ste_pre_eventchannel_unbound, |
---|
761 | .fail_eventchannel_unbound = NULL, |
---|
762 | .pre_eventchannel_interdomain = ste_pre_eventchannel_interdomain, |
---|
763 | .fail_eventchannel_interdomain = NULL, |
---|
764 | |
---|
765 | /* grant table control hooks */ |
---|
766 | .pre_grant_map_ref = ste_pre_grant_map_ref, |
---|
767 | .fail_grant_map_ref = NULL, |
---|
768 | .pre_grant_setup = ste_pre_grant_setup, |
---|
769 | .fail_grant_setup = NULL, |
---|
770 | .sharing = ste_sharing, |
---|
771 | }; |
---|
772 | |
---|
773 | /* |
---|
774 | * Local variables: |
---|
775 | * mode: C |
---|
776 | * c-set-style: "BSD" |
---|
777 | * c-basic-offset: 4 |
---|
778 | * tab-width: 4 |
---|
779 | * indent-tabs-mode: nil |
---|
780 | * End: |
---|
781 | */ |
---|