/* Author : Stephen Smalley, <sds@tycho.nsa.gov> */
/*
* 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
* Copyright (C) 2017 Mellanox Technologies Inc.
*
* 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(policydb_t *p,
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_write_helper(hashtab_key_t key, void *data, void *ptr)
{
uint32_t buf[4];
size_t items, len;
struct filename_trans *ft = (struct filename_trans *)key;
struct filename_trans_datum *otype = data;
void *fp = ptr;
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(otype->otype);
items = put_entry(buf, sizeof(uint32_t), 4, fp);
if (items != 4)
return POLICYDB_ERROR;
return 0;
}
static int filename_trans_write(struct policydb *p, void *fp)
{
size_t nel, items;
uint32_t buf[1];
int rc;
if (p->policyvers < POLICYDB_VERSION_FILENAME_TRANS)
return 0;
nel = p->filename_trans->nel;
buf[0] = cpu_to_le32(nel);
items = put_entry(buf, sizeof(uint32_t), 1, fp);
if (items != 1)
return POLICYDB_ERROR;
rc = hashtab_map(p->filename_trans, filename_write_helper, fp);
if (rc)
return rc;
return 0;
}
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(p, node->avtrue_list, fp))
return POLICYDB_ERROR;
if (avrule_write_list(p, 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) {
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 (role->s.value == OBJECT_R_VAL) {
/*
* CIL populates object_r's types map
* rather than handling it as a special case.
* However, this creates an inconsistency with
* the kernel policy read from /sys/fs/selinux/policy
* because the kernel ignores everything except for
* object_r's value from the policy file.
* Make them consistent by writing an empty
* ebitmap instead.
*/
ebitmap_t empty;
ebitmap_init(&empty);
if (ebitmap_write(&empty, fp))
return POLICYDB_ERROR;
} else {
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_IBPKEY:
/* The subnet prefix is in network order */
memcpy(buf, &c->u.ibpkey.subnet_prefix,
sizeof(c->u.ibpkey.subnet_prefix));
buf[2] = cpu_to_le32(c->u.ibpkey.low_pkey);
buf[3] = cpu_to_le32(c->u.ibpkey.high_pkey);
items = put_entry(buf, sizeof(uint32_t), 4, fp);
if (items != 4)
return POLICYDB_ERROR;
if (context_write(p, &c->context[0], fp))
return POLICYDB_ERROR;
break;
case OCON_IBENDPORT:
len = strlen(c->u.ibendport.dev_name);
buf[0] = cpu_to_le32(len);
buf[1] = cpu_to_le32(c->u.ibendport.port);
items = put_entry(buf, sizeof(uint32_t), 2, fp);
if (items != 2)
return POLICYDB_ERROR;
items = put_entry(c->u.ibendport.dev_name, 1, len, fp);
if (items != len)
return POLICYDB_ERROR;
if (context_write(p, &c->context[0], 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;
}
struct rangetrans_write_args {
size_t nel;
int new_rangetr;
struct policy_file *fp;
};
static int rangetrans_count(hashtab_key_t key,
void *data __attribute__ ((unused)),
void *ptr)
{
struct range_trans *rt = (struct range_trans *)key;
struct rangetrans_write_args *args = ptr;
/* all range_transitions are written for the new format, only
process related range_transitions are written for the old
format, so count accordingly */
if (args->new_rangetr || rt->target_class == SECCLASS_PROCESS)
args->nel++;
return 0;
}
static int range_write_helper(hashtab_key_t key, void *data, void *ptr)
{
uint32_t buf[2];
struct range_trans *rt = (struct range_trans *)key;
struct mls_range *r = data;
struct rangetrans_write_args *args = ptr;
struct policy_file *fp = args->fp;
int new_rangetr = args->new_rangetr;
size_t items;
static int warning_issued = 0;
int rc;
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;
return 0;
}
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;
}
rc = mls_write_range_helper(r, fp);
if (rc)
return rc;
return 0;
}
static int range_write(policydb_t * p, struct policy_file *fp)
{
size_t items;
uint32_t buf[2];
int new_rangetr = (p->policy_type == POLICY_KERN &&
p->policyvers >= POLICYDB_VERSION_RANGETRANS);
struct rangetrans_write_args args;
int rc;
args.nel = 0;
args.new_rangetr = new_rangetr;
args.fp = fp;
rc = hashtab_map(p->range_tr, rangetrans_count, &args);
if (rc)
return rc;
buf[0] = cpu_to_le32(args.nel);
items = put_entry(buf, sizeof(uint32_t), 1, fp);
if (items != 1)
return POLICYDB_ERROR;
return hashtab_map(p->range_tr, range_write_helper, &args);
}
/************** module writing functions below **************/
static int avrule_write(policydb_t *p, avrule_t * avrule,
struct policy_file *fp)
{
size_t items, items2;
uint32_t buf[32], len;
class_perm_node_t *cur;
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;
}
if (avrule->specified & AVRULE_XPERMS) {
size_t nel = ARRAY_SIZE(avrule->xperms->perms);
uint32_t buf32[nel];
uint8_t buf8;
unsigned int i;
if (p->policyvers < MOD_POLICYDB_VERSION_XPERMS_IOCTL) {
ERR(fp->handle,
"module 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) {
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;
}
buf8 = avrule->xperms->specified;
items = put_entry(&buf8, sizeof(uint8_t),1,fp);
if (items != 1)
return POLICYDB_ERROR;
buf8 = avrule->xperms->driver;
items = put_entry(&buf8, sizeof(uint8_t),1,fp);
if (items != 1)
return POLICYDB_ERROR;
for (i = 0; i < nel; i++)
buf32[i] = cpu_to_le32(avrule->xperms->perms[i]);
items = put_entry(buf32, sizeof(uint32_t), nel, fp);
if (items != nel)
return POLICYDB_ERROR;
}
return POLICYDB_SUCCESS;
}
static int avrule_write_list(policydb_t *p, 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(p, 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(p, 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, 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;
}