selinux/checkpolicy/module_compiler.c
Caleb Case f7917ea9cf aliases for the boundry format
The boundry format mapped the primary field to a boolean in the
properties bitmap. This is appropriate for the kernel policy, but in
modular policy the primary field may be an integer that indicates the
primary type that is being aliased. In this case, the primary value cannot
be assumed to be boolean.

This patch creates a new module format that writes out the primary value
as was done before the boundry format.

Signed-off-by: Caleb Case <ccase@tresys.com>
Signed-off-by: Joshua Brindle <method@manicmethod.com>
2009-02-16 11:52:03 -05:00

1517 lines
35 KiB
C

/* Author : Joshua Brindle <jbrindle@tresys.com>
* Karl MacMillan <kmacmillan@tresys.com>
* Jason Tang <jtang@tresys.com>
* Added support for binary policy modules
*
* Copyright (C) 2004 - 2005 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/avrule_block.h>
#include <sepol/policydb/conditional.h>
#include "queue.h"
#include "module_compiler.h"
union stack_item_u {
avrule_block_t *avrule;
cond_list_t *cond_list;
};
typedef struct scope_stack {
union stack_item_u u;
int type; /* for above union: 1 = avrule block, 2 = conditional */
avrule_decl_t *decl; /* if in an avrule block, which
* declaration is current */
avrule_t *last_avrule;
int in_else; /* if in an avrule block, within ELSE branch */
int require_given; /* 1 if this block had at least one require */
struct scope_stack *parent, *child;
} scope_stack_t;
extern policydb_t *policydbp;
extern queue_t id_queue;
extern int yyerror(char *msg);
extern void yyerror2(char *fmt, ...);
static int push_stack(int stack_type, ...);
static void pop_stack(void);
/* keep track of the last item added to the stack */
static scope_stack_t *stack_top = NULL;
static avrule_block_t *last_block;
static uint32_t next_decl_id = 1;
int define_policy(int pass, int module_header_given)
{
char *id;
if (module_header_given) {
if (policydbp->policy_type != POLICY_MOD) {
yyerror
("Module specification found while not building a policy module.\n");
return -1;
}
if (pass == 2) {
while ((id = queue_remove(id_queue)) != NULL)
free(id);
} else {
id = (char *)queue_remove(id_queue);
if (!id) {
yyerror("no module name");
return -1;
}
policydbp->name = id;
if ((policydbp->version =
queue_remove(id_queue)) == NULL) {
yyerror
("Expected a module version but none was found.");
return -1;
}
}
} else {
if (policydbp->policy_type == POLICY_MOD) {
yyerror
("Building a policy module, but no module specification found.\n");
return -1;
}
}
/* the first declaration within the global avrule
block will always have an id of 1 */
next_decl_id = 2;
/* reset the scoping stack */
while (stack_top != NULL) {
pop_stack();
}
if (push_stack(1, policydbp->global, policydbp->global->branch_list) ==
-1) {
return -1;
}
last_block = policydbp->global;
return 0;
}
/* Given the current parse stack, returns 1 if a declaration would be
* allowed here or 0 if not. For example, declarations are not
* allowed in conditionals, so if there are any conditionals in the
* current scope stack then this would return a 0.
*/
static int is_declaration_allowed(void)
{
if (stack_top->type != 1 || stack_top->in_else) {
return 0;
}
return 1;
}
/* Attempt to declare a symbol within the current declaration. If
* currently within a non-conditional and in a non-else branch then
* insert the symbol, return 0 on success if symbol was undeclared.
* For roles and users, it is legal to have multiple declarations; as
* such return 1 to indicate that caller must free() the datum because
* it was not added. If symbols may not be declared here return -1.
* For duplicate declarations return -2. For all else, including out
* of memory, return -3. Note that dest_value and datum_value might
* not be restricted pointers. */
int declare_symbol(uint32_t symbol_type,
hashtab_key_t key, hashtab_datum_t datum,
uint32_t * dest_value, uint32_t * datum_value)
{
avrule_decl_t *decl = stack_top->decl;
int retval;
/* first check that symbols may be declared here */
if (!is_declaration_allowed()) {
return -1;
}
retval = symtab_insert(policydbp, symbol_type, key, datum,
SCOPE_DECL, decl->decl_id, dest_value);
if (retval == 1 && dest_value) {
symtab_datum_t *s =
(symtab_datum_t *) hashtab_search(policydbp->
symtab[symbol_type].table,
key);
assert(s != NULL);
if (symbol_type == SYM_LEVELS) {
*dest_value = ((level_datum_t *)s)->level->sens;
} else {
*dest_value = s->value;
}
} else if (retval == -2) {
return -2;
} else if (retval < 0) {
return -3;
} else { /* fall through possible if retval is 0 */
}
if (datum_value != NULL) {
if (ebitmap_set_bit(decl->declared.scope + symbol_type,
*datum_value - 1, 1)) {
return -3;
}
}
return retval;
}
static int role_implicit_bounds(hashtab_t roles_tab,
char *role_id, role_datum_t *role)
{
role_datum_t *bounds;
char *bounds_id, *delim;
delim = strrchr(role_id, '.');
if (!delim)
return 0; /* no implicit boundary */
bounds_id = strdup(role_id);
if (!bounds_id) {
yyerror("out of memory");
return -1;
}
bounds_id[(size_t)(delim - role_id)] = '\0';
bounds = hashtab_search(roles_tab, bounds_id);
if (!bounds) {
yyerror2("role %s doesn't exist, is implicit bounds of %s",
bounds_id, role_id);
return -1;
}
if (!role->bounds)
role->bounds = bounds->s.value;
else if (role->bounds != bounds->s.value) {
yyerror2("role %s has inconsistent bounds %s/%s",
role_id, bounds_id,
policydbp->p_role_val_to_name[role->bounds - 1]);
return -1;
}
free(bounds_id);
return 0;
}
role_datum_t *declare_role(void)
{
char *id = queue_remove(id_queue), *dest_id = NULL;
role_datum_t *role = NULL, *dest_role = NULL;
int retval;
uint32_t value;
if (id == NULL) {
yyerror("no role name");
return NULL;
}
if ((role = (role_datum_t *) malloc(sizeof(*role))) == NULL) {
yyerror("Out of memory!");
free(id);
return NULL;
}
role_datum_init(role);
retval =
declare_symbol(SYM_ROLES, id, (hashtab_datum_t *) role, &value,
&value);
if (retval == 0) {
role->s.value = value;
if ((dest_id = strdup(id)) == NULL) {
yyerror("Out of memory!");
return NULL;
}
} else {
/* this role was already declared in this module, or error */
dest_id = id;
role_datum_destroy(role);
free(role);
}
if (retval == 0 || retval == 1) {
/* create a new role_datum_t for this decl, if necessary */
hashtab_t roles_tab;
assert(stack_top->type == 1);
if (stack_top->parent == NULL) {
/* in parent, so use global symbol table */
roles_tab = policydbp->p_roles.table;
} else {
roles_tab = stack_top->decl->p_roles.table;
}
dest_role = (role_datum_t *) hashtab_search(roles_tab, dest_id);
if (dest_role == NULL) {
if ((dest_role =
(role_datum_t *) malloc(sizeof(*dest_role))) ==
NULL) {
yyerror("Out of memory!");
free(dest_id);
return NULL;
}
role_datum_init(dest_role);
dest_role->s.value = value;
if (role_implicit_bounds(roles_tab, dest_id, dest_role)) {
free(dest_id);
role_datum_destroy(dest_role);
free(dest_role);
return NULL;
}
if (hashtab_insert(roles_tab, dest_id, dest_role)) {
yyerror("Out of memory!");
free(dest_id);
role_datum_destroy(dest_role);
free(dest_role);
return NULL;
}
} else {
free(dest_id);
}
} else {
free(dest_id);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return NULL;
}
case -2:{
yyerror("duplicate declaration of role");
return NULL;
}
case -1:{
yyerror("could not declare role here");
return NULL;
}
case 0:{
if (ebitmap_set_bit
(&dest_role->dominates, role->s.value - 1, 1)) {
yyerror("out of memory");
return NULL;
}
return dest_role;
}
case 1:{
return dest_role; /* role already declared for this block */
}
default:{
assert(0); /* should never get here */
}
}
}
type_datum_t *declare_type(unsigned char primary, unsigned char isattr)
{
char *id;
type_datum_t *typdatum;
int retval;
uint32_t value = 0;
id = (char *)queue_remove(id_queue);
if (!id) {
yyerror("no type/attribute name?");
return NULL;
}
if (strcmp(id, "self") == 0) {
yyerror
("'self' is a reserved type name and may not be declared.");
free(id);
return NULL;
}
typdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
if (!typdatum) {
yyerror("Out of memory!");
free(id);
return NULL;
}
type_datum_init(typdatum);
typdatum->primary = primary;
typdatum->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
retval = declare_symbol(SYM_TYPES, id, typdatum, &value, &value);
if (retval == 0 || retval == 1) {
if (typdatum->primary) {
typdatum->s.value = value;
}
} else {
/* error occurred (can't have duplicate type declarations) */
free(id);
type_datum_destroy(typdatum);
free(typdatum);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return NULL;
}
case -2:{
yyerror2("duplicate declaration of type/attribute");
return NULL;
}
case -1:{
yyerror("could not declare type/attribute here");
return NULL;
}
case 0:
case 1:{
return typdatum;
}
default:{
assert(0); /* should never get here */
}
}
}
static int user_implicit_bounds(hashtab_t users_tab,
char *user_id, user_datum_t *user)
{
user_datum_t *bounds;
char *bounds_id, *delim;
delim = strrchr(user_id, '.');
if (!delim)
return 0; /* no implicit boundary */
bounds_id = strdup(user_id);
if (!bounds_id) {
yyerror("out of memory");
return -1;
}
bounds_id[(size_t)(delim - user_id)] = '\0';
bounds = hashtab_search(users_tab, bounds_id);
if (!bounds) {
yyerror2("user %s doesn't exist, is implicit bounds of %s",
bounds_id, user_id);
return -1;
}
if (!user->bounds)
user->bounds = bounds->s.value;
else if (user->bounds != bounds->s.value) {
yyerror2("user %s has inconsistent bounds %s/%s",
user_id, bounds_id,
policydbp->p_role_val_to_name[user->bounds - 1]);
return -1;
}
free(bounds_id);
return 0;
}
user_datum_t *declare_user(void)
{
char *id = queue_remove(id_queue), *dest_id = NULL;
user_datum_t *user = NULL, *dest_user = NULL;
int retval;
uint32_t value = 0;
if (id == NULL) {
yyerror("no user name");
return NULL;
}
if ((user = (user_datum_t *) malloc(sizeof(*user))) == NULL) {
yyerror("Out of memory!");
free(id);
return NULL;
}
user_datum_init(user);
retval =
declare_symbol(SYM_USERS, id, (hashtab_datum_t *) user, &value,
&value);
if (retval == 0) {
user->s.value = value;
if ((dest_id = strdup(id)) == NULL) {
yyerror("Out of memory!");
return NULL;
}
} else {
/* this user was already declared in this module, or error */
dest_id = id;
user_datum_destroy(user);
free(user);
}
if (retval == 0 || retval == 1) {
/* create a new user_datum_t for this decl, if necessary */
hashtab_t users_tab;
assert(stack_top->type == 1);
if (stack_top->parent == NULL) {
/* in parent, so use global symbol table */
users_tab = policydbp->p_users.table;
} else {
users_tab = stack_top->decl->p_users.table;
}
dest_user = (user_datum_t *) hashtab_search(users_tab, dest_id);
if (dest_user == NULL) {
if ((dest_user =
(user_datum_t *) malloc(sizeof(*dest_user))) ==
NULL) {
yyerror("Out of memory!");
free(dest_id);
return NULL;
}
user_datum_init(dest_user);
dest_user->s.value = value;
if (user_implicit_bounds(users_tab, dest_id, dest_user)) {
free(dest_id);
user_datum_destroy(dest_user);
free(dest_user);
return NULL;
}
if (hashtab_insert(users_tab, dest_id, dest_user)) {
yyerror("Out of memory!");
free(dest_id);
user_datum_destroy(dest_user);
free(dest_user);
return NULL;
}
} else {
free(dest_id);
}
} else {
free(dest_id);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return NULL;
}
case -2:{
yyerror("duplicate declaration of user");
return NULL;
}
case -1:{
yyerror("could not declare user here");
return NULL;
}
case 0:{
return dest_user;
}
case 1:{
return dest_user; /* user already declared for this block */
}
default:{
assert(0); /* should never get here */
}
}
}
/* Return a type_datum_t for the local avrule_decl with the given ID.
* If it does not exist, create one with the same value as 'value'.
* This function assumes that the ID is within scope. c.f.,
* is_id_in_scope().
*
* NOTE: this function usurps ownership of id afterwards. The caller
* shall not reference it nor free() it afterwards.
*/
type_datum_t *get_local_type(char *id, uint32_t value, unsigned char isattr)
{
type_datum_t *dest_typdatum;
hashtab_t types_tab;
assert(stack_top->type == 1);
if (stack_top->parent == NULL) {
/* in global, so use global symbol table */
types_tab = policydbp->p_types.table;
} else {
types_tab = stack_top->decl->p_types.table;
}
dest_typdatum = hashtab_search(types_tab, id);
if (!dest_typdatum) {
dest_typdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
if (dest_typdatum == NULL) {
free(id);
return NULL;
}
type_datum_init(dest_typdatum);
dest_typdatum->s.value = value;
dest_typdatum->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
dest_typdatum->primary = 1;
if (hashtab_insert(types_tab, id, dest_typdatum)) {
free(id);
type_datum_destroy(dest_typdatum);
free(dest_typdatum);
return NULL;
}
} else {
free(id);
if (dest_typdatum->flavor != isattr ? TYPE_ATTRIB : TYPE_TYPE) {
return NULL;
}
}
return dest_typdatum;
}
/* Given the current parse stack, returns 1 if a requirement would be
* allowed here or 0 if not. For example, the ELSE branch may never
* have its own requirements.
*/
static int is_require_allowed(void)
{
if (stack_top->type == 1 && !stack_top->in_else) {
return 1;
}
return 0;
}
/* Attempt to require a symbol within the current scope. If currently
* within an optional (and not its else branch), add the symbol to the
* required list. Return 0 on success, 1 if caller needs to free()
* datum. If symbols may not be declared here return -1. For duplicate
* declarations return -2. For all else, including out of memory,
* return -3.. Note that dest_value and datum_value might not be
* restricted pointers.
*/
int require_symbol(uint32_t symbol_type,
hashtab_key_t key, hashtab_datum_t datum,
uint32_t * dest_value, uint32_t * datum_value)
{
avrule_decl_t *decl = stack_top->decl;
int retval;
/* first check that symbols may be required here */
if (!is_require_allowed()) {
return -1;
}
retval = symtab_insert(policydbp, symbol_type, key, datum,
SCOPE_REQ, decl->decl_id, dest_value);
if (retval == 1) {
symtab_datum_t *s =
(symtab_datum_t *) hashtab_search(policydbp->
symtab[symbol_type].table,
key);
assert(s != NULL);
if (symbol_type == SYM_LEVELS) {
*dest_value = ((level_datum_t *)s)->level->sens;
} else {
*dest_value = s->value;
}
} else if (retval == -2) {
/* ignore require statements if that symbol was
* previously declared and is in current scope */
int prev_declaration_ok = 0;
if (is_id_in_scope(symbol_type, key)) {
if (symbol_type == SYM_TYPES) {
/* check that previous symbol has same
* type/attribute-ness */
unsigned char new_isattr =
((type_datum_t *) datum)->flavor;
type_datum_t *old_datum =
(type_datum_t *) hashtab_search(policydbp->
symtab
[SYM_TYPES].
table, key);
assert(old_datum != NULL);
unsigned char old_isattr = old_datum->flavor;
prev_declaration_ok =
(old_isattr == new_isattr ? 1 : 0);
} else {
prev_declaration_ok = 1;
}
}
if (prev_declaration_ok) {
/* ignore this require statement because it
* was already declared within my scope */
stack_top->require_given = 1;
return 1;
} else {
/* previous declaration was not in scope or
* had a mismatched type/attribute, so
* generate an error */
return -2;
}
} else if (retval < 0) {
return -3;
} else { /* fall through possible if retval is 0 or 1 */
}
if (datum_value != NULL) {
if (ebitmap_set_bit(decl->required.scope + symbol_type,
*datum_value - 1, 1)) {
return -3;
}
}
stack_top->require_given = 1;
return retval;
}
int add_perm_to_class(uint32_t perm_value, uint32_t class_value)
{
avrule_decl_t *decl = stack_top->decl;
scope_index_t *scope;
assert(perm_value >= 1);
assert(class_value >= 1);
scope = &decl->required;
if (class_value > scope->class_perms_len) {
int i;
ebitmap_t *new_map = realloc(scope->class_perms_map,
class_value * sizeof(*new_map));
if (new_map == NULL) {
return -1;
}
scope->class_perms_map = new_map;
for (i = scope->class_perms_len; i < class_value; i++) {
ebitmap_init(scope->class_perms_map + i);
}
scope->class_perms_len = class_value;
}
if (ebitmap_set_bit(scope->class_perms_map + class_value - 1,
perm_value - 1, 1)) {
return -1;
}
return 0;
}
static int perm_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p
__attribute__ ((unused)))
{
if (key)
free(key);
free(datum);
return 0;
}
static void class_datum_destroy(class_datum_t * cladatum)
{
if (cladatum != NULL) {
hashtab_map(cladatum->permissions.table, perm_destroy, NULL);
hashtab_destroy(cladatum->permissions.table);
free(cladatum);
}
}
int require_class(int pass)
{
char *class_id = queue_remove(id_queue);
char *perm_id = NULL;
class_datum_t *datum = NULL;
perm_datum_t *perm = NULL;
int ret;
if (pass == 2) {
free(class_id);
while ((perm_id = queue_remove(id_queue)) != NULL)
free(perm_id);
return 0;
}
/* first add the class if it is not already there */
if (class_id == NULL) {
yyerror("no class name for class definition?");
return -1;
}
if ((datum = calloc(1, sizeof(*datum))) == NULL ||
symtab_init(&datum->permissions, PERM_SYMTAB_SIZE)) {
yyerror("Out of memory!");
goto cleanup;
}
ret =
require_symbol(SYM_CLASSES, class_id, datum, &datum->s.value,
&datum->s.value);
switch (ret) {
case -3:{
yyerror("Out of memory!");
free(class_id);
class_datum_destroy(datum);
goto cleanup;
}
case -2:{
yyerror("duplicate declaration of class");
free(class_id);
class_datum_destroy(datum);
goto cleanup;
}
case -1:{
yyerror("could not require class here");
free(class_id);
class_datum_destroy(datum);
goto cleanup;
}
case 0:{
/* a new class was added; reindex everything */
if (policydb_index_classes(policydbp)) {
yyerror("Out of memory!");
goto cleanup;
}
break;
}
case 1:{
class_datum_destroy(datum);
datum =
hashtab_search(policydbp->p_classes.table,
class_id);
assert(datum); /* the class datum should have existed */
free(class_id);
break;
}
default:{
assert(0); /* should never get here */
}
}
/* now add each of the permissions to this class's requirements */
while ((perm_id = queue_remove(id_queue)) != NULL) {
int allocated = 0;
/* Is the permission already in the table? */
perm = hashtab_search(datum->permissions.table, perm_id);
if (!perm && datum->comdatum)
perm =
hashtab_search(datum->comdatum->permissions.table,
perm_id);
if (perm) {
/* Yes, drop the name. */
free(perm_id);
} else {
/* No - allocate and insert an entry for it. */
if (policydbp->policy_type == POLICY_BASE) {
yyerror2
("Base policy - require of permission %s without prior declaration.",
perm_id);
free(perm_id);
goto cleanup;
}
allocated = 1;
if ((perm = malloc(sizeof(*perm))) == NULL) {
yyerror("Out of memory!");
free(perm_id);
goto cleanup;
}
memset(perm, 0, sizeof(*perm));
ret =
hashtab_insert(datum->permissions.table, perm_id,
perm);
if (ret) {
yyerror("Out of memory!");
free(perm_id);
free(perm);
goto cleanup;
}
perm->s.value = datum->permissions.nprim + 1;
}
if (add_perm_to_class(perm->s.value, datum->s.value) == -1) {
yyerror("Out of memory!");
goto cleanup;
}
/* Update number of primitives if we allocated one. */
if (allocated)
datum->permissions.nprim++;
}
return 0;
cleanup:
return -1;
}
int require_role(int pass)
{
char *id = queue_remove(id_queue);
role_datum_t *role = NULL;
int retval;
if (pass == 2) {
free(id);
return 0;
}
if (id == NULL) {
yyerror("no role name");
return -1;
}
if ((role = malloc(sizeof(*role))) == NULL) {
free(id);
yyerror("Out of memory!");
return -1;
}
role_datum_init(role);
retval =
require_symbol(SYM_ROLES, id, (hashtab_datum_t *) role,
&role->s.value, &role->s.value);
if (retval != 0) {
free(id);
role_datum_destroy(role);
free(role);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of role");
return -1;
}
case -1:{
yyerror("could not require role here");
return -1;
}
case 0:{
/* all roles dominate themselves */
if (ebitmap_set_bit
(&role->dominates, role->s.value - 1, 1)) {
yyerror("Out of memory");
return -1;
}
return 0;
}
case 1:{
return 0; /* role already required */
}
default:{
assert(0); /* should never get here */
}
}
}
static int require_type_or_attribute(int pass, unsigned char isattr)
{
char *id = queue_remove(id_queue);
type_datum_t *type = NULL;
int retval;
if (pass == 2) {
free(id);
return 0;
}
if (id == NULL) {
yyerror("no type name");
return -1;
}
if ((type = malloc(sizeof(*type))) == NULL) {
free(id);
yyerror("Out of memory!");
return -1;
}
type_datum_init(type);
type->primary = 1;
type->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE;
retval =
require_symbol(SYM_TYPES, id, (hashtab_datum_t *) type,
&type->s.value, &type->s.value);
if (retval != 0) {
free(id);
free(type);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of type/attribute");
return -1;
}
case -1:{
yyerror("could not require type/attribute here");
return -1;
}
case 0:{
return 0;
}
case 1:{
return 0; /* type already required */
}
default:{
assert(0); /* should never get here */
}
}
}
int require_type(int pass)
{
return require_type_or_attribute(pass, 0);
}
int require_attribute(int pass)
{
return require_type_or_attribute(pass, 1);
}
int require_user(int pass)
{
char *id = queue_remove(id_queue);
user_datum_t *user = NULL;
int retval;
if (pass == 1) {
free(id);
return 0;
}
if (id == NULL) {
yyerror("no user name");
return -1;
}
if ((user = malloc(sizeof(*user))) == NULL) {
free(id);
yyerror("Out of memory!");
return -1;
}
user_datum_init(user);
retval =
require_symbol(SYM_USERS, id, (hashtab_datum_t *) user,
&user->s.value, &user->s.value);
if (retval != 0) {
free(id);
user_datum_destroy(user);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of user");
return -1;
}
case -1:{
yyerror("could not require user here");
return -1;
}
case 0:{
return 0;
}
case 1:{
return 0; /* user already required */
}
default:{
assert(0); /* should never get here */
}
}
}
int require_bool(int pass)
{
char *id = queue_remove(id_queue);
cond_bool_datum_t *booldatum = NULL;
int retval;
if (pass == 2) {
free(id);
return 0;
}
if (id == NULL) {
yyerror("no boolean name");
return -1;
}
if ((booldatum = calloc(1, sizeof(*booldatum))) == NULL) {
cond_destroy_bool(id, booldatum, NULL);
yyerror("Out of memory!");
return -1;
}
retval =
require_symbol(SYM_BOOLS, id, (hashtab_datum_t *) booldatum,
&booldatum->s.value, &booldatum->s.value);
if (retval != 0) {
cond_destroy_bool(id, booldatum, NULL);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of boolean");
return -1;
}
case -1:{
yyerror("could not require boolean here");
return -1;
}
case 0:{
return 0;
}
case 1:{
return 0; /* boolean already required */
}
default:{
assert(0); /* should never get here */
}
}
}
int require_sens(int pass)
{
char *id = queue_remove(id_queue);
level_datum_t *level = NULL;
int retval;
if (pass == 2) {
free(id);
return 0;
}
if (!id) {
yyerror("no sensitivity name");
return -1;
}
level = malloc(sizeof(level_datum_t));
if (!level) {
free(id);
yyerror("Out of memory!");
return -1;
}
level_datum_init(level);
level->level = malloc(sizeof(mls_level_t));
if (!level->level) {
free(id);
level_datum_destroy(level);
free(level);
yyerror("Out of memory!");
return -1;
}
mls_level_init(level->level);
retval = require_symbol(SYM_LEVELS, id, (hashtab_datum_t *) level,
&level->level->sens, &level->level->sens);
if (retval != 0) {
free(id);
mls_level_destroy(level->level);
free(level->level);
level_datum_destroy(level);
free(level);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of sensitivity");
return -1;
}
case -1:{
yyerror("could not require sensitivity here");
return -1;
}
case 0:{
return 0;
}
case 1:{
return 0; /* sensitivity already required */
}
default:{
assert(0); /* should never get here */
}
}
}
int require_cat(int pass)
{
char *id = queue_remove(id_queue);
cat_datum_t *cat = NULL;
int retval;
if (pass == 2) {
free(id);
return 0;
}
if (!id) {
yyerror("no category name");
return -1;
}
cat = malloc(sizeof(cat_datum_t));
if (!cat) {
free(id);
yyerror("Out of memory!");
return -1;
}
cat_datum_init(cat);
retval = require_symbol(SYM_CATS, id, (hashtab_datum_t *) cat,
&cat->s.value, &cat->s.value);
if (retval != 0) {
free(id);
cat_datum_destroy(cat);
free(cat);
}
switch (retval) {
case -3:{
yyerror("Out of memory!");
return -1;
}
case -2:{
yyerror("duplicate declaration of category");
return -1;
}
case -1:{
yyerror("could not require category here");
return -1;
}
case 0:{
return 0;
}
case 1:{
return 0; /* category already required */
}
default:{
assert(0); /* should never get here */
}
}
}
static int is_scope_in_stack(scope_datum_t * scope, scope_stack_t * stack)
{
int i;
if (stack == NULL) {
return 0; /* no matching scope found */
}
if (stack->type == 1) {
avrule_decl_t *decl = stack->decl;
for (i = 0; i < scope->decl_ids_len; i++) {
if (scope->decl_ids[i] == decl->decl_id) {
return 1;
}
}
} else {
/* note that conditionals can't declare or require
* symbols, so skip this level */
}
/* not within scope of this stack, so try its parent */
return is_scope_in_stack(scope, stack->parent);
}
int is_id_in_scope(uint32_t symbol_type, hashtab_key_t id)
{
scope_datum_t *scope =
(scope_datum_t *) hashtab_search(policydbp->scope[symbol_type].
table, id);
if (scope == NULL) {
return 1; /* id is not known, so return success */
}
return is_scope_in_stack(scope, stack_top);
}
static int is_perm_in_scope_index(uint32_t perm_value, uint32_t class_value,
scope_index_t * scope)
{
if (class_value > scope->class_perms_len) {
return 1;
}
if (ebitmap_get_bit(scope->class_perms_map + class_value - 1,
perm_value - 1)) {
return 1;
}
return 0;
}
static int is_perm_in_stack(uint32_t perm_value, uint32_t class_value,
scope_stack_t * stack)
{
if (stack == NULL) {
return 0; /* no matching scope found */
}
if (stack->type == 1) {
avrule_decl_t *decl = stack->decl;
if (is_perm_in_scope_index
(perm_value, class_value, &decl->required)
|| is_perm_in_scope_index(perm_value, class_value,
&decl->declared)) {
return 1;
}
} else {
/* note that conditionals can't declare or require
* symbols, so skip this level */
}
/* not within scope of this stack, so try its parent */
return is_perm_in_stack(perm_value, class_value, stack->parent);
}
int is_perm_in_scope(hashtab_key_t perm_id, hashtab_key_t class_id)
{
class_datum_t *cladatum =
(class_datum_t *) hashtab_search(policydbp->p_classes.table,
class_id);
perm_datum_t *perdatum;
if (cladatum == NULL) {
return 1;
}
perdatum = (perm_datum_t *) hashtab_search(cladatum->permissions.table,
perm_id);
if (perdatum == NULL) {
return 1;
}
return is_perm_in_stack(perdatum->s.value, cladatum->s.value,
stack_top);
}
cond_list_t *get_current_cond_list(cond_list_t * cond)
{
/* FIX ME: do something different here if in a nested
* conditional? */
avrule_decl_t *decl = stack_top->decl;
return get_decl_cond_list(policydbp, decl, cond);
}
/* Append the new conditional node to the existing ones. During
* expansion the list will be reversed -- i.e., the last AV rule will
* be the first one listed in the policy. This matches the behavior
* of the upstream compiler. */
void append_cond_list(cond_list_t * cond)
{
cond_list_t *old_cond = get_current_cond_list(cond);
avrule_t *tmp;
assert(old_cond != NULL); /* probably out of memory */
if (old_cond->avtrue_list == NULL) {
old_cond->avtrue_list = cond->avtrue_list;
} else {
for (tmp = old_cond->avtrue_list; tmp->next != NULL;
tmp = tmp->next) ;
tmp->next = cond->avtrue_list;
}
if (old_cond->avfalse_list == NULL) {
old_cond->avfalse_list = cond->avfalse_list;
} else {
for (tmp = old_cond->avfalse_list; tmp->next != NULL;
tmp = tmp->next) ;
tmp->next = cond->avfalse_list;
}
}
void append_avrule(avrule_t * avrule)
{
avrule_decl_t *decl = stack_top->decl;
/* currently avrules follow a completely different code path
* for handling avrules and compute types
* (define_cond_avrule_te_avtab, define_cond_compute_type);
* therefore there ought never be a conditional on top of the
* scope stack */
assert(stack_top->type == 1);
if (stack_top->last_avrule == NULL) {
decl->avrules = avrule;
} else {
stack_top->last_avrule->next = avrule;
}
stack_top->last_avrule = avrule;
}
/* this doesn't actually append, but really prepends it */
void append_role_trans(role_trans_rule_t * role_tr_rules)
{
avrule_decl_t *decl = stack_top->decl;
/* role transitions are not allowed within conditionals */
assert(stack_top->type == 1);
role_tr_rules->next = decl->role_tr_rules;
decl->role_tr_rules = role_tr_rules;
}
/* this doesn't actually append, but really prepends it */
void append_role_allow(role_allow_rule_t * role_allow_rules)
{
avrule_decl_t *decl = stack_top->decl;
/* role allows are not allowed within conditionals */
assert(stack_top->type == 1);
role_allow_rules->next = decl->role_allow_rules;
decl->role_allow_rules = role_allow_rules;
}
/* this doesn't actually append, but really prepends it */
void append_range_trans(range_trans_rule_t * range_tr_rules)
{
avrule_decl_t *decl = stack_top->decl;
/* range transitions are not allowed within conditionals */
assert(stack_top->type == 1);
range_tr_rules->next = decl->range_tr_rules;
decl->range_tr_rules = range_tr_rules;
}
int begin_optional(int pass)
{
avrule_block_t *block = NULL;
avrule_decl_t *decl;
if (pass == 1) {
/* allocate a new avrule block for this optional block */
if ((block = avrule_block_create()) == NULL ||
(decl = avrule_decl_create(next_decl_id)) == NULL) {
goto cleanup;
}
block->flags |= AVRULE_OPTIONAL;
block->branch_list = decl;
last_block->next = block;
} else {
/* select the next block from the chain built during pass 1 */
block = last_block->next;
assert(block != NULL &&
block->branch_list != NULL &&
block->branch_list->decl_id == next_decl_id);
decl = block->branch_list;
}
if (push_stack(1, block, decl) == -1) {
goto cleanup;
}
stack_top->last_avrule = NULL;
last_block = block;
next_decl_id++;
return 0;
cleanup:
yyerror("Out of memory!");
avrule_block_destroy(block);
return -1;
}
int end_optional(int pass)
{
/* once nested conditionals are allowed, do the stack unfolding here */
pop_stack();
return 0;
}
int begin_optional_else(int pass)
{
avrule_decl_t *decl;
assert(stack_top->type == 1 && stack_top->in_else == 0);
if (pass == 1) {
/* allocate a new declaration and add it to the
* current chain */
if ((decl = avrule_decl_create(next_decl_id)) == NULL) {
yyerror("Out of memory!");
return -1;
}
stack_top->decl->next = decl;
} else {
/* pick the (hopefully last) declaration of this
avrule block, built from pass 1 */
decl = stack_top->decl->next;
assert(decl != NULL &&
decl->next == NULL && decl->decl_id == next_decl_id);
}
stack_top->in_else = 1;
stack_top->decl = decl;
stack_top->last_avrule = NULL;
stack_top->require_given = 0;
next_decl_id++;
return 0;
}
static int copy_requirements(avrule_decl_t * dest, scope_stack_t * stack)
{
int i;
if (stack == NULL) {
return 0;
}
if (stack->type == 1) {
scope_index_t *src_scope = &stack->decl->required;
scope_index_t *dest_scope = &dest->required;
for (i = 0; i < SYM_NUM; i++) {
ebitmap_t *src_bitmap = &src_scope->scope[i];
ebitmap_t *dest_bitmap = &dest_scope->scope[i];
if (ebitmap_union(dest_bitmap, src_bitmap)) {
yyerror("Out of memory!");
return -1;
}
}
/* now copy class permissions */
if (src_scope->class_perms_len > dest_scope->class_perms_len) {
ebitmap_t *new_map =
realloc(dest_scope->class_perms_map,
src_scope->class_perms_len *
sizeof(*new_map));
if (new_map == NULL) {
yyerror("Out of memory!");
return -1;
}
dest_scope->class_perms_map = new_map;
for (i = dest_scope->class_perms_len;
i < src_scope->class_perms_len; i++) {
ebitmap_init(dest_scope->class_perms_map + i);
}
dest_scope->class_perms_len =
src_scope->class_perms_len;
}
for (i = 0; i < src_scope->class_perms_len; i++) {
ebitmap_t *src_bitmap = &src_scope->class_perms_map[i];
ebitmap_t *dest_bitmap =
&dest_scope->class_perms_map[i];
if (ebitmap_union(dest_bitmap, src_bitmap)) {
yyerror("Out of memory!");
return -1;
}
}
}
return copy_requirements(dest, stack->parent);
}
/* During pass 1, check that at least one thing was required within
* this block, for those places where a REQUIRED is necessary. During
* pass 2, have this block inherit its parents' requirements. Return
* 0 on success, -1 on failure. */
int end_avrule_block(int pass)
{
avrule_decl_t *decl = stack_top->decl;
assert(stack_top->type == 1);
if (pass == 2) {
/* this avrule_decl inherits all of its parents'
* requirements */
if (copy_requirements(decl, stack_top->parent) == -1) {
return -1;
}
return 0;
}
if (!stack_top->in_else && !stack_top->require_given) {
if (policydbp->policy_type == POLICY_BASE
&& stack_top->parent != NULL) {
/* if this is base no require should be in the global block */
return 0;
} else {
/* non-ELSE branches must have at least one thing required */
yyerror("This block has no require section.");
return -1;
}
}
return 0;
}
/* Push a new scope on to the stack and update the 'last' pointer.
* Return 0 on success, -1 if out * of memory. */
static int push_stack(int stack_type, ...)
{
scope_stack_t *s = calloc(1, sizeof(*s));
va_list ap;
if (s == NULL) {
return -1;
}
va_start(ap, stack_type);
switch (s->type = stack_type) {
case 1:{
s->u.avrule = va_arg(ap, avrule_block_t *);
s->decl = va_arg(ap, avrule_decl_t *);
break;
}
case 2:{
s->u.cond_list = va_arg(ap, cond_list_t *);
break;
}
default:
/* invalid stack type given */
assert(0);
}
va_end(ap);
s->parent = stack_top;
s->child = NULL;
stack_top = s;
return 0;
}
/* Pop off the most recently added from the stack. Update the 'last'
* pointer. */
static void pop_stack(void)
{
scope_stack_t *parent;
assert(stack_top != NULL);
parent = stack_top->parent;
if (parent != NULL) {
parent->child = NULL;
}
free(stack_top);
stack_top = parent;
}