| |
| /* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */ |
| |
| /* |
| * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com> |
| * |
| * Support for enhanced MLS infrastructure. |
| * |
| * Updated: Frank Mayer <mayerf@tresys.com> and Karl MacMillan <kmacmillan@tresys.com> |
| * |
| * Added conditional policy language extensions |
| * |
| * Updated: Joshua Brindle <jbrindle@tresys.com> and Jason Tang <jtang@tresys.org> |
| * |
| * Module writing support |
| * |
| * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. |
| * Copyright (C) 2003-2005 Tresys Technology, LLC |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| #include <assert.h> |
| #include <stdlib.h> |
| |
| #include <sepol/policydb/ebitmap.h> |
| #include <sepol/policydb/avtab.h> |
| #include <sepol/policydb/policydb.h> |
| #include <sepol/policydb/conditional.h> |
| #include <sepol/policydb/expand.h> |
| #include <sepol/policydb/flask.h> |
| |
| #include "debug.h" |
| #include "private.h" |
| #include "mls.h" |
| |
| struct policy_data { |
| struct policy_file *fp; |
| struct policydb *p; |
| }; |
| |
| static int avrule_write_list(avrule_t * avrules, struct policy_file *fp); |
| |
| static int ebitmap_write(ebitmap_t * e, struct policy_file *fp) |
| { |
| ebitmap_node_t *n; |
| uint32_t buf[32], bit, count; |
| uint64_t map; |
| size_t items; |
| |
| buf[0] = cpu_to_le32(MAPSIZE); |
| buf[1] = cpu_to_le32(e->highbit); |
| |
| count = 0; |
| for (n = e->node; n; n = n->next) |
| count++; |
| buf[2] = cpu_to_le32(count); |
| |
| items = put_entry(buf, sizeof(uint32_t), 3, fp); |
| if (items != 3) |
| return POLICYDB_ERROR; |
| |
| for (n = e->node; n; n = n->next) { |
| bit = cpu_to_le32(n->startbit); |
| items = put_entry(&bit, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| map = cpu_to_le64(n->map); |
| items = put_entry(&map, sizeof(uint64_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* Ordering of datums in the original avtab format in the policy file. */ |
| static uint16_t spec_order[] = { |
| AVTAB_ALLOWED, |
| AVTAB_AUDITDENY, |
| AVTAB_AUDITALLOW, |
| AVTAB_TRANSITION, |
| AVTAB_CHANGE, |
| AVTAB_MEMBER |
| }; |
| |
| static int avtab_write_item(policydb_t * p, |
| avtab_ptr_t cur, struct policy_file *fp, |
| unsigned merge, unsigned commit, uint32_t * nel) |
| { |
| avtab_ptr_t node; |
| uint8_t buf8; |
| uint16_t buf16[4]; |
| uint32_t buf32[10], lookup, val; |
| size_t items, items2; |
| unsigned set; |
| unsigned int oldvers = (p->policy_type == POLICY_KERN |
| && p->policyvers < POLICYDB_VERSION_AVTAB); |
| unsigned int i; |
| |
| if (oldvers) { |
| /* Generate the old avtab format. |
| Requires merging similar entries if uncond avtab. */ |
| if (merge) { |
| if (cur->merged) |
| return POLICYDB_SUCCESS; /* already merged by prior merge */ |
| } |
| |
| items = 1; /* item 0 is used for the item count */ |
| val = cur->key.source_type; |
| buf32[items++] = cpu_to_le32(val); |
| val = cur->key.target_type; |
| buf32[items++] = cpu_to_le32(val); |
| val = cur->key.target_class; |
| buf32[items++] = cpu_to_le32(val); |
| |
| val = cur->key.specified & ~AVTAB_ENABLED; |
| if (cur->key.specified & AVTAB_ENABLED) |
| val |= AVTAB_ENABLED_OLD; |
| set = 1; |
| |
| if (merge) { |
| /* Merge specifier values for all similar (av or type) |
| entries that have the same key. */ |
| if (val & AVTAB_AV) |
| lookup = AVTAB_AV; |
| else if (val & AVTAB_TYPE) |
| lookup = AVTAB_TYPE; |
| else |
| return POLICYDB_ERROR; |
| for (node = avtab_search_node_next(cur, lookup); |
| node; |
| node = avtab_search_node_next(node, lookup)) { |
| val |= (node->key.specified & ~AVTAB_ENABLED); |
| set++; |
| if (node->key.specified & AVTAB_ENABLED) |
| val |= AVTAB_ENABLED_OLD; |
| } |
| } |
| |
| if (!(val & (AVTAB_AV | AVTAB_TYPE))) { |
| ERR(fp->handle, "null entry"); |
| return POLICYDB_ERROR; |
| } |
| if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { |
| ERR(fp->handle, "entry has both access " |
| "vectors and types"); |
| return POLICYDB_ERROR; |
| } |
| |
| buf32[items++] = cpu_to_le32(val); |
| |
| if (merge) { |
| /* Include datums for all similar (av or type) |
| entries that have the same key. */ |
| for (i = 0; |
| i < (sizeof(spec_order) / sizeof(spec_order[0])); |
| i++) { |
| if (val & spec_order[i]) { |
| if (cur->key.specified & spec_order[i]) |
| node = cur; |
| else { |
| node = |
| avtab_search_node_next(cur, |
| spec_order |
| [i]); |
| if (nel) |
| (*nel)--; /* one less node */ |
| } |
| |
| if (!node) { |
| ERR(fp->handle, "missing node"); |
| return POLICYDB_ERROR; |
| } |
| buf32[items++] = |
| cpu_to_le32(node->datum.data); |
| set--; |
| node->merged = 1; |
| } |
| } |
| } else { |
| buf32[items++] = cpu_to_le32(cur->datum.data); |
| cur->merged = 1; |
| set--; |
| } |
| |
| if (set) { |
| ERR(fp->handle, "data count wrong"); |
| return POLICYDB_ERROR; |
| } |
| |
| buf32[0] = cpu_to_le32(items - 1); |
| |
| if (commit) { |
| /* Commit this item to the policy file. */ |
| items2 = put_entry(buf32, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* Generate the new avtab format. */ |
| buf16[0] = cpu_to_le16(cur->key.source_type); |
| buf16[1] = cpu_to_le16(cur->key.target_type); |
| buf16[2] = cpu_to_le16(cur->key.target_class); |
| buf16[3] = cpu_to_le16(cur->key.specified); |
| items = put_entry(buf16, sizeof(uint16_t), 4, fp); |
| if (items != 4) |
| return POLICYDB_ERROR; |
| if ((p->policyvers < POLICYDB_VERSION_XPERMS_IOCTL) && |
| (cur->key.specified & AVTAB_XPERMS)) { |
| ERR(fp->handle, "policy version %u does not support ioctl extended" |
| "permissions rules and one was specified", p->policyvers); |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->target_platform != SEPOL_TARGET_SELINUX && |
| (cur->key.specified & AVTAB_XPERMS)) { |
| ERR(fp->handle, "Target platform %s does not support ioctl " |
| "extended permissions rules and one was specified", |
| policydb_target_strings[p->target_platform]); |
| return POLICYDB_ERROR; |
| } |
| |
| if (cur->key.specified & AVTAB_XPERMS) { |
| buf8 = cur->datum.xperms->specified; |
| items = put_entry(&buf8, sizeof(uint8_t),1,fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| buf8 = cur->datum.xperms->driver; |
| items = put_entry(&buf8, sizeof(uint8_t),1,fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (i = 0; i < ARRAY_SIZE(cur->datum.xperms->perms); i++) |
| buf32[i] = cpu_to_le32(cur->datum.xperms->perms[i]); |
| items = put_entry(buf32, sizeof(uint32_t),8,fp); |
| if (items != 8) |
| return POLICYDB_ERROR; |
| } else { |
| buf32[0] = cpu_to_le32(cur->datum.data); |
| items = put_entry(buf32, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static inline void avtab_reset_merged(avtab_t * a) |
| { |
| unsigned int i; |
| avtab_ptr_t cur; |
| for (i = 0; i < a->nslot; i++) { |
| for (cur = a->htable[i]; cur; cur = cur->next) |
| cur->merged = 0; |
| } |
| } |
| |
| static int avtab_write(struct policydb *p, avtab_t * a, struct policy_file *fp) |
| { |
| unsigned int i; |
| int rc; |
| avtab_t expa; |
| avtab_ptr_t cur; |
| uint32_t nel; |
| size_t items; |
| unsigned int oldvers = (p->policy_type == POLICY_KERN |
| && p->policyvers < POLICYDB_VERSION_AVTAB); |
| |
| if (oldvers) { |
| /* Old avtab format. |
| First, we need to expand attributes. Then, we need to |
| merge similar entries, so we need to track merged nodes |
| and compute the final nel. */ |
| if (avtab_init(&expa)) |
| return POLICYDB_ERROR; |
| if (expand_avtab(p, a, &expa)) { |
| rc = -1; |
| goto out; |
| } |
| a = &expa; |
| avtab_reset_merged(a); |
| nel = a->nel; |
| } else { |
| /* New avtab format. nel is good to go. */ |
| nel = cpu_to_le32(a->nel); |
| items = put_entry(&nel, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| |
| for (i = 0; i < a->nslot; i++) { |
| for (cur = a->htable[i]; cur; cur = cur->next) { |
| /* If old format, compute final nel. |
| If new format, write out the items. */ |
| if (avtab_write_item(p, cur, fp, 1, !oldvers, &nel)) { |
| rc = -1; |
| goto out; |
| } |
| } |
| } |
| |
| if (oldvers) { |
| /* Old avtab format. |
| Write the computed nel value, then write the items. */ |
| nel = cpu_to_le32(nel); |
| items = put_entry(&nel, sizeof(uint32_t), 1, fp); |
| if (items != 1) { |
| rc = -1; |
| goto out; |
| } |
| avtab_reset_merged(a); |
| for (i = 0; i < a->nslot; i++) { |
| for (cur = a->htable[i]; cur; cur = cur->next) { |
| if (avtab_write_item(p, cur, fp, 1, 1, NULL)) { |
| rc = -1; |
| goto out; |
| } |
| } |
| } |
| } |
| |
| rc = 0; |
| out: |
| if (oldvers) |
| avtab_destroy(&expa); |
| return rc; |
| } |
| |
| /* |
| * Write a semantic MLS level structure to a policydb binary |
| * representation file. |
| */ |
| static int mls_write_semantic_level_helper(mls_semantic_level_t * l, |
| struct policy_file *fp) |
| { |
| uint32_t buf[2], ncat = 0; |
| size_t items; |
| mls_semantic_cat_t *cat; |
| |
| for (cat = l->cat; cat; cat = cat->next) |
| ncat++; |
| |
| buf[0] = cpu_to_le32(l->sens); |
| buf[1] = cpu_to_le32(ncat); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| |
| for (cat = l->cat; cat; cat = cat->next) { |
| buf[0] = cpu_to_le32(cat->low); |
| buf[1] = cpu_to_le32(cat->high); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* |
| * Read a semantic MLS range structure to a policydb binary |
| * representation file. |
| */ |
| static int mls_write_semantic_range_helper(mls_semantic_range_t * r, |
| struct policy_file *fp) |
| { |
| int rc; |
| |
| rc = mls_write_semantic_level_helper(&r->level[0], fp); |
| if (rc) |
| return rc; |
| |
| rc = mls_write_semantic_level_helper(&r->level[1], fp); |
| |
| return rc; |
| } |
| |
| /* |
| * Write a MLS level structure to a policydb binary |
| * representation file. |
| */ |
| static int mls_write_level(mls_level_t * l, struct policy_file *fp) |
| { |
| uint32_t sens; |
| size_t items; |
| |
| sens = cpu_to_le32(l->sens); |
| items = put_entry(&sens, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| if (ebitmap_write(&l->cat, fp)) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* |
| * Write a MLS range structure to a policydb binary |
| * representation file. |
| */ |
| static int mls_write_range_helper(mls_range_t * r, struct policy_file *fp) |
| { |
| uint32_t buf[3]; |
| size_t items, items2; |
| int eq; |
| |
| eq = mls_level_eq(&r->level[1], &r->level[0]); |
| |
| items = 1; /* item 0 is used for the item count */ |
| buf[items++] = cpu_to_le32(r->level[0].sens); |
| if (!eq) |
| buf[items++] = cpu_to_le32(r->level[1].sens); |
| buf[0] = cpu_to_le32(items - 1); |
| |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| |
| if (ebitmap_write(&r->level[0].cat, fp)) |
| return POLICYDB_ERROR; |
| if (!eq) |
| if (ebitmap_write(&r->level[1].cat, fp)) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int sens_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| level_datum_t *levdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| |
| levdatum = (level_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(levdatum->isalias); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (mls_write_level(levdatum->level, fp)) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int cat_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| cat_datum_t *catdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| |
| catdatum = (cat_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(catdatum->s.value); |
| buf[items++] = cpu_to_le32(catdatum->isalias); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int role_trans_write(policydb_t *p, struct policy_file *fp) |
| { |
| role_trans_t *r = p->role_tr; |
| role_trans_t *tr; |
| uint32_t buf[3]; |
| size_t nel, items; |
| int new_roletr = (p->policy_type == POLICY_KERN && |
| p->policyvers >= POLICYDB_VERSION_ROLETRANS); |
| int warning_issued = 0; |
| |
| nel = 0; |
| for (tr = r; tr; tr = tr->next) |
| if(new_roletr || tr->tclass == SECCLASS_PROCESS) |
| nel++; |
| |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (tr = r; tr; tr = tr->next) { |
| if (!new_roletr && tr->tclass != SECCLASS_PROCESS) { |
| if (!warning_issued) |
| WARN(fp->handle, "Discarding role_transition " |
| "rules for security classes other than " |
| "\"process\""); |
| warning_issued = 1; |
| continue; |
| } |
| buf[0] = cpu_to_le32(tr->role); |
| buf[1] = cpu_to_le32(tr->type); |
| buf[2] = cpu_to_le32(tr->new_role); |
| items = put_entry(buf, sizeof(uint32_t), 3, fp); |
| if (items != 3) |
| return POLICYDB_ERROR; |
| if (new_roletr) { |
| buf[0] = cpu_to_le32(tr->tclass); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int role_allow_write(role_allow_t * r, struct policy_file *fp) |
| { |
| role_allow_t *ra; |
| uint32_t buf[2]; |
| size_t nel, items; |
| |
| nel = 0; |
| for (ra = r; ra; ra = ra->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (ra = r; ra; ra = ra->next) { |
| buf[0] = cpu_to_le32(ra->role); |
| buf[1] = cpu_to_le32(ra->new_role); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int filename_trans_write(filename_trans_t * r, struct policy_file *fp) |
| { |
| filename_trans_t *ft; |
| uint32_t buf[4]; |
| size_t nel, items, len; |
| |
| nel = 0; |
| for (ft = r; ft; ft = ft->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (ft = r; ft; ft = ft->next) { |
| len = strlen(ft->name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(ft->name, sizeof(char), len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| buf[0] = cpu_to_le32(ft->stype); |
| buf[1] = cpu_to_le32(ft->ttype); |
| buf[2] = cpu_to_le32(ft->tclass); |
| buf[3] = cpu_to_le32(ft->otype); |
| items = put_entry(buf, sizeof(uint32_t), 4, fp); |
| if (items != 4) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int role_set_write(role_set_t * x, struct policy_file *fp) |
| { |
| size_t items; |
| uint32_t buf[1]; |
| |
| if (ebitmap_write(&x->roles, fp)) |
| return POLICYDB_ERROR; |
| |
| buf[0] = cpu_to_le32(x->flags); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int type_set_write(type_set_t * x, struct policy_file *fp) |
| { |
| size_t items; |
| uint32_t buf[1]; |
| |
| if (ebitmap_write(&x->types, fp)) |
| return POLICYDB_ERROR; |
| if (ebitmap_write(&x->negset, fp)) |
| return POLICYDB_ERROR; |
| |
| buf[0] = cpu_to_le32(x->flags); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int cond_write_bool(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| cond_bool_datum_t *booldatum; |
| uint32_t buf[3], len; |
| unsigned int items, items2; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| struct policydb *p = pd->p; |
| |
| booldatum = (cond_bool_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(booldatum->s.value); |
| buf[items++] = cpu_to_le32(booldatum->state); |
| buf[items++] = cpu_to_le32(len); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (p->policy_type != POLICY_KERN && |
| p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { |
| buf[0] = cpu_to_le32(booldatum->flags); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* |
| * cond_write_cond_av_list doesn't write out the av_list nodes. |
| * Instead it writes out the key/value pairs from the avtab. This |
| * is necessary because there is no way to uniquely identifying rules |
| * in the avtab so it is not possible to associate individual rules |
| * in the avtab with a conditional without saving them as part of |
| * the conditional. This means that the avtab with the conditional |
| * rules will not be saved but will be rebuilt on policy load. |
| */ |
| static int cond_write_av_list(policydb_t * p, |
| cond_av_list_t * list, struct policy_file *fp) |
| { |
| uint32_t buf[4]; |
| cond_av_list_t *cur_list, *new_list = NULL; |
| avtab_t expa; |
| uint32_t len, items; |
| unsigned int oldvers = (p->policy_type == POLICY_KERN |
| && p->policyvers < POLICYDB_VERSION_AVTAB); |
| int rc = -1; |
| |
| if (oldvers) { |
| if (avtab_init(&expa)) |
| return POLICYDB_ERROR; |
| if (expand_cond_av_list(p, list, &new_list, &expa)) |
| goto out; |
| list = new_list; |
| } |
| |
| len = 0; |
| for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) { |
| if (cur_list->node->parse_context) |
| len++; |
| } |
| |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| goto out; |
| |
| if (len == 0) { |
| rc = 0; |
| goto out; |
| } |
| |
| for (cur_list = list; cur_list != NULL; cur_list = cur_list->next) { |
| if (cur_list->node->parse_context) |
| if (avtab_write_item(p, cur_list->node, fp, 0, 1, NULL)) |
| goto out; |
| } |
| |
| rc = 0; |
| out: |
| if (oldvers) { |
| cond_av_list_destroy(new_list); |
| avtab_destroy(&expa); |
| } |
| |
| return rc; |
| } |
| |
| static int cond_write_node(policydb_t * p, |
| cond_node_t * node, struct policy_file *fp) |
| { |
| cond_expr_t *cur_expr; |
| uint32_t buf[2]; |
| uint32_t items, items2, len; |
| |
| buf[0] = cpu_to_le32(node->cur_state); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| /* expr */ |
| len = 0; |
| for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) |
| len++; |
| |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| for (cur_expr = node->expr; cur_expr != NULL; cur_expr = cur_expr->next) { |
| items = 0; |
| buf[items++] = cpu_to_le32(cur_expr->expr_type); |
| buf[items++] = cpu_to_le32(cur_expr->bool); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policy_type == POLICY_KERN) { |
| if (cond_write_av_list(p, node->true_list, fp) != 0) |
| return POLICYDB_ERROR; |
| if (cond_write_av_list(p, node->false_list, fp) != 0) |
| return POLICYDB_ERROR; |
| } else { |
| if (avrule_write_list(node->avtrue_list, fp)) |
| return POLICYDB_ERROR; |
| if (avrule_write_list(node->avfalse_list, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policy_type != POLICY_KERN && |
| p->policyvers >= MOD_POLICYDB_VERSION_TUNABLE_SEP) { |
| buf[0] = cpu_to_le32(node->flags); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int cond_write_list(policydb_t * p, cond_list_t * list, |
| struct policy_file *fp) |
| { |
| cond_node_t *cur; |
| uint32_t len, items; |
| uint32_t buf[1]; |
| |
| len = 0; |
| for (cur = list; cur != NULL; cur = cur->next) |
| len++; |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| for (cur = list; cur != NULL; cur = cur->next) { |
| if (cond_write_node(p, cur, fp) != 0) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* |
| * Write a security context structure |
| * to a policydb binary representation file. |
| */ |
| static int context_write(struct policydb *p, context_struct_t * c, |
| struct policy_file *fp) |
| { |
| uint32_t buf[32]; |
| size_t items, items2; |
| |
| items = 0; |
| buf[items++] = cpu_to_le32(c->user); |
| buf[items++] = cpu_to_le32(c->role); |
| buf[items++] = cpu_to_le32(c->type); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| if ((p->policyvers >= POLICYDB_VERSION_MLS |
| && p->policy_type == POLICY_KERN) |
| || (p->policyvers >= MOD_POLICYDB_VERSION_MLS |
| && p->policy_type == POLICY_BASE)) |
| if (mls_write_range_helper(&c->range, fp)) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| /* |
| * The following *_write functions are used to |
| * write the symbol data to a policy database |
| * binary representation file. |
| */ |
| |
| static int perm_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| perm_datum_t *perdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| |
| perdatum = (perm_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(perdatum->s.value); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int common_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| common_datum_t *comdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| |
| comdatum = (common_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(comdatum->s.value); |
| buf[items++] = cpu_to_le32(comdatum->permissions.nprim); |
| buf[items++] = cpu_to_le32(comdatum->permissions.table->nel); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (hashtab_map(comdatum->permissions.table, perm_write, pd)) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int write_cons_helper(policydb_t * p, |
| constraint_node_t * node, int allowxtarget, |
| struct policy_file *fp) |
| { |
| constraint_node_t *c; |
| constraint_expr_t *e; |
| uint32_t buf[3], nexpr; |
| int items; |
| |
| for (c = node; c; c = c->next) { |
| nexpr = 0; |
| for (e = c->expr; e; e = e->next) { |
| nexpr++; |
| } |
| buf[0] = cpu_to_le32(c->permissions); |
| buf[1] = cpu_to_le32(nexpr); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| for (e = c->expr; e; e = e->next) { |
| items = 0; |
| buf[0] = cpu_to_le32(e->expr_type); |
| buf[1] = cpu_to_le32(e->attr); |
| buf[2] = cpu_to_le32(e->op); |
| items = put_entry(buf, sizeof(uint32_t), 3, fp); |
| if (items != 3) |
| return POLICYDB_ERROR; |
| |
| switch (e->expr_type) { |
| case CEXPR_NAMES: |
| if (!allowxtarget && (e->attr & CEXPR_XTARGET)) |
| return POLICYDB_ERROR; |
| if (ebitmap_write(&e->names, fp)) { |
| return POLICYDB_ERROR; |
| } |
| if ((p->policy_type != POLICY_KERN && |
| type_set_write(e->type_names, fp)) || |
| (p->policy_type == POLICY_KERN && |
| (p->policyvers >= POLICYDB_VERSION_CONSTRAINT_NAMES) && |
| type_set_write(e->type_names, fp))) { |
| return POLICYDB_ERROR; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int class_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| class_datum_t *cladatum; |
| constraint_node_t *c; |
| uint32_t buf[32], ncons; |
| size_t items, items2, len, len2; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| struct policydb *p = pd->p; |
| |
| cladatum = (class_datum_t *) datum; |
| |
| len = strlen(key); |
| if (cladatum->comkey) |
| len2 = strlen(cladatum->comkey); |
| else |
| len2 = 0; |
| |
| ncons = 0; |
| for (c = cladatum->constraints; c; c = c->next) { |
| ncons++; |
| } |
| |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(len2); |
| buf[items++] = cpu_to_le32(cladatum->s.value); |
| buf[items++] = cpu_to_le32(cladatum->permissions.nprim); |
| if (cladatum->permissions.table) |
| buf[items++] = cpu_to_le32(cladatum->permissions.table->nel); |
| else |
| buf[items++] = 0; |
| buf[items++] = cpu_to_le32(ncons); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (cladatum->comkey) { |
| items = put_entry(cladatum->comkey, 1, len2, fp); |
| if (items != len2) |
| return POLICYDB_ERROR; |
| } |
| if (hashtab_map(cladatum->permissions.table, perm_write, pd)) |
| return POLICYDB_ERROR; |
| |
| if (write_cons_helper(p, cladatum->constraints, 0, fp)) |
| return POLICYDB_ERROR; |
| |
| if ((p->policy_type == POLICY_KERN |
| && p->policyvers >= POLICYDB_VERSION_VALIDATETRANS) |
| || (p->policy_type == POLICY_BASE |
| && p->policyvers >= MOD_POLICYDB_VERSION_VALIDATETRANS)) { |
| /* write out the validatetrans rule */ |
| ncons = 0; |
| for (c = cladatum->validatetrans; c; c = c->next) { |
| ncons++; |
| } |
| buf[0] = cpu_to_le32(ncons); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (write_cons_helper(p, cladatum->validatetrans, 1, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| if ((p->policy_type == POLICY_KERN && |
| p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) || |
| (p->policy_type == POLICY_BASE && |
| p->policyvers >= MOD_POLICYDB_VERSION_NEW_OBJECT_DEFAULTS)) { |
| buf[0] = cpu_to_le32(cladatum->default_user); |
| buf[1] = cpu_to_le32(cladatum->default_role); |
| buf[2] = cpu_to_le32(cladatum->default_range); |
| items = put_entry(buf, sizeof(uint32_t), 3, fp); |
| if (items != 3) |
| return POLICYDB_ERROR; |
| } |
| |
| if ((p->policy_type == POLICY_KERN && |
| p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) || |
| (p->policy_type == POLICY_BASE && |
| p->policyvers >= MOD_POLICYDB_VERSION_DEFAULT_TYPE)) { |
| buf[0] = cpu_to_le32(cladatum->default_type); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int role_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| role_datum_t *role; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| struct policydb *p = pd->p; |
| |
| role = (role_datum_t *) datum; |
| |
| /* |
| * Role attributes are redundant for policy.X, skip them |
| * when writing the roles symbol table. They are also skipped |
| * when pp is downgraded. |
| * |
| * Their numbers would be deducted in policydb_write(). |
| */ |
| if ((role->flavor == ROLE_ATTRIB) && |
| ((p->policy_type == POLICY_KERN) || |
| (p->policy_type != POLICY_KERN && |
| p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB))) |
| return POLICYDB_SUCCESS; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(role->s.value); |
| if (policydb_has_boundary_feature(p)) |
| buf[items++] = cpu_to_le32(role->bounds); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (ebitmap_write(&role->dominates, fp)) |
| return POLICYDB_ERROR; |
| if (p->policy_type == POLICY_KERN) { |
| if (ebitmap_write(&role->types.types, fp)) |
| return POLICYDB_ERROR; |
| } else { |
| if (type_set_write(&role->types, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policy_type != POLICY_KERN && |
| p->policyvers >= MOD_POLICYDB_VERSION_ROLEATTRIB) { |
| buf[0] = cpu_to_le32(role->flavor); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| if (ebitmap_write(&role->roles, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int type_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| type_datum_t *typdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| struct policydb *p = pd->p; |
| |
| typdatum = (type_datum_t *) datum; |
| |
| /* |
| * The kernel policy version less than 24 (= POLICYDB_VERSION_BOUNDARY) |
| * does not support to load entries of attribute, so we skip to write it. |
| */ |
| if (p->policy_type == POLICY_KERN |
| && p->policyvers < POLICYDB_VERSION_BOUNDARY |
| && typdatum->flavor == TYPE_ATTRIB) |
| return POLICYDB_SUCCESS; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(typdatum->s.value); |
| if (policydb_has_boundary_feature(p)) { |
| uint32_t properties = 0; |
| |
| if (p->policy_type != POLICY_KERN |
| && p->policyvers >= MOD_POLICYDB_VERSION_BOUNDARY_ALIAS) { |
| buf[items++] = cpu_to_le32(typdatum->primary); |
| } |
| |
| if (typdatum->primary) |
| properties |= TYPEDATUM_PROPERTY_PRIMARY; |
| |
| if (typdatum->flavor == TYPE_ATTRIB) { |
| properties |= TYPEDATUM_PROPERTY_ATTRIBUTE; |
| } else if (typdatum->flavor == TYPE_ALIAS |
| && p->policy_type != POLICY_KERN) |
| properties |= TYPEDATUM_PROPERTY_ALIAS; |
| |
| if (typdatum->flags & TYPE_FLAGS_PERMISSIVE |
| && p->policy_type != POLICY_KERN) |
| properties |= TYPEDATUM_PROPERTY_PERMISSIVE; |
| |
| buf[items++] = cpu_to_le32(properties); |
| buf[items++] = cpu_to_le32(typdatum->bounds); |
| } else { |
| buf[items++] = cpu_to_le32(typdatum->primary); |
| |
| if (p->policy_type != POLICY_KERN) { |
| buf[items++] = cpu_to_le32(typdatum->flavor); |
| |
| if (p->policyvers >= MOD_POLICYDB_VERSION_PERMISSIVE) |
| buf[items++] = cpu_to_le32(typdatum->flags); |
| else if (typdatum->flags & TYPE_FLAGS_PERMISSIVE) |
| WARN(fp->handle, "Warning! Module policy " |
| "version %d cannot support permissive " |
| "types, but one was defined", |
| p->policyvers); |
| } |
| } |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| if (p->policy_type != POLICY_KERN) { |
| if (ebitmap_write(&typdatum->types, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int user_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| user_datum_t *usrdatum; |
| uint32_t buf[32]; |
| size_t items, items2, len; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| struct policydb *p = pd->p; |
| |
| usrdatum = (user_datum_t *) datum; |
| |
| len = strlen(key); |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| buf[items++] = cpu_to_le32(usrdatum->s.value); |
| if (policydb_has_boundary_feature(p)) |
| buf[items++] = cpu_to_le32(usrdatum->bounds); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(key, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (p->policy_type == POLICY_KERN) { |
| if (ebitmap_write(&usrdatum->roles.roles, fp)) |
| return POLICYDB_ERROR; |
| } else { |
| if (role_set_write(&usrdatum->roles, fp)) |
| return POLICYDB_ERROR; |
| } |
| |
| if ((p->policyvers >= POLICYDB_VERSION_MLS |
| && p->policy_type == POLICY_KERN) |
| || (p->policyvers >= MOD_POLICYDB_VERSION_MLS |
| && p->policyvers < MOD_POLICYDB_VERSION_MLS_USERS |
| && p->policy_type == POLICY_MOD) |
| || (p->policyvers >= MOD_POLICYDB_VERSION_MLS |
| && p->policyvers < MOD_POLICYDB_VERSION_MLS_USERS |
| && p->policy_type == POLICY_BASE)) { |
| if (mls_write_range_helper(&usrdatum->exp_range, fp)) |
| return POLICYDB_ERROR; |
| if (mls_write_level(&usrdatum->exp_dfltlevel, fp)) |
| return POLICYDB_ERROR; |
| } else if ((p->policyvers >= MOD_POLICYDB_VERSION_MLS_USERS |
| && p->policy_type == POLICY_MOD) |
| || (p->policyvers >= MOD_POLICYDB_VERSION_MLS_USERS |
| && p->policy_type == POLICY_BASE)) { |
| if (mls_write_semantic_range_helper(&usrdatum->range, fp)) |
| return -1; |
| if (mls_write_semantic_level_helper(&usrdatum->dfltlevel, fp)) |
| return -1; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int (*write_f[SYM_NUM]) (hashtab_key_t key, hashtab_datum_t datum, |
| void *datap) = { |
| common_write, class_write, role_write, type_write, user_write, |
| cond_write_bool, sens_write, cat_write,}; |
| |
| static int ocontext_write_xen(struct policydb_compat_info *info, policydb_t *p, |
| struct policy_file *fp) |
| { |
| unsigned int i, j; |
| size_t nel, items, len; |
| uint32_t buf[32]; |
| ocontext_t *c; |
| for (i = 0; i < info->ocon_num; i++) { |
| nel = 0; |
| for (c = p->ocontexts[i]; c; c = c->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (c = p->ocontexts[i]; c; c = c->next) { |
| switch (i) { |
| case OCON_XEN_ISID: |
| buf[0] = cpu_to_le32(c->sid[0]); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_XEN_PIRQ: |
| buf[0] = cpu_to_le32(c->u.pirq); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_XEN_IOPORT: |
| buf[0] = c->u.ioport.low_ioport; |
| buf[1] = c->u.ioport.high_ioport; |
| for (j = 0; j < 2; j++) |
| buf[j] = cpu_to_le32(buf[j]); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_XEN_IOMEM: |
| if (p->policyvers >= POLICYDB_VERSION_XEN_DEVICETREE) { |
| uint64_t b64[2]; |
| b64[0] = c->u.iomem.low_iomem; |
| b64[1] = c->u.iomem.high_iomem; |
| for (j = 0; j < 2; j++) |
| b64[j] = cpu_to_le64(b64[j]); |
| items = put_entry(b64, sizeof(uint64_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| } else { |
| if (c->u.iomem.high_iomem > 0xFFFFFFFFULL) { |
| ERR(fp->handle, "policy version %d" |
| " cannot represent IOMEM addresses over 16TB", |
| p->policyvers); |
| return POLICYDB_ERROR; |
| } |
| |
| buf[0] = c->u.iomem.low_iomem; |
| buf[1] = c->u.iomem.high_iomem; |
| for (j = 0; j < 2; j++) |
| buf[j] = cpu_to_le32(buf[j]); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| } |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_XEN_PCIDEVICE: |
| buf[0] = cpu_to_le32(c->u.device); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_XEN_DEVICETREE: |
| len = strlen(c->u.name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(c->u.name, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| } |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int ocontext_write_selinux(struct policydb_compat_info *info, |
| policydb_t *p, struct policy_file *fp) |
| { |
| unsigned int i, j; |
| size_t nel, items, len; |
| uint32_t buf[32]; |
| ocontext_t *c; |
| for (i = 0; i < info->ocon_num; i++) { |
| nel = 0; |
| for (c = p->ocontexts[i]; c; c = c->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (c = p->ocontexts[i]; c; c = c->next) { |
| switch (i) { |
| case OCON_ISID: |
| buf[0] = cpu_to_le32(c->sid[0]); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_FS: |
| case OCON_NETIF: |
| len = strlen(c->u.name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(c->u.name, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[1], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_PORT: |
| buf[0] = c->u.port.protocol; |
| buf[1] = c->u.port.low_port; |
| buf[2] = c->u.port.high_port; |
| for (j = 0; j < 3; j++) { |
| buf[j] = cpu_to_le32(buf[j]); |
| } |
| items = put_entry(buf, sizeof(uint32_t), 3, fp); |
| if (items != 3) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_NODE: |
| buf[0] = c->u.node.addr; /* network order */ |
| buf[1] = c->u.node.mask; /* network order */ |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_FSUSE: |
| buf[0] = cpu_to_le32(c->v.behavior); |
| len = strlen(c->u.name); |
| buf[1] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| items = put_entry(c->u.name, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| case OCON_NODE6: |
| for (j = 0; j < 4; j++) |
| buf[j] = c->u.node6.addr[j]; /* network order */ |
| for (j = 0; j < 4; j++) |
| buf[j + 4] = c->u.node6.mask[j]; /* network order */ |
| items = put_entry(buf, sizeof(uint32_t), 8, fp); |
| if (items != 8) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| break; |
| } |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int ocontext_write(struct policydb_compat_info *info, policydb_t * p, |
| struct policy_file *fp) |
| { |
| int rc = POLICYDB_ERROR; |
| switch (p->target_platform) { |
| case SEPOL_TARGET_SELINUX: |
| rc = ocontext_write_selinux(info, p, fp); |
| break; |
| case SEPOL_TARGET_XEN: |
| rc = ocontext_write_xen(info, p, fp); |
| break; |
| } |
| return rc; |
| } |
| |
| static int genfs_write(policydb_t * p, struct policy_file *fp) |
| { |
| genfs_t *genfs; |
| ocontext_t *c; |
| size_t nel = 0, items, len; |
| uint32_t buf[32]; |
| |
| for (genfs = p->genfs; genfs; genfs = genfs->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (genfs = p->genfs; genfs; genfs = genfs->next) { |
| len = strlen(genfs->fstype); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(genfs->fstype, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| nel = 0; |
| for (c = genfs->head; c; c = c->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (c = genfs->head; c; c = c->next) { |
| len = strlen(c->u.name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(c->u.name, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| buf[0] = cpu_to_le32(c->v.sclass); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| if (context_write(p, &c->context[0], fp)) |
| return POLICYDB_ERROR; |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int range_write(policydb_t * p, struct policy_file *fp) |
| { |
| size_t nel, items; |
| struct range_trans *rt; |
| uint32_t buf[2]; |
| int new_rangetr = (p->policy_type == POLICY_KERN && |
| p->policyvers >= POLICYDB_VERSION_RANGETRANS); |
| int warning_issued = 0; |
| |
| nel = 0; |
| for (rt = p->range_tr; rt; rt = rt->next) { |
| /* all range_transitions are written for the new format, only |
| process related range_transitions are written for the old |
| format, so count accordingly */ |
| if (new_rangetr || rt->target_class == SECCLASS_PROCESS) |
| nel++; |
| } |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (rt = p->range_tr; rt; rt = rt->next) { |
| if (!new_rangetr && rt->target_class != SECCLASS_PROCESS) { |
| if (!warning_issued) |
| WARN(fp->handle, "Discarding range_transition " |
| "rules for security classes other than " |
| "\"process\""); |
| warning_issued = 1; |
| continue; |
| } |
| buf[0] = cpu_to_le32(rt->source_type); |
| buf[1] = cpu_to_le32(rt->target_type); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| if (new_rangetr) { |
| buf[0] = cpu_to_le32(rt->target_class); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| if (mls_write_range_helper(&rt->target_range, fp)) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| /************** module writing functions below **************/ |
| |
| static int avrule_write(avrule_t * avrule, struct policy_file *fp) |
| { |
| size_t items, items2; |
| uint32_t buf[32], len; |
| class_perm_node_t *cur; |
| |
| if (avrule->specified & AVRULE_XPERMS) { |
| ERR(fp->handle, "module policy does not support extended" |
| " permissions rules and one was specified"); |
| return POLICYDB_ERROR; |
| } |
| |
| items = 0; |
| buf[items++] = cpu_to_le32(avrule->specified); |
| buf[items++] = cpu_to_le32(avrule->flags); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| |
| if (type_set_write(&avrule->stypes, fp)) |
| return POLICYDB_ERROR; |
| |
| if (type_set_write(&avrule->ttypes, fp)) |
| return POLICYDB_ERROR; |
| |
| cur = avrule->perms; |
| len = 0; |
| while (cur) { |
| len++; |
| cur = cur->next; |
| } |
| items = 0; |
| buf[items++] = cpu_to_le32(len); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| cur = avrule->perms; |
| while (cur) { |
| items = 0; |
| buf[items++] = cpu_to_le32(cur->tclass); |
| buf[items++] = cpu_to_le32(cur->data); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items2 != items) |
| return POLICYDB_ERROR; |
| |
| cur = cur->next; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int avrule_write_list(avrule_t * avrules, struct policy_file *fp) |
| { |
| uint32_t buf[32], len; |
| avrule_t *avrule; |
| |
| avrule = avrules; |
| len = 0; |
| while (avrule) { |
| len++; |
| avrule = avrule->next; |
| } |
| |
| buf[0] = cpu_to_le32(len); |
| if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) |
| return POLICYDB_ERROR; |
| |
| avrule = avrules; |
| while (avrule) { |
| if (avrule_write(avrule, fp)) |
| return POLICYDB_ERROR; |
| avrule = avrule->next; |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int only_process(ebitmap_t *in) |
| { |
| unsigned int i; |
| ebitmap_node_t *node; |
| |
| ebitmap_for_each_bit(in, node, i) { |
| if (ebitmap_node_get_bit(node, i) && |
| i != SECCLASS_PROCESS - 1) |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int role_trans_rule_write(policydb_t *p, role_trans_rule_t * t, |
| struct policy_file *fp) |
| { |
| int nel = 0; |
| size_t items; |
| uint32_t buf[1]; |
| role_trans_rule_t *tr; |
| int warned = 0; |
| int new_role = p->policyvers >= MOD_POLICYDB_VERSION_ROLETRANS; |
| |
| for (tr = t; tr; tr = tr->next) |
| if (new_role || only_process(&tr->classes)) |
| nel++; |
| |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (tr = t; tr; tr = tr->next) { |
| if (!new_role && !only_process(&tr->classes)) { |
| if (!warned) |
| WARN(fp->handle, "Discarding role_transition " |
| "rules for security classes other than " |
| "\"process\""); |
| warned = 1; |
| continue; |
| } |
| if (role_set_write(&tr->roles, fp)) |
| return POLICYDB_ERROR; |
| if (type_set_write(&tr->types, fp)) |
| return POLICYDB_ERROR; |
| if (new_role) |
| if (ebitmap_write(&tr->classes, fp)) |
| return POLICYDB_ERROR; |
| buf[0] = cpu_to_le32(tr->new_role); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int role_allow_rule_write(role_allow_rule_t * r, struct policy_file *fp) |
| { |
| int nel = 0; |
| size_t items; |
| uint32_t buf[1]; |
| role_allow_rule_t *ra; |
| |
| for (ra = r; ra; ra = ra->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (ra = r; ra; ra = ra->next) { |
| if (role_set_write(&ra->roles, fp)) |
| return POLICYDB_ERROR; |
| if (role_set_write(&ra->new_roles, fp)) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int filename_trans_rule_write(filename_trans_rule_t * t, struct policy_file *fp) |
| { |
| int nel = 0; |
| size_t items; |
| uint32_t buf[2], len; |
| filename_trans_rule_t *ftr; |
| |
| for (ftr = t; ftr; ftr = ftr->next) |
| nel++; |
| |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| for (ftr = t; ftr; ftr = ftr->next) { |
| len = strlen(ftr->name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| |
| items = put_entry(ftr->name, sizeof(char), len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| if (type_set_write(&ftr->stypes, fp)) |
| return POLICYDB_ERROR; |
| if (type_set_write(&ftr->ttypes, fp)) |
| return POLICYDB_ERROR; |
| |
| buf[0] = cpu_to_le32(ftr->tclass); |
| buf[1] = cpu_to_le32(ftr->otype); |
| |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int range_trans_rule_write(range_trans_rule_t * t, |
| struct policy_file *fp) |
| { |
| int nel = 0; |
| size_t items; |
| uint32_t buf[1]; |
| range_trans_rule_t *rt; |
| |
| for (rt = t; rt; rt = rt->next) |
| nel++; |
| buf[0] = cpu_to_le32(nel); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| for (rt = t; rt; rt = rt->next) { |
| if (type_set_write(&rt->stypes, fp)) |
| return POLICYDB_ERROR; |
| if (type_set_write(&rt->ttypes, fp)) |
| return POLICYDB_ERROR; |
| if (ebitmap_write(&rt->tclasses, fp)) |
| return POLICYDB_ERROR; |
| if (mls_write_semantic_range_helper(&rt->trange, fp)) |
| return POLICYDB_ERROR; |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int scope_index_write(scope_index_t * scope_index, |
| unsigned int num_scope_syms, |
| struct policy_file *fp) |
| { |
| unsigned int i; |
| uint32_t buf[1]; |
| for (i = 0; i < num_scope_syms; i++) { |
| if (ebitmap_write(scope_index->scope + i, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| } |
| buf[0] = cpu_to_le32(scope_index->class_perms_len); |
| if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) { |
| return POLICYDB_ERROR; |
| } |
| for (i = 0; i < scope_index->class_perms_len; i++) { |
| if (ebitmap_write(scope_index->class_perms_map + i, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int avrule_decl_write(avrule_decl_t * decl, int num_scope_syms, |
| policydb_t * p, struct policy_file *fp) |
| { |
| struct policy_data pd; |
| uint32_t buf[2]; |
| int i; |
| buf[0] = cpu_to_le32(decl->decl_id); |
| buf[1] = cpu_to_le32(decl->enabled); |
| if (put_entry(buf, sizeof(uint32_t), 2, fp) != 2) { |
| return POLICYDB_ERROR; |
| } |
| if (cond_write_list(p, decl->cond_list, fp) == -1 || |
| avrule_write_list(decl->avrules, fp) == -1 || |
| role_trans_rule_write(p, decl->role_tr_rules, fp) == -1 || |
| role_allow_rule_write(decl->role_allow_rules, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policyvers >= MOD_POLICYDB_VERSION_FILENAME_TRANS && |
| filename_trans_rule_write(decl->filename_trans_rules, fp)) |
| return POLICYDB_ERROR; |
| |
| if (p->policyvers >= MOD_POLICYDB_VERSION_RANGETRANS && |
| range_trans_rule_write(decl->range_tr_rules, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| if (scope_index_write(&decl->required, num_scope_syms, fp) == -1 || |
| scope_index_write(&decl->declared, num_scope_syms, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| pd.fp = fp; |
| pd.p = p; |
| for (i = 0; i < num_scope_syms; i++) { |
| buf[0] = cpu_to_le32(decl->symtab[i].nprim); |
| buf[1] = cpu_to_le32(decl->symtab[i].table->nel); |
| if (put_entry(buf, sizeof(uint32_t), 2, fp) != 2) { |
| return POLICYDB_ERROR; |
| } |
| if (hashtab_map(decl->symtab[i].table, write_f[i], &pd)) { |
| return POLICYDB_ERROR; |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int avrule_block_write(avrule_block_t * block, int num_scope_syms, |
| policydb_t * p, struct policy_file *fp) |
| { |
| /* first write a count of the total number of blocks */ |
| uint32_t buf[1], num_blocks = 0; |
| avrule_block_t *cur; |
| for (cur = block; cur != NULL; cur = cur->next) { |
| num_blocks++; |
| } |
| buf[0] = cpu_to_le32(num_blocks); |
| if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) { |
| return POLICYDB_ERROR; |
| } |
| |
| /* now write each block */ |
| for (cur = block; cur != NULL; cur = cur->next) { |
| uint32_t num_decls = 0; |
| avrule_decl_t *decl; |
| /* write a count of number of branches */ |
| for (decl = cur->branch_list; decl != NULL; decl = decl->next) { |
| num_decls++; |
| } |
| buf[0] = cpu_to_le32(num_decls); |
| if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) { |
| return POLICYDB_ERROR; |
| } |
| for (decl = cur->branch_list; decl != NULL; decl = decl->next) { |
| if (avrule_decl_write(decl, num_scope_syms, p, fp) == |
| -1) { |
| return POLICYDB_ERROR; |
| } |
| } |
| } |
| return POLICYDB_SUCCESS; |
| } |
| |
| static int scope_write(hashtab_key_t key, hashtab_datum_t datum, void *ptr) |
| { |
| scope_datum_t *scope = (scope_datum_t *) datum; |
| struct policy_data *pd = ptr; |
| struct policy_file *fp = pd->fp; |
| uint32_t static_buf[32], *dyn_buf = NULL, *buf; |
| size_t key_len = strlen(key); |
| unsigned int items = 2 + scope->decl_ids_len, i; |
| int rc; |
| |
| buf = static_buf; |
| if (items >= (sizeof(static_buf) / 4)) { |
| /* too many things required, so dynamically create a |
| * buffer. this would have been easier with C99's |
| * dynamic arrays... */ |
| rc = POLICYDB_ERROR; |
| dyn_buf = malloc(items * sizeof(*dyn_buf)); |
| if (!dyn_buf) |
| goto err; |
| buf = dyn_buf; |
| } |
| buf[0] = cpu_to_le32(key_len); |
| |
| rc = POLICYDB_ERROR; |
| if (put_entry(buf, sizeof(*buf), 1, fp) != 1 || |
| put_entry(key, 1, key_len, fp) != key_len) |
| goto err; |
| buf[0] = cpu_to_le32(scope->scope); |
| buf[1] = cpu_to_le32(scope->decl_ids_len); |
| |
| for (i = 0; i < scope->decl_ids_len; i++) |
| buf[2 + i] = cpu_to_le32(scope->decl_ids[i]); |
| |
| rc = POLICYDB_ERROR; |
| if (put_entry(buf, sizeof(*buf), items, fp) != items) |
| goto err; |
| rc = POLICYDB_SUCCESS; |
| err: |
| free(dyn_buf); |
| return rc; |
| } |
| |
| static int type_attr_uncount(hashtab_key_t key __attribute__ ((unused)), |
| hashtab_datum_t datum, void *args) |
| { |
| type_datum_t *typdatum = datum; |
| uint32_t *p_nel = args; |
| |
| if (typdatum->flavor == TYPE_ATTRIB) { |
| /* uncount attribute from total number of types */ |
| (*p_nel)--; |
| } |
| return 0; |
| } |
| |
| static int role_attr_uncount(hashtab_key_t key __attribute__ ((unused)), |
| hashtab_datum_t datum, void *args) |
| { |
| role_datum_t *role = datum; |
| uint32_t *p_nel = args; |
| |
| if (role->flavor == ROLE_ATTRIB) { |
| /* uncount attribute from total number of roles */ |
| (*p_nel)--; |
| } |
| return 0; |
| } |
| |
| /* |
| * Write the configuration data in a policy database |
| * structure to a policy database binary representation |
| * file. |
| */ |
| int policydb_write(policydb_t * p, struct policy_file *fp) |
| { |
| unsigned int i, num_syms; |
| uint32_t buf[32], config; |
| size_t items, items2, len; |
| struct policydb_compat_info *info; |
| struct policy_data pd; |
| const char *policydb_str; |
| |
| if (p->unsupported_format) |
| return POLICYDB_UNSUPPORTED; |
| |
| pd.fp = fp; |
| pd.p = p; |
| |
| config = 0; |
| if (p->mls) { |
| if ((p->policyvers < POLICYDB_VERSION_MLS && |
| p->policy_type == POLICY_KERN) || |
| (p->policyvers < MOD_POLICYDB_VERSION_MLS && |
| p->policy_type == POLICY_BASE) || |
| (p->policyvers < MOD_POLICYDB_VERSION_MLS && |
| p->policy_type == POLICY_MOD)) { |
| ERR(fp->handle, "policy version %d cannot support MLS", |
| p->policyvers); |
| return POLICYDB_ERROR; |
| } |
| config |= POLICYDB_CONFIG_MLS; |
| } |
| |
| config |= (POLICYDB_CONFIG_UNKNOWN_MASK & p->handle_unknown); |
| |
| /* Write the magic number and string identifiers. */ |
| items = 0; |
| if (p->policy_type == POLICY_KERN) { |
| buf[items++] = cpu_to_le32(POLICYDB_MAGIC); |
| len = strlen(policydb_target_strings[p->target_platform]); |
| policydb_str = policydb_target_strings[p->target_platform]; |
| } else { |
| buf[items++] = cpu_to_le32(POLICYDB_MOD_MAGIC); |
| len = strlen(POLICYDB_MOD_STRING); |
| policydb_str = POLICYDB_MOD_STRING; |
| } |
| buf[items++] = cpu_to_le32(len); |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| items = put_entry(policydb_str, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| |
| /* Write the version, config, and table sizes. */ |
| items = 0; |
| info = policydb_lookup_compat(p->policyvers, p->policy_type, |
| p->target_platform); |
| if (!info) { |
| ERR(fp->handle, "compatibility lookup failed for policy " |
| "version %d", p->policyvers); |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policy_type != POLICY_KERN) { |
| buf[items++] = cpu_to_le32(p->policy_type); |
| } |
| buf[items++] = cpu_to_le32(p->policyvers); |
| buf[items++] = cpu_to_le32(config); |
| buf[items++] = cpu_to_le32(info->sym_num); |
| buf[items++] = cpu_to_le32(info->ocon_num); |
| |
| items2 = put_entry(buf, sizeof(uint32_t), items, fp); |
| if (items != items2) |
| return POLICYDB_ERROR; |
| |
| if (p->policy_type == POLICY_MOD) { |
| /* Write module name and version */ |
| len = strlen(p->name); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(p->name, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| len = strlen(p->version); |
| buf[0] = cpu_to_le32(len); |
| items = put_entry(buf, sizeof(uint32_t), 1, fp); |
| if (items != 1) |
| return POLICYDB_ERROR; |
| items = put_entry(p->version, 1, len, fp); |
| if (items != len) |
| return POLICYDB_ERROR; |
| } |
| |
| if ((p->policyvers >= POLICYDB_VERSION_POLCAP && |
| p->policy_type == POLICY_KERN) || |
| (p->policyvers >= MOD_POLICYDB_VERSION_POLCAP && |
| p->policy_type == POLICY_BASE) || |
| (p->policyvers >= MOD_POLICYDB_VERSION_POLCAP && |
| p->policy_type == POLICY_MOD)) { |
| if (ebitmap_write(&p->policycaps, fp) == -1) |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policyvers < POLICYDB_VERSION_PERMISSIVE && |
| p->policy_type == POLICY_KERN) { |
| ebitmap_node_t *tnode; |
| |
| ebitmap_for_each_bit(&p->permissive_map, tnode, i) { |
| if (ebitmap_node_get_bit(tnode, i)) { |
| WARN(fp->handle, "Warning! Policy version %d cannot " |
| "support permissive types, but some were defined", |
| p->policyvers); |
| break; |
| } |
| } |
| } |
| |
| if (p->policyvers >= POLICYDB_VERSION_PERMISSIVE && |
| p->policy_type == POLICY_KERN) { |
| if (ebitmap_write(&p->permissive_map, fp) == -1) |
| return POLICYDB_ERROR; |
| } |
| |
| num_syms = info->sym_num; |
| for (i = 0; i < num_syms; i++) { |
| buf[0] = cpu_to_le32(p->symtab[i].nprim); |
| buf[1] = p->symtab[i].table->nel; |
| |
| /* |
| * A special case when writing type/attribute symbol table. |
| * The kernel policy version less than 24 does not support |
| * to load entries of attribute, so we have to re-calculate |
| * the actual number of types except for attributes. |
| */ |
| if (i == SYM_TYPES && |
| p->policyvers < POLICYDB_VERSION_BOUNDARY && |
| p->policy_type == POLICY_KERN) { |
| hashtab_map(p->symtab[i].table, type_attr_uncount, &buf[1]); |
| } |
| |
| /* |
| * Another special case when writing role/attribute symbol |
| * table, role attributes are redundant for policy.X, or |
| * when the pp's version is not big enough. So deduct |
| * their numbers from p_roles.table->nel. |
| */ |
| if ((i == SYM_ROLES) && |
| ((p->policy_type == POLICY_KERN) || |
| (p->policy_type != POLICY_KERN && |
| p->policyvers < MOD_POLICYDB_VERSION_ROLEATTRIB))) |
| (void)hashtab_map(p->symtab[i].table, role_attr_uncount, &buf[1]); |
| |
| buf[1] = cpu_to_le32(buf[1]); |
| items = put_entry(buf, sizeof(uint32_t), 2, fp); |
| if (items != 2) |
| return POLICYDB_ERROR; |
| if (hashtab_map(p->symtab[i].table, write_f[i], &pd)) |
| return POLICYDB_ERROR; |
| } |
| |
| if (p->policy_type == POLICY_KERN) { |
| if (avtab_write(p, &p->te_avtab, fp)) |
| return POLICYDB_ERROR; |
| if (p->policyvers < POLICYDB_VERSION_BOOL) { |
| if (p->p_bools.nprim) |
| WARN(fp->handle, "Discarding " |
| "booleans and conditional rules"); |
| } else { |
| if (cond_write_list(p, p->cond_list, fp)) |
| return POLICYDB_ERROR; |
| } |
| if (role_trans_write(p, fp)) |
| return POLICYDB_ERROR; |
| if (role_allow_write(p->role_allow, fp)) |
| return POLICYDB_ERROR; |
| if (p->policyvers >= POLICYDB_VERSION_FILENAME_TRANS) { |
| if (filename_trans_write(p->filename_trans, fp)) |
| return POLICYDB_ERROR; |
| } else { |
| if (p->filename_trans) |
| WARN(fp->handle, "Discarding filename type transition rules"); |
| } |
| } else { |
| if (avrule_block_write(p->global, num_syms, p, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| |
| for (i = 0; i < num_syms; i++) { |
| buf[0] = cpu_to_le32(p->scope[i].table->nel); |
| if (put_entry(buf, sizeof(uint32_t), 1, fp) != 1) { |
| return POLICYDB_ERROR; |
| } |
| if (hashtab_map(p->scope[i].table, scope_write, &pd)) |
| return POLICYDB_ERROR; |
| } |
| } |
| |
| if (ocontext_write(info, p, fp) == -1 || genfs_write(p, fp) == -1) { |
| return POLICYDB_ERROR; |
| } |
| |
| if ((p->policyvers >= POLICYDB_VERSION_MLS |
| && p->policy_type == POLICY_KERN) |
| || (p->policyvers >= MOD_POLICYDB_VERSION_MLS |
| && p->policyvers < MOD_POLICYDB_VERSION_RANGETRANS |
| && p->policy_type == POLICY_BASE)) { |
| if (range_write(p, fp)) { |
| return POLICYDB_ERROR; |
| } |
| } |
| |
| if (p->policy_type == POLICY_KERN |
| && p->policyvers >= POLICYDB_VERSION_AVTAB) { |
| for (i = 0; i < p->p_types.nprim; i++) { |
| if (ebitmap_write(&p->type_attr_map[i], fp) == -1) |
| return POLICYDB_ERROR; |
| } |
| } |
| |
| return POLICYDB_SUCCESS; |
| } |