1 | /**************************************************************** |
---|
2 | * acm_chinesewall_hooks.c |
---|
3 | * |
---|
4 | * Copyright (C) 2005 IBM Corporation |
---|
5 | * |
---|
6 | * Author: |
---|
7 | * Reiner Sailer <sailer@watson.ibm.com> |
---|
8 | * |
---|
9 | * Contributions: |
---|
10 | * Stefan Berger <stefanb@watson.ibm.com> |
---|
11 | * |
---|
12 | * This program is free software; you can redistribute it and/or |
---|
13 | * modify it under the terms of the GNU General Public License as |
---|
14 | * published by the Free Software Foundation, version 2 of the |
---|
15 | * License. |
---|
16 | * |
---|
17 | * sHype Chinese Wall Policy for Xen |
---|
18 | * This code implements the hooks that are called |
---|
19 | * throughout Xen operations and decides authorization |
---|
20 | * based on domain types and Chinese Wall conflict type |
---|
21 | * sets. The CHWALL policy decides if a new domain can be started |
---|
22 | * based on the types of running domains and the type of the |
---|
23 | * new domain to be started. If the new domain's type is in |
---|
24 | * conflict with types of running domains, then this new domain |
---|
25 | * is not allowed to be created. A domain can have multiple types, |
---|
26 | * in which case all types of a new domain must be conflict-free |
---|
27 | * with all types of already running domains. |
---|
28 | * |
---|
29 | * indent -i4 -kr -nut |
---|
30 | * |
---|
31 | */ |
---|
32 | |
---|
33 | #include <xen/config.h> |
---|
34 | #include <xen/errno.h> |
---|
35 | #include <xen/types.h> |
---|
36 | #include <xen/lib.h> |
---|
37 | #include <xen/delay.h> |
---|
38 | #include <xen/sched.h> |
---|
39 | #include <public/acm.h> |
---|
40 | #include <asm/atomic.h> |
---|
41 | #include <acm/acm_core.h> |
---|
42 | #include <acm/acm_hooks.h> |
---|
43 | #include <acm/acm_endian.h> |
---|
44 | #include <acm/acm_core.h> |
---|
45 | |
---|
46 | ssidref_t dom0_chwall_ssidref = 0x0001; |
---|
47 | |
---|
48 | /* local cache structures for chinese wall policy */ |
---|
49 | struct chwall_binary_policy chwall_bin_pol; |
---|
50 | |
---|
51 | /* |
---|
52 | * Initializing chinese wall policy (will be filled by policy partition |
---|
53 | * using setpolicy command) |
---|
54 | */ |
---|
55 | int acm_init_chwall_policy(void) |
---|
56 | { |
---|
57 | /* minimal startup policy; policy write-locked already */ |
---|
58 | chwall_bin_pol.max_types = 1; |
---|
59 | chwall_bin_pol.max_ssidrefs = 1 + dom0_chwall_ssidref; |
---|
60 | chwall_bin_pol.max_conflictsets = 1; |
---|
61 | chwall_bin_pol.ssidrefs = |
---|
62 | (domaintype_t *) xmalloc_array(domaintype_t, |
---|
63 | chwall_bin_pol.max_ssidrefs * |
---|
64 | chwall_bin_pol.max_types); |
---|
65 | chwall_bin_pol.conflict_sets = |
---|
66 | (domaintype_t *) xmalloc_array(domaintype_t, |
---|
67 | chwall_bin_pol.max_conflictsets * |
---|
68 | chwall_bin_pol.max_types); |
---|
69 | chwall_bin_pol.running_types = |
---|
70 | (domaintype_t *) xmalloc_array(domaintype_t, |
---|
71 | chwall_bin_pol.max_types); |
---|
72 | chwall_bin_pol.conflict_aggregate_set = |
---|
73 | (domaintype_t *) xmalloc_array(domaintype_t, |
---|
74 | chwall_bin_pol.max_types); |
---|
75 | |
---|
76 | if ((chwall_bin_pol.conflict_sets == NULL) |
---|
77 | || (chwall_bin_pol.running_types == NULL) |
---|
78 | || (chwall_bin_pol.ssidrefs == NULL) |
---|
79 | || (chwall_bin_pol.conflict_aggregate_set == NULL)) |
---|
80 | return ACM_INIT_SSID_ERROR; |
---|
81 | |
---|
82 | /* initialize state */ |
---|
83 | memset((void *) chwall_bin_pol.ssidrefs, 0, |
---|
84 | chwall_bin_pol.max_ssidrefs * chwall_bin_pol.max_types * |
---|
85 | sizeof(domaintype_t)); |
---|
86 | memset((void *) chwall_bin_pol.conflict_sets, 0, |
---|
87 | chwall_bin_pol.max_conflictsets * chwall_bin_pol.max_types * |
---|
88 | sizeof(domaintype_t)); |
---|
89 | memset((void *) chwall_bin_pol.running_types, 0, |
---|
90 | chwall_bin_pol.max_types * sizeof(domaintype_t)); |
---|
91 | memset((void *) chwall_bin_pol.conflict_aggregate_set, 0, |
---|
92 | chwall_bin_pol.max_types * sizeof(domaintype_t)); |
---|
93 | return ACM_OK; |
---|
94 | } |
---|
95 | |
---|
96 | static int chwall_init_domain_ssid(void **chwall_ssid, ssidref_t ssidref) |
---|
97 | { |
---|
98 | struct chwall_ssid *chwall_ssidp = xmalloc(struct chwall_ssid); |
---|
99 | traceprintk("%s.\n", __func__); |
---|
100 | if (chwall_ssidp == NULL) |
---|
101 | return ACM_INIT_SSID_ERROR; |
---|
102 | |
---|
103 | chwall_ssidp->chwall_ssidref = |
---|
104 | GET_SSIDREF(ACM_CHINESE_WALL_POLICY, ssidref); |
---|
105 | |
---|
106 | if ((chwall_ssidp->chwall_ssidref >= chwall_bin_pol.max_ssidrefs) |
---|
107 | || (chwall_ssidp->chwall_ssidref == ACM_DEFAULT_LOCAL_SSID)) |
---|
108 | { |
---|
109 | printkd("%s: ERROR chwall_ssidref(%x) undefined (>max) or unset (0).\n", |
---|
110 | __func__, chwall_ssidp->chwall_ssidref); |
---|
111 | xfree(chwall_ssidp); |
---|
112 | return ACM_INIT_SSID_ERROR; |
---|
113 | } |
---|
114 | (*chwall_ssid) = chwall_ssidp; |
---|
115 | printkd("%s: determined chwall_ssidref to %x.\n", |
---|
116 | __func__, chwall_ssidp->chwall_ssidref); |
---|
117 | return ACM_OK; |
---|
118 | } |
---|
119 | |
---|
120 | static void chwall_free_domain_ssid(void *chwall_ssid) |
---|
121 | { |
---|
122 | traceprintk("%s.\n", __func__); |
---|
123 | xfree(chwall_ssid); |
---|
124 | return; |
---|
125 | } |
---|
126 | |
---|
127 | |
---|
128 | /* dump chinese wall cache; policy read-locked already */ |
---|
129 | static int chwall_dump_policy(u8 * buf, u32 buf_size) |
---|
130 | { |
---|
131 | struct acm_chwall_policy_buffer *chwall_buf = |
---|
132 | (struct acm_chwall_policy_buffer *) buf; |
---|
133 | int ret = 0; |
---|
134 | |
---|
135 | if (buf_size < sizeof(struct acm_chwall_policy_buffer)) |
---|
136 | return -EINVAL; |
---|
137 | |
---|
138 | chwall_buf->chwall_max_types = cpu_to_be32(chwall_bin_pol.max_types); |
---|
139 | chwall_buf->chwall_max_ssidrefs = cpu_to_be32(chwall_bin_pol.max_ssidrefs); |
---|
140 | chwall_buf->policy_code = cpu_to_be32(ACM_CHINESE_WALL_POLICY); |
---|
141 | chwall_buf->chwall_ssid_offset = |
---|
142 | cpu_to_be32(sizeof(struct acm_chwall_policy_buffer)); |
---|
143 | chwall_buf->chwall_max_conflictsets = |
---|
144 | cpu_to_be32(chwall_bin_pol.max_conflictsets); |
---|
145 | chwall_buf->chwall_conflict_sets_offset = |
---|
146 | cpu_to_be32(be32_to_cpu(chwall_buf->chwall_ssid_offset) + |
---|
147 | sizeof(domaintype_t) * chwall_bin_pol.max_ssidrefs * |
---|
148 | chwall_bin_pol.max_types); |
---|
149 | chwall_buf->chwall_running_types_offset = |
---|
150 | cpu_to_be32(be32_to_cpu(chwall_buf->chwall_conflict_sets_offset) + |
---|
151 | sizeof(domaintype_t) * chwall_bin_pol.max_conflictsets * |
---|
152 | chwall_bin_pol.max_types); |
---|
153 | chwall_buf->chwall_conflict_aggregate_offset = |
---|
154 | cpu_to_be32(be32_to_cpu(chwall_buf->chwall_running_types_offset) + |
---|
155 | sizeof(domaintype_t) * chwall_bin_pol.max_types); |
---|
156 | |
---|
157 | ret = be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset) + |
---|
158 | sizeof(domaintype_t) * chwall_bin_pol.max_types; |
---|
159 | |
---|
160 | ret = (ret + 7) & ~7; |
---|
161 | |
---|
162 | if (buf_size < ret) |
---|
163 | return -EINVAL; |
---|
164 | |
---|
165 | /* now copy buffers over */ |
---|
166 | arrcpy16((u16 *) (buf + be32_to_cpu(chwall_buf->chwall_ssid_offset)), |
---|
167 | chwall_bin_pol.ssidrefs, |
---|
168 | chwall_bin_pol.max_ssidrefs * chwall_bin_pol.max_types); |
---|
169 | |
---|
170 | arrcpy16((u16 *) (buf + |
---|
171 | be32_to_cpu(chwall_buf->chwall_conflict_sets_offset)), |
---|
172 | chwall_bin_pol.conflict_sets, |
---|
173 | chwall_bin_pol.max_conflictsets * chwall_bin_pol.max_types); |
---|
174 | |
---|
175 | arrcpy16((u16 *) (buf + |
---|
176 | be32_to_cpu(chwall_buf->chwall_running_types_offset)), |
---|
177 | chwall_bin_pol.running_types, chwall_bin_pol.max_types); |
---|
178 | |
---|
179 | arrcpy16((u16 *) (buf + |
---|
180 | be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset)), |
---|
181 | chwall_bin_pol.conflict_aggregate_set, |
---|
182 | chwall_bin_pol.max_types); |
---|
183 | return ret; |
---|
184 | } |
---|
185 | |
---|
186 | /* |
---|
187 | * Adapt security state (running_types and conflict_aggregate_set) to all |
---|
188 | * running domains; chwall_init_state is called when a policy is changed |
---|
189 | * to bring the security information into a consistent state and to detect |
---|
190 | * violations (return != 0) from a security point of view, we simulate |
---|
191 | * that all running domains are re-started |
---|
192 | */ |
---|
193 | static int |
---|
194 | chwall_init_state(struct acm_chwall_policy_buffer *chwall_buf, |
---|
195 | domaintype_t * ssidrefs, |
---|
196 | domaintype_t * conflict_sets, |
---|
197 | domaintype_t * running_types, |
---|
198 | domaintype_t * conflict_aggregate_set, |
---|
199 | struct acm_sized_buffer *errors /* may be NULL */) |
---|
200 | { |
---|
201 | int violation = 0, i, j; |
---|
202 | struct chwall_ssid *chwall_ssid; |
---|
203 | ssidref_t chwall_ssidref; |
---|
204 | struct acm_ssid_domain *rawssid; |
---|
205 | |
---|
206 | read_lock(&ssid_list_rwlock); |
---|
207 | |
---|
208 | /* go through all domains and adjust policy as if this domain was started now */ |
---|
209 | for_each_acmssid( rawssid ) |
---|
210 | { |
---|
211 | chwall_ssid = |
---|
212 | GET_SSIDP(ACM_CHINESE_WALL_POLICY, rawssid); |
---|
213 | chwall_ssidref = chwall_ssid->chwall_ssidref; |
---|
214 | traceprintk("%s: validating policy for domain %x (chwall-REF=%x).\n", |
---|
215 | __func__, d->domain_id, chwall_ssidref); |
---|
216 | /* a) adjust types ref-count for running domains */ |
---|
217 | for (i = 0; i < chwall_buf->chwall_max_types; i++) |
---|
218 | running_types[i] += |
---|
219 | ssidrefs[chwall_ssidref * chwall_buf->chwall_max_types + i]; |
---|
220 | |
---|
221 | /* b) check for conflict */ |
---|
222 | for (i = 0; i < chwall_buf->chwall_max_types; i++) |
---|
223 | if (conflict_aggregate_set[i] && |
---|
224 | ssidrefs[chwall_ssidref * chwall_buf->chwall_max_types + i]) |
---|
225 | { |
---|
226 | printk("%s: CHINESE WALL CONFLICT in type %02x.\n", |
---|
227 | __func__, i); |
---|
228 | violation = 1; |
---|
229 | |
---|
230 | acm_array_append_tuple(errors, ACM_CHWALL_CONFLICT, i); |
---|
231 | |
---|
232 | goto out; |
---|
233 | } |
---|
234 | /* set violation and break out of the loop */ |
---|
235 | /* c) adapt conflict aggregate set for this domain (notice conflicts) */ |
---|
236 | for (i = 0; i < chwall_buf->chwall_max_conflictsets; i++) |
---|
237 | { |
---|
238 | int common = 0; |
---|
239 | /* check if conflict_set_i and ssidref have common types */ |
---|
240 | for (j = 0; j < chwall_buf->chwall_max_types; j++) |
---|
241 | if (conflict_sets[i * chwall_buf->chwall_max_types + j] && |
---|
242 | ssidrefs[chwall_ssidref * |
---|
243 | chwall_buf->chwall_max_types + j]) |
---|
244 | { |
---|
245 | common = 1; |
---|
246 | break; |
---|
247 | } |
---|
248 | if (common == 0) |
---|
249 | continue; /* try next conflict set */ |
---|
250 | /* now add types of the conflict set to conflict_aggregate_set (except types in chwall_ssidref) */ |
---|
251 | for (j = 0; j < chwall_buf->chwall_max_types; j++) |
---|
252 | if (conflict_sets[i * chwall_buf->chwall_max_types + j] && |
---|
253 | !ssidrefs[chwall_ssidref * |
---|
254 | chwall_buf->chwall_max_types + j]) |
---|
255 | conflict_aggregate_set[j]++; |
---|
256 | } |
---|
257 | } |
---|
258 | out: |
---|
259 | read_unlock(&ssid_list_rwlock); |
---|
260 | return violation; |
---|
261 | /* returning "violation != 0" means that the currently running set of domains would |
---|
262 | * not be possible if the new policy had been enforced before starting them; for chinese |
---|
263 | * wall, this means that the new policy includes at least one conflict set of which |
---|
264 | * more than one type is currently running */ |
---|
265 | } |
---|
266 | |
---|
267 | |
---|
268 | int |
---|
269 | do_chwall_init_state_curr(struct acm_sized_buffer *errors) |
---|
270 | { |
---|
271 | struct acm_chwall_policy_buffer chwall_buf = { |
---|
272 | /* only these two are important */ |
---|
273 | .chwall_max_types = chwall_bin_pol.max_types, |
---|
274 | .chwall_max_conflictsets = chwall_bin_pol.max_conflictsets, |
---|
275 | }; |
---|
276 | /* reset running_types and aggregate set for recalculation */ |
---|
277 | memset(chwall_bin_pol.running_types, |
---|
278 | 0x0, |
---|
279 | sizeof(domaintype_t) * chwall_bin_pol.max_types); |
---|
280 | memset(chwall_bin_pol.conflict_aggregate_set, |
---|
281 | 0x0, |
---|
282 | sizeof(domaintype_t) * chwall_bin_pol.max_types); |
---|
283 | return chwall_init_state(&chwall_buf, |
---|
284 | chwall_bin_pol.ssidrefs, |
---|
285 | chwall_bin_pol.conflict_sets, |
---|
286 | chwall_bin_pol.running_types, |
---|
287 | chwall_bin_pol.conflict_aggregate_set, |
---|
288 | errors); |
---|
289 | } |
---|
290 | |
---|
291 | /* |
---|
292 | * Attempt to set the policy. This function must be called in test_only |
---|
293 | * mode first to only perform checks. A second call then does the |
---|
294 | * actual changes. |
---|
295 | */ |
---|
296 | static int _chwall_update_policy(u8 *buf, u32 buf_size, int test_only, |
---|
297 | struct acm_sized_buffer *errors) |
---|
298 | { |
---|
299 | int rc = -EFAULT; |
---|
300 | /* policy write-locked already */ |
---|
301 | struct acm_chwall_policy_buffer *chwall_buf = |
---|
302 | (struct acm_chwall_policy_buffer *) buf; |
---|
303 | void *ssids = NULL, *conflict_sets = NULL, *running_types = |
---|
304 | NULL, *conflict_aggregate_set = NULL; |
---|
305 | |
---|
306 | /* 1. allocate new buffers */ |
---|
307 | ssids = |
---|
308 | xmalloc_array(domaintype_t, |
---|
309 | chwall_buf->chwall_max_types * |
---|
310 | chwall_buf->chwall_max_ssidrefs); |
---|
311 | conflict_sets = |
---|
312 | xmalloc_array(domaintype_t, |
---|
313 | chwall_buf->chwall_max_conflictsets * |
---|
314 | chwall_buf->chwall_max_types); |
---|
315 | running_types = |
---|
316 | xmalloc_array(domaintype_t, chwall_buf->chwall_max_types); |
---|
317 | conflict_aggregate_set = |
---|
318 | xmalloc_array(domaintype_t, chwall_buf->chwall_max_types); |
---|
319 | |
---|
320 | if ((ssids == NULL) || (conflict_sets == NULL) |
---|
321 | || (running_types == NULL) || (conflict_aggregate_set == NULL)) |
---|
322 | goto error_free; |
---|
323 | |
---|
324 | /* 2. set new policy */ |
---|
325 | if (chwall_buf->chwall_ssid_offset + sizeof(domaintype_t) * |
---|
326 | chwall_buf->chwall_max_types * chwall_buf->chwall_max_ssidrefs > |
---|
327 | buf_size) |
---|
328 | goto error_free; |
---|
329 | |
---|
330 | arrcpy(ssids, buf + chwall_buf->chwall_ssid_offset, |
---|
331 | sizeof(domaintype_t), |
---|
332 | chwall_buf->chwall_max_types * chwall_buf->chwall_max_ssidrefs); |
---|
333 | |
---|
334 | if (chwall_buf->chwall_conflict_sets_offset + sizeof(domaintype_t) * |
---|
335 | chwall_buf->chwall_max_types * |
---|
336 | chwall_buf->chwall_max_conflictsets > buf_size) |
---|
337 | goto error_free; |
---|
338 | |
---|
339 | arrcpy(conflict_sets, buf + chwall_buf->chwall_conflict_sets_offset, |
---|
340 | sizeof(domaintype_t), |
---|
341 | chwall_buf->chwall_max_types * |
---|
342 | chwall_buf->chwall_max_conflictsets); |
---|
343 | |
---|
344 | /* we also use new state buffers since max_types can change */ |
---|
345 | memset(running_types, 0, |
---|
346 | sizeof(domaintype_t) * chwall_buf->chwall_max_types); |
---|
347 | memset(conflict_aggregate_set, 0, |
---|
348 | sizeof(domaintype_t) * chwall_buf->chwall_max_types); |
---|
349 | |
---|
350 | /* 3. now re-calculate the state for the new policy based on running domains; |
---|
351 | * this can fail if new policy is conflicting with running domains */ |
---|
352 | if (chwall_init_state(chwall_buf, ssids, |
---|
353 | conflict_sets, running_types, |
---|
354 | conflict_aggregate_set, |
---|
355 | errors)) |
---|
356 | { |
---|
357 | printk("%s: New policy conflicts with running domains. Policy load aborted.\n", |
---|
358 | __func__); |
---|
359 | goto error_free; /* new policy conflicts with running domains */ |
---|
360 | } |
---|
361 | |
---|
362 | /* if this was only a test run, exit with ACM_OK */ |
---|
363 | if (test_only) { |
---|
364 | rc = ACM_OK; |
---|
365 | goto error_free; |
---|
366 | } |
---|
367 | |
---|
368 | /* 4. free old policy buffers, replace with new ones */ |
---|
369 | chwall_bin_pol.max_types = chwall_buf->chwall_max_types; |
---|
370 | chwall_bin_pol.max_ssidrefs = chwall_buf->chwall_max_ssidrefs; |
---|
371 | chwall_bin_pol.max_conflictsets = chwall_buf->chwall_max_conflictsets; |
---|
372 | xfree(chwall_bin_pol.ssidrefs); |
---|
373 | xfree(chwall_bin_pol.conflict_aggregate_set); |
---|
374 | xfree(chwall_bin_pol.running_types); |
---|
375 | xfree(chwall_bin_pol.conflict_sets); |
---|
376 | chwall_bin_pol.ssidrefs = ssids; |
---|
377 | chwall_bin_pol.conflict_aggregate_set = conflict_aggregate_set; |
---|
378 | chwall_bin_pol.running_types = running_types; |
---|
379 | chwall_bin_pol.conflict_sets = conflict_sets; |
---|
380 | return ACM_OK; |
---|
381 | |
---|
382 | error_free: |
---|
383 | if (!test_only) printk("%s: ERROR setting policy.\n", __func__); |
---|
384 | xfree(ssids); |
---|
385 | xfree(conflict_sets); |
---|
386 | xfree(running_types); |
---|
387 | xfree(conflict_aggregate_set); |
---|
388 | return rc; |
---|
389 | } |
---|
390 | |
---|
391 | /* |
---|
392 | * This function MUST be called before the chwall_ste_policy function! |
---|
393 | */ |
---|
394 | static int chwall_test_policy(u8 *buf, u32 buf_size, int is_bootpolicy, |
---|
395 | struct acm_sized_buffer *errors) |
---|
396 | { |
---|
397 | struct acm_chwall_policy_buffer *chwall_buf = |
---|
398 | (struct acm_chwall_policy_buffer *) buf; |
---|
399 | |
---|
400 | if (buf_size < sizeof(struct acm_chwall_policy_buffer)) |
---|
401 | return -EINVAL; |
---|
402 | |
---|
403 | /* rewrite the policy due to endianess */ |
---|
404 | chwall_buf->policy_code = be32_to_cpu(chwall_buf->policy_code); |
---|
405 | chwall_buf->policy_version = be32_to_cpu(chwall_buf->policy_version); |
---|
406 | chwall_buf->chwall_max_types = |
---|
407 | be32_to_cpu(chwall_buf->chwall_max_types); |
---|
408 | chwall_buf->chwall_max_ssidrefs = |
---|
409 | be32_to_cpu(chwall_buf->chwall_max_ssidrefs); |
---|
410 | chwall_buf->chwall_max_conflictsets = |
---|
411 | be32_to_cpu(chwall_buf->chwall_max_conflictsets); |
---|
412 | chwall_buf->chwall_ssid_offset = |
---|
413 | be32_to_cpu(chwall_buf->chwall_ssid_offset); |
---|
414 | chwall_buf->chwall_conflict_sets_offset = |
---|
415 | be32_to_cpu(chwall_buf->chwall_conflict_sets_offset); |
---|
416 | chwall_buf->chwall_running_types_offset = |
---|
417 | be32_to_cpu(chwall_buf->chwall_running_types_offset); |
---|
418 | chwall_buf->chwall_conflict_aggregate_offset = |
---|
419 | be32_to_cpu(chwall_buf->chwall_conflict_aggregate_offset); |
---|
420 | |
---|
421 | /* policy type and version checks */ |
---|
422 | if ((chwall_buf->policy_code != ACM_CHINESE_WALL_POLICY) || |
---|
423 | (chwall_buf->policy_version != ACM_CHWALL_VERSION)) |
---|
424 | return -EINVAL; |
---|
425 | |
---|
426 | /* during boot dom0_chwall_ssidref is set */ |
---|
427 | if (is_bootpolicy && |
---|
428 | (dom0_chwall_ssidref >= chwall_buf->chwall_max_ssidrefs)) |
---|
429 | return -EINVAL; |
---|
430 | |
---|
431 | |
---|
432 | return _chwall_update_policy(buf, buf_size, 1, errors); |
---|
433 | } |
---|
434 | |
---|
435 | static int chwall_set_policy(u8 *buf, u32 buf_size) |
---|
436 | { |
---|
437 | return _chwall_update_policy(buf, buf_size, 0, NULL); |
---|
438 | } |
---|
439 | |
---|
440 | static int chwall_dump_stats(u8 * buf, u16 len) |
---|
441 | { |
---|
442 | /* no stats for Chinese Wall Policy */ |
---|
443 | return 0; |
---|
444 | } |
---|
445 | |
---|
446 | static int chwall_dump_ssid_types(ssidref_t ssidref, u8 * buf, u16 len) |
---|
447 | { |
---|
448 | int i; |
---|
449 | |
---|
450 | /* fill in buffer */ |
---|
451 | if (chwall_bin_pol.max_types > len) |
---|
452 | return -EFAULT; |
---|
453 | |
---|
454 | if (ssidref >= chwall_bin_pol.max_ssidrefs) |
---|
455 | return -EFAULT; |
---|
456 | |
---|
457 | /* read types for chwall ssidref */ |
---|
458 | for (i = 0; i < chwall_bin_pol.max_types; i++) |
---|
459 | { |
---|
460 | if (chwall_bin_pol. |
---|
461 | ssidrefs[ssidref * chwall_bin_pol.max_types + i]) |
---|
462 | buf[i] = 1; |
---|
463 | else |
---|
464 | buf[i] = 0; |
---|
465 | } |
---|
466 | return chwall_bin_pol.max_types; |
---|
467 | } |
---|
468 | |
---|
469 | /*************************** |
---|
470 | * Authorization functions |
---|
471 | ***************************/ |
---|
472 | |
---|
473 | /* -------- DOMAIN OPERATION HOOKS -----------*/ |
---|
474 | |
---|
475 | static int _chwall_pre_domain_create(void *subject_ssid, ssidref_t ssidref) |
---|
476 | { |
---|
477 | ssidref_t chwall_ssidref; |
---|
478 | int i, j; |
---|
479 | traceprintk("%s.\n", __func__); |
---|
480 | |
---|
481 | chwall_ssidref = GET_SSIDREF(ACM_CHINESE_WALL_POLICY, ssidref); |
---|
482 | if (chwall_ssidref == ACM_DEFAULT_LOCAL_SSID) |
---|
483 | { |
---|
484 | printk("%s: ERROR CHWALL SSID is NOT SET but policy enforced.\n", |
---|
485 | __func__); |
---|
486 | return ACM_ACCESS_DENIED; /* catching and indicating config error */ |
---|
487 | } |
---|
488 | if (chwall_ssidref >= chwall_bin_pol.max_ssidrefs) |
---|
489 | { |
---|
490 | printk("%s: ERROR chwall_ssidref > max(%x).\n", |
---|
491 | __func__, chwall_bin_pol.max_ssidrefs - 1); |
---|
492 | return ACM_ACCESS_DENIED; |
---|
493 | } |
---|
494 | /* A: chinese wall check for conflicts */ |
---|
495 | for (i = 0; i < chwall_bin_pol.max_types; i++) |
---|
496 | if (chwall_bin_pol.conflict_aggregate_set[i] && |
---|
497 | chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
498 | chwall_bin_pol.max_types + i]) |
---|
499 | { |
---|
500 | printk("%s: CHINESE WALL CONFLICT in type %02x.\n", __func__, i); |
---|
501 | return ACM_ACCESS_DENIED; |
---|
502 | } |
---|
503 | |
---|
504 | /* B: chinese wall conflict set adjustment (so that other |
---|
505 | * other domains simultaneously created are evaluated against this new set)*/ |
---|
506 | for (i = 0; i < chwall_bin_pol.max_conflictsets; i++) |
---|
507 | { |
---|
508 | int common = 0; |
---|
509 | /* check if conflict_set_i and ssidref have common types */ |
---|
510 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
511 | if (chwall_bin_pol. |
---|
512 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
513 | && chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
514 | chwall_bin_pol.max_types + j]) |
---|
515 | { |
---|
516 | common = 1; |
---|
517 | break; |
---|
518 | } |
---|
519 | if (common == 0) |
---|
520 | continue; /* try next conflict set */ |
---|
521 | /* now add types of the conflict set to conflict_aggregate_set (except types in chwall_ssidref) */ |
---|
522 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
523 | if (chwall_bin_pol. |
---|
524 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
525 | && !chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
526 | chwall_bin_pol.max_types + j]) |
---|
527 | chwall_bin_pol.conflict_aggregate_set[j]++; |
---|
528 | } |
---|
529 | return ACM_ACCESS_PERMITTED; |
---|
530 | } |
---|
531 | |
---|
532 | |
---|
533 | static void _chwall_post_domain_create(domid_t domid, ssidref_t ssidref) |
---|
534 | { |
---|
535 | int i, j; |
---|
536 | ssidref_t chwall_ssidref; |
---|
537 | traceprintk("%s.\n", __func__); |
---|
538 | |
---|
539 | chwall_ssidref = GET_SSIDREF(ACM_CHINESE_WALL_POLICY, ssidref); |
---|
540 | /* adjust types ref-count for running domains */ |
---|
541 | for (i = 0; i < chwall_bin_pol.max_types; i++) |
---|
542 | chwall_bin_pol.running_types[i] += |
---|
543 | chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
544 | chwall_bin_pol.max_types + i]; |
---|
545 | if (domid) |
---|
546 | { |
---|
547 | return; |
---|
548 | } |
---|
549 | /* Xen does not call pre-create hook for DOM0; |
---|
550 | * to consider type conflicts of any domain with DOM0, we need |
---|
551 | * to adjust the conflict_aggregate for DOM0 here the same way it |
---|
552 | * is done for non-DOM0 domains in the pre-hook */ |
---|
553 | printkd("%s: adjusting security state for DOM0 (ssidref=%x, chwall_ssidref=%x).\n", |
---|
554 | __func__, ssidref, chwall_ssidref); |
---|
555 | |
---|
556 | /* chinese wall conflict set adjustment (so that other |
---|
557 | * other domains simultaneously created are evaluated against this new set)*/ |
---|
558 | for (i = 0; i < chwall_bin_pol.max_conflictsets; i++) |
---|
559 | { |
---|
560 | int common = 0; |
---|
561 | /* check if conflict_set_i and ssidref have common types */ |
---|
562 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
563 | if (chwall_bin_pol. |
---|
564 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
565 | && chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
566 | chwall_bin_pol.max_types + j]) |
---|
567 | { |
---|
568 | common = 1; |
---|
569 | break; |
---|
570 | } |
---|
571 | if (common == 0) |
---|
572 | continue; /* try next conflict set */ |
---|
573 | /* now add types of the conflict set to conflict_aggregate_set (except types in chwall_ssidref) */ |
---|
574 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
575 | if (chwall_bin_pol. |
---|
576 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
577 | && !chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
578 | chwall_bin_pol.max_types + j]) |
---|
579 | chwall_bin_pol.conflict_aggregate_set[j]++; |
---|
580 | } |
---|
581 | return; |
---|
582 | } |
---|
583 | |
---|
584 | |
---|
585 | /* |
---|
586 | * To be called when creating a domain. If this call is unsuccessful, |
---|
587 | * no state changes have occurred (adjustments of counters etc.). If it |
---|
588 | * was successful, state was changed and can be undone using |
---|
589 | * chwall_domain_destroy. |
---|
590 | */ |
---|
591 | static int chwall_domain_create(void *subject_ssid, ssidref_t ssidref, |
---|
592 | domid_t domid) |
---|
593 | { |
---|
594 | int rc; |
---|
595 | read_lock(&acm_bin_pol_rwlock); |
---|
596 | rc = _chwall_pre_domain_create(subject_ssid, ssidref); |
---|
597 | if (rc == ACM_ACCESS_PERMITTED) { |
---|
598 | _chwall_post_domain_create(domid, ssidref); |
---|
599 | } |
---|
600 | read_unlock(&acm_bin_pol_rwlock); |
---|
601 | return rc; |
---|
602 | } |
---|
603 | |
---|
604 | /* |
---|
605 | * This function undoes everything a successful call to |
---|
606 | * chwall_domain_create has done. |
---|
607 | */ |
---|
608 | static void chwall_domain_destroy(void *object_ssid, struct domain *d) |
---|
609 | { |
---|
610 | int i, j; |
---|
611 | struct chwall_ssid *chwall_ssidp = GET_SSIDP(ACM_CHINESE_WALL_POLICY, |
---|
612 | (struct acm_ssid_domain *) |
---|
613 | object_ssid); |
---|
614 | ssidref_t chwall_ssidref = chwall_ssidp->chwall_ssidref; |
---|
615 | |
---|
616 | traceprintk("%s.\n", __func__); |
---|
617 | |
---|
618 | read_lock(&acm_bin_pol_rwlock); |
---|
619 | /* adjust running types set */ |
---|
620 | for (i = 0; i < chwall_bin_pol.max_types; i++) |
---|
621 | chwall_bin_pol.running_types[i] -= |
---|
622 | chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
623 | chwall_bin_pol.max_types + i]; |
---|
624 | |
---|
625 | /* roll-back: re-adjust conflicting types aggregate */ |
---|
626 | for (i = 0; i < chwall_bin_pol.max_conflictsets; i++) |
---|
627 | { |
---|
628 | int common = 0; |
---|
629 | /* check if conflict_set_i and ssidref have common types */ |
---|
630 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
631 | if (chwall_bin_pol. |
---|
632 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
633 | && chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
634 | chwall_bin_pol.max_types + j]) |
---|
635 | { |
---|
636 | common = 1; |
---|
637 | break; |
---|
638 | } |
---|
639 | if (common == 0) |
---|
640 | continue; /* try next conflict set, this one does not include any type of chwall_ssidref */ |
---|
641 | /* now add types of the conflict set to conflict_aggregate_set (except types in chwall_ssidref) */ |
---|
642 | for (j = 0; j < chwall_bin_pol.max_types; j++) |
---|
643 | if (chwall_bin_pol. |
---|
644 | conflict_sets[i * chwall_bin_pol.max_types + j] |
---|
645 | && !chwall_bin_pol.ssidrefs[chwall_ssidref * |
---|
646 | chwall_bin_pol.max_types + j]) |
---|
647 | chwall_bin_pol.conflict_aggregate_set[j]--; |
---|
648 | } |
---|
649 | read_unlock(&acm_bin_pol_rwlock); |
---|
650 | return; |
---|
651 | } |
---|
652 | |
---|
653 | struct acm_operations acm_chinesewall_ops = { |
---|
654 | /* policy management services */ |
---|
655 | .init_domain_ssid = chwall_init_domain_ssid, |
---|
656 | .free_domain_ssid = chwall_free_domain_ssid, |
---|
657 | .dump_binary_policy = chwall_dump_policy, |
---|
658 | .test_binary_policy = chwall_test_policy, |
---|
659 | .set_binary_policy = chwall_set_policy, |
---|
660 | .dump_statistics = chwall_dump_stats, |
---|
661 | .dump_ssid_types = chwall_dump_ssid_types, |
---|
662 | /* domain management control hooks */ |
---|
663 | .domain_create = chwall_domain_create, |
---|
664 | .domain_destroy = chwall_domain_destroy, |
---|
665 | /* event channel control hooks */ |
---|
666 | .pre_eventchannel_unbound = NULL, |
---|
667 | .fail_eventchannel_unbound = NULL, |
---|
668 | .pre_eventchannel_interdomain = NULL, |
---|
669 | .fail_eventchannel_interdomain = NULL, |
---|
670 | /* grant table control hooks */ |
---|
671 | .pre_grant_map_ref = NULL, |
---|
672 | .fail_grant_map_ref = NULL, |
---|
673 | .pre_grant_setup = NULL, |
---|
674 | .fail_grant_setup = NULL, |
---|
675 | /* generic domain-requested decision hooks */ |
---|
676 | .sharing = NULL, |
---|
677 | }; |
---|
678 | |
---|
679 | /* |
---|
680 | * Local variables: |
---|
681 | * mode: C |
---|
682 | * c-set-style: "BSD" |
---|
683 | * c-basic-offset: 4 |
---|
684 | * tab-width: 4 |
---|
685 | * indent-tabs-mode: nil |
---|
686 | * End: |
---|
687 | */ |
---|