source: trunk/packages/xen-common/xen-common/xen/acm/acm_policy.c @ 77

Last change on this file since 77 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 23.6 KB
Line 
1/****************************************************************
2 * acm_policy.c
3 *
4 * Copyright (C) 2005-2007 IBM Corporation
5 *
6 * Author:
7 * Reiner Sailer <sailer@watson.ibm.com>
8 *
9 * Contributors:
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 access control policy management for Xen.
18 *       This interface allows policy tools in authorized
19 *       domains to interact with the Xen access control module
20 *
21 */
22
23#include <xen/config.h>
24#include <xen/errno.h>
25#include <xen/types.h>
26#include <xen/lib.h>
27#include <xen/delay.h>
28#include <xen/sched.h>
29#include <xen/guest_access.h>
30#include <public/xen.h>
31#include <acm/acm_core.h>
32#include <public/acm_ops.h>
33#include <acm/acm_hooks.h>
34#include <acm/acm_endian.h>
35#include <asm/current.h>
36
37static int acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels,
38                                      struct acm_sized_buffer *errors);
39static void acm_doms_change_ssidref(ssidref_t (*translator)
40                                     (const struct acm_ssid_domain *,
41                                      const struct acm_sized_buffer *),
42                                      struct acm_sized_buffer *translation_map);
43static void acm_doms_restore_ssidref(void);
44static ssidref_t oldssid_to_newssid(const struct acm_ssid_domain *,
45                                    const struct acm_sized_buffer *map);
46
47
48int
49acm_set_policy(XEN_GUEST_HANDLE_64(void) buf, u32 buf_size)
50{
51    u8 *policy_buffer = NULL;
52    int ret = -EFAULT;
53 
54    if (buf_size < sizeof(struct acm_policy_buffer))
55        return -EFAULT;
56
57    /* copy buffer from guest domain */
58    if ((policy_buffer = xmalloc_array(u8, buf_size)) == NULL)
59        return -ENOMEM;
60
61    if (copy_from_guest(policy_buffer, buf, buf_size))
62    {
63        printk("%s: Error copying!\n",__func__);
64        goto error_free;
65    }
66    ret = do_acm_set_policy(policy_buffer, buf_size, 0,
67                            NULL, NULL, NULL);
68
69 error_free:
70    xfree(policy_buffer);
71    return ret;
72}
73
74
75/*
76 * Update the policy of the running system by:
77 * - deleting ssidrefs that are not in the new policy anymore
78 *   -> no running domain may use such an ssidref
79 * - assign new ssidrefs to domains based on their old ssidrefs
80 *
81 */
82static int
83_acm_update_policy(void *buf, u32 buf_size, int is_bootpolicy,
84                   struct acm_policy_buffer *pol,
85                   struct acm_sized_buffer *deletions,
86                   struct acm_sized_buffer *ssidchanges,
87                   struct acm_sized_buffer *errors)
88{
89    uint32_t offset, length;
90
91    write_lock(&acm_bin_pol_rwlock);
92
93    /*
94       first some tests to check compatibility of new policy with
95       current state of system/domains
96     */
97
98    /* if ssidrefs are to be deleted, make sure no domain is using them */
99    if (deletions != NULL) {
100        if (acm_check_deleted_ssidrefs(deletions, errors))
101            goto error_lock_free;
102    }
103
104    if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
105        /* assign all running domains new ssidrefs as requested */
106        acm_doms_change_ssidref(oldssid_to_newssid, ssidchanges);
107    }
108
109    /* test primary policy data with the new ssidrefs */
110    offset = be32_to_cpu(pol->primary_buffer_offset);
111    length = be32_to_cpu(pol->secondary_buffer_offset) - offset;
112
113    if ( (offset + length) > buf_size ||
114         acm_primary_ops->test_binary_policy(buf + offset, length,
115                                             is_bootpolicy,
116                                             errors))
117        goto error_lock_free;
118
119    /* test secondary policy data with the new ssidrefs */
120    offset = be32_to_cpu(pol->secondary_buffer_offset);
121    length = be32_to_cpu(pol->len) - offset;
122    if ( (offset + length) > buf_size ||
123         acm_secondary_ops->test_binary_policy(buf + offset, length,
124                                               is_bootpolicy,
125                                               errors)) {
126        goto error_lock_free;
127    }
128
129    /* end of testing --- now real updates */
130
131    offset = be32_to_cpu(pol->policy_reference_offset);
132    length = be32_to_cpu(pol->primary_buffer_offset) - offset;
133
134    /* set label reference name */
135    if ( (offset + length) > buf_size ||
136        acm_set_policy_reference(buf + offset, length) )
137        goto error_lock_free;
138
139    /* set primary policy data */
140    offset = be32_to_cpu(pol->primary_buffer_offset);
141    length = be32_to_cpu(pol->secondary_buffer_offset) - offset;
142
143    if ( acm_primary_ops->set_binary_policy(buf + offset, length) )
144        goto error_lock_free;
145
146    /* set secondary policy data */
147    offset = be32_to_cpu(pol->secondary_buffer_offset);
148    length = be32_to_cpu(pol->len) - offset;
149    if ( acm_secondary_ops->set_binary_policy(buf + offset, length) )
150        goto error_lock_free;
151
152    memcpy(&acm_bin_pol.xml_pol_version,
153           &pol->xml_pol_version,
154           sizeof(acm_bin_pol.xml_pol_version));
155
156    write_unlock(&acm_bin_pol_rwlock);
157    return ACM_OK;
158
159error_lock_free:
160    if ((ssidchanges != NULL) && (ssidchanges->num_items > 0)) {
161        acm_doms_restore_ssidref();
162    }
163    do_chwall_init_state_curr(NULL);
164    write_unlock(&acm_bin_pol_rwlock);
165
166    return -EFAULT;
167}
168
169
170int
171do_acm_set_policy(void *buf, u32 buf_size, int is_bootpolicy,
172                  struct acm_sized_buffer *deletions,
173                  struct acm_sized_buffer *ssidchanges,
174                  struct acm_sized_buffer *errors)
175{
176    struct acm_policy_buffer *pol = (struct acm_policy_buffer *)buf;
177
178    /* some sanity checking */
179    if ((be32_to_cpu(pol->magic) != ACM_MAGIC) ||
180        (buf_size != be32_to_cpu(pol->len)) ||
181        (be32_to_cpu(pol->policy_version) != ACM_POLICY_VERSION))
182    {
183        printk("%s: ERROR in Magic, Version, or buf size.\n", __func__);
184        goto error_free;
185    }
186
187    if (acm_active_security_policy == ACM_POLICY_UNDEFINED) {
188        /* setup the policy with the boot policy */
189        if (acm_init_binary_policy((be32_to_cpu(pol->secondary_policy_code) << 4) |
190                                   be32_to_cpu(pol->primary_policy_code))) {
191            goto error_free;
192        }
193        acm_active_security_policy = (acm_bin_pol.secondary_policy_code << 4) |
194                                      acm_bin_pol.primary_policy_code;
195    }
196
197    /* once acm_active_security_policy is set, it cannot be changed */
198    if ((be32_to_cpu(pol->primary_policy_code) != acm_bin_pol.primary_policy_code) ||
199        (be32_to_cpu(pol->secondary_policy_code) != acm_bin_pol.secondary_policy_code))
200    {
201        printkd("%s: Wrong policy type in boot policy!\n", __func__);
202        goto error_free;
203    }
204
205    return _acm_update_policy(buf, buf_size, is_bootpolicy,
206                              pol,
207                              deletions, ssidchanges,
208                              errors);
209
210 error_free:
211    printk("%s: Error setting policy.\n", __func__);
212    return -EFAULT;
213}
214
215int
216acm_get_policy(XEN_GUEST_HANDLE_64(void) buf, u32 buf_size)
217{ 
218    u8 *policy_buffer;
219    int ret;
220    struct acm_policy_buffer *bin_pol;
221
222    if (buf_size < sizeof(struct acm_policy_buffer))
223        return -EFAULT;
224
225    if ((policy_buffer = xmalloc_array(u8, buf_size)) == NULL)
226        return -ENOMEM;
227
228    read_lock(&acm_bin_pol_rwlock);
229
230    bin_pol = (struct acm_policy_buffer *)policy_buffer;
231    bin_pol->magic = cpu_to_be32(ACM_MAGIC);
232    bin_pol->primary_policy_code = cpu_to_be32(acm_bin_pol.primary_policy_code);
233    bin_pol->secondary_policy_code = cpu_to_be32(acm_bin_pol.secondary_policy_code);
234
235    bin_pol->len = cpu_to_be32(sizeof(struct acm_policy_buffer));
236    bin_pol->policy_reference_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
237    bin_pol->primary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
238    bin_pol->secondary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
239
240    memcpy(&bin_pol->xml_pol_version,
241           &acm_bin_pol.xml_pol_version,
242           sizeof(struct acm_policy_version));
243
244    ret = acm_dump_policy_reference(policy_buffer + be32_to_cpu(bin_pol->policy_reference_offset),
245                                    buf_size - be32_to_cpu(bin_pol->policy_reference_offset));
246    if (ret < 0)
247        goto error_free_unlock;
248
249    bin_pol->len = cpu_to_be32(be32_to_cpu(bin_pol->len) + ret);
250    bin_pol->primary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
251
252    ret = acm_primary_ops->dump_binary_policy (policy_buffer + be32_to_cpu(bin_pol->primary_buffer_offset),
253                                               buf_size - be32_to_cpu(bin_pol->primary_buffer_offset));
254    if (ret < 0)
255        goto error_free_unlock;
256
257    bin_pol->len = cpu_to_be32(be32_to_cpu(bin_pol->len) + ret);
258    bin_pol->secondary_buffer_offset = cpu_to_be32(be32_to_cpu(bin_pol->len));
259
260    ret = acm_secondary_ops->dump_binary_policy(policy_buffer + be32_to_cpu(bin_pol->secondary_buffer_offset),
261                                                buf_size - be32_to_cpu(bin_pol->secondary_buffer_offset));
262    if (ret < 0)
263        goto error_free_unlock;
264
265    bin_pol->len = cpu_to_be32(be32_to_cpu(bin_pol->len) + ret);
266    if (copy_to_guest(buf, policy_buffer, be32_to_cpu(bin_pol->len)))
267        goto error_free_unlock;
268
269    read_unlock(&acm_bin_pol_rwlock);
270    xfree(policy_buffer);
271    return ACM_OK;
272
273 error_free_unlock:
274    read_unlock(&acm_bin_pol_rwlock);
275    printk("%s: Error getting policy.\n", __func__);
276    xfree(policy_buffer);
277    return -EFAULT;
278}
279
280int
281acm_dump_statistics(XEN_GUEST_HANDLE_64(void) buf, u16 buf_size)
282{ 
283    /* send stats to user space */
284    u8 *stats_buffer;
285    int len1, len2;
286    struct acm_stats_buffer acm_stats;
287
288    if ((stats_buffer = xmalloc_array(u8, buf_size)) == NULL)
289        return -ENOMEM;
290
291    read_lock(&acm_bin_pol_rwlock);
292     
293    len1 = acm_primary_ops->dump_statistics(stats_buffer + sizeof(struct acm_stats_buffer),
294                                            buf_size - sizeof(struct acm_stats_buffer));
295    if (len1 < 0)
296        goto error_lock_free;
297     
298    len2 = acm_secondary_ops->dump_statistics(stats_buffer + sizeof(struct acm_stats_buffer) + len1,
299                                              buf_size - sizeof(struct acm_stats_buffer) - len1);
300    if (len2 < 0)
301        goto error_lock_free;
302
303    acm_stats.magic = cpu_to_be32(ACM_MAGIC);
304    acm_stats.primary_policy_code = cpu_to_be32(acm_bin_pol.primary_policy_code);
305    acm_stats.secondary_policy_code = cpu_to_be32(acm_bin_pol.secondary_policy_code);
306    acm_stats.primary_stats_offset = cpu_to_be32(sizeof(struct acm_stats_buffer));
307    acm_stats.secondary_stats_offset = cpu_to_be32(sizeof(struct acm_stats_buffer) + len1);
308    acm_stats.len = cpu_to_be32(sizeof(struct acm_stats_buffer) + len1 + len2);
309
310    memcpy(stats_buffer, &acm_stats, sizeof(struct acm_stats_buffer));
311
312    if (copy_to_guest(buf, stats_buffer, sizeof(struct acm_stats_buffer) + len1 + len2))
313        goto error_lock_free;
314
315    read_unlock(&acm_bin_pol_rwlock);
316    xfree(stats_buffer);
317    return ACM_OK;
318
319 error_lock_free:
320    read_unlock(&acm_bin_pol_rwlock);
321    xfree(stats_buffer);
322    return -EFAULT;
323}
324
325
326int
327acm_get_ssid(ssidref_t ssidref, XEN_GUEST_HANDLE_64(void) buf, u16 buf_size)
328{
329    /* send stats to user space */
330    u8 *ssid_buffer;
331    int ret;
332    struct acm_ssid_buffer *acm_ssid;
333    if (buf_size < sizeof(struct acm_ssid_buffer))
334        return -EFAULT;
335
336    if ((ssid_buffer = xmalloc_array(u8, buf_size)) == NULL)
337        return -ENOMEM;
338
339    read_lock(&acm_bin_pol_rwlock);
340
341    acm_ssid = (struct acm_ssid_buffer *)ssid_buffer;
342    acm_ssid->len = sizeof(struct acm_ssid_buffer);
343    acm_ssid->ssidref = ssidref;
344    acm_ssid->primary_policy_code = acm_bin_pol.primary_policy_code;
345    acm_ssid->secondary_policy_code = acm_bin_pol.secondary_policy_code;
346
347    acm_ssid->policy_reference_offset = acm_ssid->len;
348    ret = acm_dump_policy_reference(ssid_buffer + acm_ssid->policy_reference_offset,
349                                    buf_size - acm_ssid->policy_reference_offset);
350    if (ret < 0)
351        goto error_free_unlock;
352
353    acm_ssid->len += ret;
354    acm_ssid->primary_types_offset = acm_ssid->len;
355
356    /* ret >= 0 --> ret == max_types */
357    ret = acm_primary_ops->dump_ssid_types(ACM_PRIMARY(ssidref),
358                                           ssid_buffer + acm_ssid->primary_types_offset,
359                                           buf_size - acm_ssid->primary_types_offset);
360    if (ret < 0)
361        goto error_free_unlock;
362
363    acm_ssid->len += ret;
364    acm_ssid->primary_max_types = ret;
365    acm_ssid->secondary_types_offset = acm_ssid->len;
366
367    ret = acm_secondary_ops->dump_ssid_types(ACM_SECONDARY(ssidref),
368                                             ssid_buffer + acm_ssid->secondary_types_offset,
369                                             buf_size - acm_ssid->secondary_types_offset);
370    if (ret < 0)
371        goto error_free_unlock;
372
373    acm_ssid->len += ret;
374    acm_ssid->secondary_max_types = ret;
375
376    if (copy_to_guest(buf, ssid_buffer, acm_ssid->len))
377        goto error_free_unlock;
378
379    read_unlock(&acm_bin_pol_rwlock);
380    xfree(ssid_buffer);
381    return ACM_OK;
382
383 error_free_unlock:
384    read_unlock(&acm_bin_pol_rwlock);
385    printk("%s: Error getting ssid.\n", __func__);
386    xfree(ssid_buffer);
387    return -ENOMEM;
388}
389
390int
391acm_get_decision(ssidref_t ssidref1, ssidref_t ssidref2, u32 hook)
392{
393    int ret = ACM_ACCESS_DENIED;
394    switch (hook) {
395
396    case ACMHOOK_sharing:
397        /* Sharing hook restricts access in STE policy only */
398        ret = acm_sharing(ssidref1, ssidref2);
399        break;
400
401    default:
402        /* deny */
403        break;
404    }
405
406    printkd("%s: ssid1=%x, ssid2=%x, decision=%s.\n",
407            __func__, ssidref1, ssidref2,
408            (ret == ACM_ACCESS_PERMITTED) ? "GRANTED" : "DENIED");
409
410    return ret;
411}
412
413
414
415/*
416   Check if an ssidref of the current policy type is being used by any
417   domain.
418 */
419static int
420acm_check_used_ssidref(uint32_t policy_type, uint32_t search_ssidref,
421                       struct acm_sized_buffer *errors)
422{
423    int rc = 0;
424    struct acm_ssid_domain *rawssid;
425
426    read_lock(&ssid_list_rwlock);
427
428    for_each_acmssid( rawssid ) {
429        ssidref_t ssidref;
430        void *s = GET_SSIDP(policy_type, rawssid);
431
432        if (policy_type == ACM_CHINESE_WALL_POLICY) {
433            ssidref = ((struct chwall_ssid *)s)->chwall_ssidref;
434        } else {
435            ssidref = ((struct ste_ssid *)s)->ste_ssidref;
436        }
437        gdprintk(XENLOG_INFO,"domid=%d: search ssidref=%d, ssidref=%d\n",
438                 rawssid->domainid,search_ssidref,ssidref);
439        if (ssidref == search_ssidref) {
440            /* one is enough */
441            acm_array_append_tuple(errors, ACM_SSIDREF_IN_USE, search_ssidref);
442            rc = 1;
443            break;
444        }
445    }
446
447    read_unlock(&ssid_list_rwlock);
448
449    return rc;
450}
451
452
453/*
454 * Translate a current ssidref into its future representation under
455 * the new policy.
456 * The map provides translation of ssidrefs from old to new in tuples
457 * of (old ssidref, new ssidref).
458 */
459static ssidref_t
460oldssid_to_newssid(const struct acm_ssid_domain *rawssid,
461                   const struct acm_sized_buffer *map)
462{
463    uint i;
464
465    if (rawssid != NULL) {
466        ssidref_t ssid = rawssid->ssidref & 0xffff;
467        for (i = 0; i+1 < map->num_items; i += 2) {
468            if (map->array[i] == ssid) {
469                return (map->array[i+1] << 16 | map->array[i+1]);
470            }
471        }
472    }
473    return ACM_INVALID_SSIDREF;
474}
475
476
477/*
478 * Assign an ssidref to the CHWALL policy component of the domain
479 */
480static void
481acm_pri_policy_assign_ssidref(struct acm_ssid_domain *rawssid, ssidref_t new_ssid)
482{
483    struct chwall_ssid *chwall = (struct chwall_ssid *)rawssid->primary_ssid;
484    chwall->chwall_ssidref = new_ssid;
485}
486
487
488/*
489 * Assign an ssidref to the STE policy component of the domain
490 */
491static void
492acm_sec_policy_assign_ssidref(struct acm_ssid_domain *rawssid, ssidref_t new_ssid)
493{
494    struct ste_ssid *ste = (struct ste_ssid *)rawssid->secondary_ssid;
495    ste->ste_ssidref = new_ssid;
496}
497
498/*
499   Change the ssidrefs on each domain using a passed translation function;
500 */
501static void
502acm_doms_change_ssidref(ssidref_t (*translator_fn)
503                          (const struct acm_ssid_domain *,
504                           const struct acm_sized_buffer *),
505                        struct acm_sized_buffer *translation_map)
506{
507    struct acm_ssid_domain *rawssid;
508
509    write_lock(&ssid_list_rwlock);
510
511    for_each_acmssid( rawssid ) {
512        ssidref_t new_ssid;
513
514        rawssid->old_ssidref = rawssid->ssidref;
515
516        new_ssid = translator_fn(rawssid, translation_map);
517        if (new_ssid == ACM_INVALID_SSIDREF) {
518            /* means no mapping found, so no change -- old = new */
519            continue;
520        }
521
522        acm_pri_policy_assign_ssidref(rawssid, ACM_PRIMARY  (new_ssid) );
523        acm_sec_policy_assign_ssidref(rawssid, ACM_SECONDARY(new_ssid) );
524
525        rawssid->ssidref = new_ssid;
526    }
527
528    write_unlock(&ssid_list_rwlock);
529}
530
531/*
532 * Restore the previous ssidref values on all domains
533 */
534static void
535acm_doms_restore_ssidref(void)
536{
537    struct acm_ssid_domain *rawssid;
538
539    write_lock(&ssid_list_rwlock);
540
541    for_each_acmssid( rawssid ) {
542        ssidref_t old_ssid;
543
544        if (rawssid->old_ssidref == rawssid->ssidref)
545            continue;
546
547        old_ssid = rawssid->old_ssidref & 0xffff;
548        rawssid->ssidref = rawssid->old_ssidref;
549
550        acm_pri_policy_assign_ssidref(rawssid, old_ssid);
551        acm_sec_policy_assign_ssidref(rawssid, old_ssid);
552    }
553
554    write_unlock(&ssid_list_rwlock);
555}
556
557
558/*
559   Check the list of domains whether either one of them uses a
560   to-be-deleted ssidref.
561 */
562static int
563acm_check_deleted_ssidrefs(struct acm_sized_buffer *dels,
564                           struct acm_sized_buffer *errors)
565{
566    int rc = 0;
567    uint idx;
568    /* check for running domains that should not be there anymore */
569    for (idx = 0; idx < dels->num_items; idx++) {
570        if (acm_check_used_ssidref(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY,
571                                   dels->array[idx],
572                                   errors) > 0 ||
573            acm_check_used_ssidref(ACM_CHINESE_WALL_POLICY,
574                                   dels->array[idx],
575                                   errors) > 0) {
576            rc = ACM_ERROR;
577            break;
578        }
579    }
580    return rc;
581}
582
583
584/*
585 * Change the policy of the system.
586 */
587int
588acm_change_policy(struct acm_change_policy *chgpolicy)
589{
590    int rc = 0;
591    u8 *binpolicy = NULL;
592    struct acm_sized_buffer dels = {
593        .array = NULL,
594    };
595    struct acm_sized_buffer ssidmap = {
596        .array = NULL,
597    };
598    struct acm_sized_buffer errors = {
599        .array = NULL,
600    };
601
602    gdprintk(XENLOG_INFO, "change policy operation\n");
603
604    if ((chgpolicy->delarray_size > 4096) ||
605        (chgpolicy->chgarray_size > 4096) ||
606        (chgpolicy->errarray_size > 4096)) {
607        return ACM_ERROR;
608    }
609
610    dels.num_items = chgpolicy->delarray_size / sizeof(uint32_t);
611    if (dels.num_items > 0) {
612        dels.array = xmalloc_array(uint32_t, dels.num_items);
613        if (dels.array == NULL) {
614            rc = -ENOMEM;
615            goto acm_chg_policy_exit;
616        }
617    }
618
619    ssidmap.num_items = chgpolicy->chgarray_size / sizeof(uint32_t);
620    if (ssidmap.num_items > 0) {
621        ssidmap.array = xmalloc_array(uint32_t, ssidmap.num_items);
622        if (ssidmap.array == NULL) {
623            rc = -ENOMEM;
624            goto acm_chg_policy_exit;
625        }
626    }
627
628    errors.num_items = chgpolicy->errarray_size / sizeof(uint32_t);
629    if (errors.num_items > 0) {
630        errors.array = xmalloc_array(uint32_t, errors.num_items);
631        if (errors.array == NULL) {
632            rc = -ENOMEM;
633            goto acm_chg_policy_exit;
634        }
635        memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items);
636    }
637
638    binpolicy = xmalloc_array(u8,
639                              chgpolicy->policy_pushcache_size);
640    if (binpolicy == NULL) {
641        rc = -ENOMEM;
642        goto acm_chg_policy_exit;
643    }
644
645    if ( copy_from_guest(dels.array,
646                         chgpolicy->del_array,
647                         chgpolicy->delarray_size) ||
648         copy_from_guest(ssidmap.array,
649                         chgpolicy->chg_array,
650                         chgpolicy->chgarray_size) ||
651         copy_from_guest(binpolicy,
652                         chgpolicy->policy_pushcache,
653                         chgpolicy->policy_pushcache_size )) {
654        rc = -EFAULT;
655        goto acm_chg_policy_exit;
656    }
657
658    rc = do_acm_set_policy(binpolicy,
659                           chgpolicy->policy_pushcache_size,
660                           0,
661                           &dels, &ssidmap, &errors);
662
663    if ( (errors.num_items > 0) &&
664         copy_to_guest(chgpolicy->err_array,
665                       errors.array,
666                       errors.num_items ) ) {
667        rc = -EFAULT;
668        goto acm_chg_policy_exit;
669    }
670
671
672acm_chg_policy_exit:
673    xfree(dels.array);
674    xfree(ssidmap.array);
675    xfree(errors.array);
676    xfree(binpolicy);
677
678    return rc;
679}
680
681
682/*
683 * Lookup the new ssidref given the domain's id.
684 * The translation map provides a list of tuples in the format
685 * (domid, new ssidref).
686 */
687static ssidref_t
688domid_to_newssid(const struct acm_ssid_domain *rawssid,
689                 const struct acm_sized_buffer *map)
690{
691    domid_t domid = rawssid->domainid;
692    uint i;
693    for (i = 0; (i+1) < map->num_items; i += 2) {
694        if (map->array[i] == domid) {
695            return (ssidref_t)map->array[i+1];
696        }
697    }
698    return ACM_INVALID_SSIDREF;
699}
700
701
702int
703do_acm_relabel_doms(struct acm_sized_buffer *relabel_map,
704                    struct acm_sized_buffer *errors)
705{
706    int rc = 0, irc;
707
708    write_lock(&acm_bin_pol_rwlock);
709
710    acm_doms_change_ssidref(domid_to_newssid, relabel_map);
711
712    /* run tests; collect as much error info as possible */
713    irc =  do_chwall_init_state_curr(errors);
714    irc += do_ste_init_state_curr(errors);
715    if (irc != 0) {
716        rc = -EFAULT;
717        goto acm_relabel_doms_lock_err_exit;
718    }
719
720    write_unlock(&acm_bin_pol_rwlock);
721
722    return rc;
723
724acm_relabel_doms_lock_err_exit:
725    /* revert the new ssidref assignment */
726    acm_doms_restore_ssidref();
727    do_chwall_init_state_curr(NULL);
728
729    write_unlock(&acm_bin_pol_rwlock);
730
731    return rc;
732}
733
734
735int
736acm_relabel_domains(struct acm_relabel_doms *relabel)
737{
738    int rc = ACM_OK;
739    struct acm_sized_buffer relabels = {
740        .array = NULL,
741    };
742    struct acm_sized_buffer errors = {
743        .array = NULL,
744    };
745
746    if (relabel->relabel_map_size > 4096) {
747        return ACM_ERROR;
748    }
749
750    relabels.num_items = relabel->relabel_map_size / sizeof(uint32_t);
751    if (relabels.num_items > 0) {
752        relabels.array = xmalloc_array(uint32_t, relabels.num_items);
753        if (relabels.array == NULL) {
754            rc = -ENOMEM;
755            goto acm_relabel_doms_exit;
756        }
757    }
758
759    errors.num_items = relabel->errarray_size / sizeof(uint32_t);
760    if (errors.num_items > 0) {
761        errors.array = xmalloc_array(uint32_t, errors.num_items);
762        if (errors.array == NULL) {
763            rc = -ENOMEM;
764            goto acm_relabel_doms_exit;
765        }
766        memset(errors.array, 0x0, sizeof(uint32_t) * errors.num_items);
767    }
768
769    if ( copy_from_guest(relabels.array,
770                         relabel->relabel_map,
771                         relabel->relabel_map_size) ) {
772        rc = -EFAULT;
773        goto acm_relabel_doms_exit;
774    }
775
776    rc = do_acm_relabel_doms(&relabels, &errors);
777
778    if ( copy_to_guest(relabel->err_array,
779                       errors.array,
780                       errors.num_items ) ) {
781        rc = -EFAULT;
782        goto acm_relabel_doms_exit;
783    }
784
785acm_relabel_doms_exit:
786    xfree(relabels.array);
787    xfree(errors.array);
788    return rc;
789}
790
791/*
792 * Local variables:
793 * mode: C
794 * c-set-style: "BSD"
795 * c-basic-offset: 4
796 * tab-width: 4
797 * indent-tabs-mode: nil
798 * End:
799 */
Note: See TracBrowser for help on using the repository browser.