25e9c91a8b
A require statement for a class permission adds that permission to the
class representation for the current module. In case the resulting
class would have more than the supported amount of 32 permissions
assigned the resulting binary module will fail to load at link-time
without an informative error message (since [1]).
Bail out if adding a permission would result in a class having more than
the supported amount of 32 permissions assigned.
[1]: 97af65f696
Closes: https://github.com/SELinuxProject/selinux/issues/356
Reported-by: Julie Pichon
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
Acked-by: James Carter <jwcart2@gmail.com>
1494 lines
35 KiB
C
1494 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(const char *msg);
|
|
__attribute__ ((format(printf, 1, 2)))
|
|
extern void yyerror2(const 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;
|
|
|
|
static const char * const flavor_str[SYM_NUM] = {
|
|
[SYM_COMMONS] = "common",
|
|
[SYM_CLASSES] = "class",
|
|
[SYM_ROLES] = "role",
|
|
[SYM_TYPES] = "type",
|
|
[SYM_USERS] = "user",
|
|
[SYM_BOOLS] = "bool",
|
|
[SYM_LEVELS] = "level",
|
|
[SYM_CATS] = "cat"
|
|
};
|
|
|
|
static void print_error_msg(int ret, uint32_t symbol_type)
|
|
{
|
|
switch (ret) {
|
|
case -3:
|
|
yyerror("Out of memory!");
|
|
break;
|
|
case -2:
|
|
yyerror2("Duplicate declaration of %s", flavor_str[symbol_type]);
|
|
break;
|
|
case -1:
|
|
yyerror2("Could not declare %s here", flavor_str[symbol_type]);
|
|
break;
|
|
default:
|
|
yyerror("Unknown error");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
free(policydbp->name);
|
|
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 or require would
|
|
* be allowed here or 0 if not. For example, declarations and requirements 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_creation_allowed(void)
|
|
{
|
|
if (stack_top->type != 1 || stack_top->in_else) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Attempt to declare or require a symbol within the current scope.
|
|
* Returns:
|
|
* 0: Success - Symbol had not been previously created.
|
|
* 1: Success - Symbol had already been created and caller must free datum.
|
|
* -1: Failure - Symbol cannot be created here
|
|
* -2: Failure - Duplicate declaration or type/attribute mismatch
|
|
* -3: Failure - Out of memory or some other error
|
|
*/
|
|
static int create_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum,
|
|
uint32_t * dest_value, uint32_t scope)
|
|
{
|
|
avrule_decl_t *decl = stack_top->decl;
|
|
int ret;
|
|
|
|
if (!is_creation_allowed()) {
|
|
return -1;
|
|
}
|
|
|
|
ret = symtab_insert(policydbp, symbol_type, key, datum, scope,
|
|
decl->decl_id, dest_value);
|
|
|
|
if (ret == 1 && dest_value) {
|
|
hashtab_datum_t s =
|
|
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 = ((symtab_datum_t *)s)->value;
|
|
}
|
|
} else if (ret == -2) {
|
|
return -2;
|
|
} else if (ret < 0) {
|
|
return -3;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* 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 ret = create_symbol(symbol_type, key, datum, dest_value, SCOPE_DECL);
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ebitmap_set_bit(decl->declared.scope + symbol_type,
|
|
*datum_value - 1, 1)) {
|
|
return -3;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int create_role(uint32_t scope, unsigned char isattr, role_datum_t **role, char **key)
|
|
{
|
|
char *id = queue_remove(id_queue);
|
|
role_datum_t *datum = NULL;
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
*role = NULL;
|
|
*key = NULL;
|
|
isattr = isattr ? ROLE_ATTRIB : ROLE_ROLE;
|
|
|
|
if (id == NULL) {
|
|
yyerror("no role name");
|
|
return -1;
|
|
}
|
|
|
|
datum = malloc(sizeof(*datum));
|
|
if (datum == NULL) {
|
|
yyerror("Out of memory!");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
role_datum_init(datum);
|
|
datum->flavor = isattr;
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
ret = declare_symbol(SYM_ROLES, id, datum, &value, &value);
|
|
} else {
|
|
ret = require_symbol(SYM_ROLES, id, datum, &value, &value);
|
|
}
|
|
|
|
datum->s.value = value;
|
|
|
|
if (ret == 0) {
|
|
*role = datum;
|
|
*key = strdup(id);
|
|
if (*key == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
} else if (ret == 1) {
|
|
*role = hashtab_search(policydbp->symtab[SYM_ROLES].table, id);
|
|
if (*role && (isattr != (*role)->flavor)) {
|
|
yyerror2("Identifier %s used as both an attribute and a role",
|
|
id);
|
|
free(id);
|
|
role_datum_destroy(datum);
|
|
free(datum);
|
|
return -1;
|
|
}
|
|
*role = datum;
|
|
*key = id;
|
|
} else {
|
|
print_error_msg(ret, SYM_ROLES);
|
|
free(id);
|
|
role_datum_destroy(datum);
|
|
free(datum);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
role_datum_t *declare_role(unsigned char isattr)
|
|
{
|
|
char *key = NULL;
|
|
role_datum_t *role = NULL;
|
|
role_datum_t *dest_role = NULL;
|
|
hashtab_t roles_tab;
|
|
int ret, ret2;
|
|
|
|
ret = create_role(SCOPE_DECL, isattr, &role, &key);
|
|
if (ret < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
/* create a new role_datum_t for this decl, if necessary */
|
|
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 = hashtab_search(roles_tab, key);
|
|
if (dest_role == NULL) {
|
|
if (ret == 0) {
|
|
dest_role = malloc(sizeof(*dest_role));
|
|
if (dest_role == NULL) {
|
|
yyerror("Out of memory!");
|
|
free(key);
|
|
return NULL;
|
|
}
|
|
role_datum_init(dest_role);
|
|
dest_role->s.value = role->s.value;
|
|
dest_role->flavor = role->flavor;
|
|
} else {
|
|
dest_role = role;
|
|
}
|
|
ret2 = role_implicit_bounds(roles_tab, key, dest_role);
|
|
if (ret2 != 0) {
|
|
free(key);
|
|
role_datum_destroy(dest_role);
|
|
free(dest_role);
|
|
return NULL;
|
|
}
|
|
ret2 = hashtab_insert(roles_tab, key, dest_role);
|
|
if (ret2 != 0) {
|
|
yyerror("Out of memory!");
|
|
free(key);
|
|
role_datum_destroy(dest_role);
|
|
free(dest_role);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
free(key);
|
|
if (ret == 1) {
|
|
role_datum_destroy(role);
|
|
free(role);
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
ret2 = ebitmap_set_bit(&dest_role->dominates, dest_role->s.value - 1, 1);
|
|
if (ret2 != 0) {
|
|
yyerror("out of memory");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return dest_role;
|
|
}
|
|
|
|
static int create_type(uint32_t scope, unsigned char isattr, type_datum_t **type)
|
|
{
|
|
char *id;
|
|
type_datum_t *datum;
|
|
int ret;
|
|
uint32_t value = 0;
|
|
|
|
*type = NULL;
|
|
isattr = isattr ? TYPE_ATTRIB : TYPE_TYPE;
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no type/attribute name?");
|
|
return -1;
|
|
}
|
|
if (strcmp(id, "self") == 0) {
|
|
yyerror("\"self\" is a reserved type name.");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
datum = malloc(sizeof(*datum));
|
|
if (!datum) {
|
|
yyerror("Out of memory!");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
type_datum_init(datum);
|
|
datum->primary = 1;
|
|
datum->flavor = isattr;
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
ret = declare_symbol(SYM_TYPES, id, datum, &value, &value);
|
|
} else {
|
|
ret = require_symbol(SYM_TYPES, id, datum, &value, &value);
|
|
}
|
|
|
|
if (ret == 0) {
|
|
datum->s.value = value;
|
|
*type = datum;
|
|
} else if (ret == 1) {
|
|
type_datum_destroy(datum);
|
|
free(datum);
|
|
*type = hashtab_search(policydbp->symtab[SYM_TYPES].table, id);
|
|
if (*type && (isattr != (*type)->flavor)) {
|
|
yyerror2("Identifier %s used as both an attribute and a type",
|
|
id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
} else {
|
|
print_error_msg(ret, SYM_TYPES);
|
|
free(id);
|
|
type_datum_destroy(datum);
|
|
free(datum);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
type_datum_t *declare_type(unsigned char primary, unsigned char isattr)
|
|
{
|
|
type_datum_t *type = NULL;
|
|
int ret = create_type(SCOPE_DECL, isattr, &type);
|
|
|
|
if (ret == 0) {
|
|
type->primary = primary;
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int create_user(uint32_t scope, user_datum_t **user, char **key)
|
|
{
|
|
char *id = queue_remove(id_queue);
|
|
user_datum_t *datum = NULL;
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
*user = NULL;
|
|
*key = NULL;
|
|
|
|
if (id == NULL) {
|
|
yyerror("no user name");
|
|
return -1;
|
|
}
|
|
|
|
datum = malloc(sizeof(*datum));
|
|
if (datum == NULL) {
|
|
yyerror("Out of memory!");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
user_datum_init(datum);
|
|
|
|
if (scope == SCOPE_DECL) {
|
|
ret = declare_symbol(SYM_USERS, id, datum, &value, &value);
|
|
} else {
|
|
ret = require_symbol(SYM_USERS, id, datum, &value, &value);
|
|
}
|
|
|
|
datum->s.value = value;
|
|
|
|
if (ret == 0) {
|
|
*user = datum;
|
|
*key = strdup(id);
|
|
if (*key == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
} else if (ret == 1) {
|
|
*user = datum;
|
|
*key = id;
|
|
} else {
|
|
print_error_msg(ret, SYM_USERS);
|
|
free(id);
|
|
user_datum_destroy(datum);
|
|
free(datum);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
user_datum_t *declare_user(void)
|
|
{
|
|
char *key = NULL;
|
|
user_datum_t *user = NULL;
|
|
user_datum_t *dest_user = NULL;
|
|
hashtab_t users_tab;
|
|
int ret, ret2;
|
|
|
|
ret = create_user(SCOPE_DECL, &user, &key);
|
|
if (ret < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
/* create a new user_datum_t for this decl, if necessary */
|
|
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 = hashtab_search(users_tab, key);
|
|
if (dest_user == NULL) {
|
|
if (ret == 0) {
|
|
dest_user = malloc(sizeof(*dest_user));
|
|
if (dest_user == NULL) {
|
|
yyerror("Out of memory!");
|
|
free(key);
|
|
return NULL;
|
|
}
|
|
user_datum_init(dest_user);
|
|
dest_user->s.value = user->s.value;
|
|
} else {
|
|
dest_user = user;
|
|
}
|
|
ret2 = user_implicit_bounds(users_tab, key, dest_user);
|
|
if (ret2 != 0) {
|
|
free(key);
|
|
user_datum_destroy(dest_user);
|
|
free(dest_user);
|
|
return NULL;
|
|
}
|
|
ret2 = hashtab_insert(users_tab, key, dest_user);
|
|
if (ret2 != 0) {
|
|
yyerror("Out of memory!");
|
|
free(key);
|
|
user_datum_destroy(dest_user);
|
|
free(dest_user);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
free(key);
|
|
if (ret == 1) {
|
|
user_datum_destroy(user);
|
|
free(user);
|
|
}
|
|
}
|
|
|
|
return dest_user;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Return a role_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.
|
|
*/
|
|
role_datum_t *get_local_role(char *id, uint32_t value, unsigned char isattr)
|
|
{
|
|
role_datum_t *dest_roledatum;
|
|
hashtab_t roles_tab;
|
|
|
|
assert(stack_top->type == 1);
|
|
|
|
if (stack_top->parent == NULL) {
|
|
/* in global, so use global symbol table */
|
|
roles_tab = policydbp->p_roles.table;
|
|
} else {
|
|
roles_tab = stack_top->decl->p_roles.table;
|
|
}
|
|
|
|
dest_roledatum = hashtab_search(roles_tab, id);
|
|
if (!dest_roledatum) {
|
|
dest_roledatum = (role_datum_t *)malloc(sizeof(role_datum_t));
|
|
if (dest_roledatum == NULL) {
|
|
free(id);
|
|
return NULL;
|
|
}
|
|
|
|
role_datum_init(dest_roledatum);
|
|
dest_roledatum->s.value = value;
|
|
dest_roledatum->flavor = isattr ? ROLE_ATTRIB : ROLE_ROLE;
|
|
|
|
if (hashtab_insert(roles_tab, id, dest_roledatum)) {
|
|
free(id);
|
|
role_datum_destroy(dest_roledatum);
|
|
free(dest_roledatum);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
free(id);
|
|
if (dest_roledatum->flavor != isattr ? ROLE_ATTRIB : ROLE_ROLE)
|
|
return NULL;
|
|
}
|
|
|
|
return dest_roledatum;
|
|
}
|
|
|
|
/* 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 ret = create_symbol(symbol_type, key, datum, dest_value, SCOPE_REQ);
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
if (ebitmap_set_bit(decl->required.scope + symbol_type,
|
|
*datum_value - 1, 1)) {
|
|
return -3;
|
|
}
|
|
|
|
stack_top->require_given = 1;
|
|
return ret;
|
|
}
|
|
|
|
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) {
|
|
uint32_t 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!");
|
|
class_datum_destroy(datum);
|
|
return -1;
|
|
}
|
|
ret =
|
|
require_symbol(SYM_CLASSES, class_id, datum, &datum->s.value,
|
|
&datum->s.value);
|
|
if (ret < 0) {
|
|
print_error_msg(ret, SYM_CLASSES);
|
|
free(class_id);
|
|
class_datum_destroy(datum);
|
|
return -1;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
/* a new class was added; reindex everything */
|
|
if (policydb_index_classes(policydbp)) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
} else {
|
|
class_datum_destroy(datum);
|
|
datum = hashtab_search(policydbp->p_classes.table, class_id);
|
|
assert(datum); /* the class datum should have existed */
|
|
free(class_id);
|
|
}
|
|
|
|
/* 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);
|
|
return -1;
|
|
}
|
|
if (datum->permissions.nprim >= PERM_SYMTAB_SIZE) {
|
|
yyerror2("Class %s would have too many permissions "
|
|
"to fit in an access vector with permission %s",
|
|
policydbp->p_class_val_to_name[datum->s.value - 1],
|
|
perm_id);
|
|
free(perm_id);
|
|
return -1;
|
|
}
|
|
allocated = 1;
|
|
if ((perm = malloc(sizeof(*perm))) == NULL) {
|
|
yyerror("Out of memory!");
|
|
free(perm_id);
|
|
return -1;
|
|
}
|
|
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);
|
|
return -1;
|
|
}
|
|
perm->s.value = datum->permissions.nprim + 1;
|
|
}
|
|
|
|
if (add_perm_to_class(perm->s.value, datum->s.value) == -1) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
|
|
/* Update number of primitives if we allocated one. */
|
|
if (allocated)
|
|
datum->permissions.nprim++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int require_role_or_attribute(int pass, unsigned char isattr)
|
|
{
|
|
char *key = NULL;
|
|
role_datum_t *role = NULL;
|
|
int ret;
|
|
|
|
if (pass == 2) {
|
|
free(queue_remove(id_queue));
|
|
return 0;
|
|
}
|
|
|
|
ret = create_role(SCOPE_REQ, isattr, &role, &key);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
free(key);
|
|
|
|
if (ret == 0) {
|
|
ret = ebitmap_set_bit(&role->dominates, role->s.value - 1, 1);
|
|
if (ret != 0) {
|
|
yyerror("Out of memory");
|
|
return -1;
|
|
}
|
|
} else {
|
|
role_datum_destroy(role);
|
|
free(role);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int require_role(int pass)
|
|
{
|
|
return require_role_or_attribute(pass, 0);
|
|
}
|
|
|
|
int require_attribute_role(int pass)
|
|
{
|
|
return require_role_or_attribute(pass, 1);
|
|
}
|
|
|
|
static int require_type_or_attribute(int pass, unsigned char isattr)
|
|
{
|
|
type_datum_t *type = NULL;
|
|
int ret;
|
|
|
|
if (pass == 2) {
|
|
free(queue_remove(id_queue));
|
|
return 0;
|
|
}
|
|
|
|
ret = create_type(SCOPE_REQ, isattr, &type);
|
|
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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 *key = NULL;
|
|
user_datum_t *user = NULL;
|
|
int ret;
|
|
|
|
if (pass == 1) {
|
|
free(queue_remove(id_queue));
|
|
return 0;
|
|
}
|
|
|
|
ret = create_user(SCOPE_REQ, &user, &key);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
free(key);
|
|
|
|
if (ret == 1) {
|
|
user_datum_destroy(user);
|
|
free(user);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int require_bool_tunable(int pass, int is_tunable)
|
|
{
|
|
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;
|
|
}
|
|
if (is_tunable)
|
|
booldatum->flags |= COND_BOOL_FLAGS_TUNABLE;
|
|
retval =
|
|
require_symbol(SYM_BOOLS, id, booldatum,
|
|
&booldatum->s.value, &booldatum->s.value);
|
|
if (retval != 0) {
|
|
cond_destroy_bool(id, booldatum, NULL);
|
|
if (retval < 0) {
|
|
print_error_msg(retval, SYM_BOOLS);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int require_bool(int pass)
|
|
{
|
|
return require_bool_tunable(pass, 0);
|
|
}
|
|
|
|
int require_tunable(int pass)
|
|
{
|
|
return require_bool_tunable(pass, 1);
|
|
}
|
|
|
|
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, 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);
|
|
if (retval < 0) {
|
|
print_error_msg(retval, SYM_LEVELS);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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, cat,
|
|
&cat->s.value, &cat->s.value);
|
|
if (retval != 0) {
|
|
free(id);
|
|
cat_datum_destroy(cat);
|
|
free(cat);
|
|
if (retval < 0) {
|
|
print_error_msg(retval, SYM_CATS);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_scope_in_stack(const scope_datum_t * scope, const scope_stack_t * stack)
|
|
{
|
|
uint32_t i;
|
|
if (stack == NULL) {
|
|
return 0; /* no matching scope found */
|
|
}
|
|
if (stack->type == 1) {
|
|
const 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, const_hashtab_key_t id)
|
|
{
|
|
const 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,
|
|
const 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,
|
|
const 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(const_hashtab_key_t perm_id, const_hashtab_key_t class_id)
|
|
{
|
|
const class_datum_t *cladatum =
|
|
(class_datum_t *) hashtab_search(policydbp->p_classes.table,
|
|
class_id);
|
|
const 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;
|
|
}
|
|
|
|
old_cond->flags |= cond->flags;
|
|
}
|
|
|
|
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_filename_trans(filename_trans_rule_t * filename_trans_rules)
|
|
{
|
|
avrule_decl_t *decl = stack_top->decl;
|
|
|
|
/* filename transitions are not allowed within conditionals */
|
|
assert(stack_top->type == 1);
|
|
|
|
filename_trans_rules->next = decl->filename_trans_rules;
|
|
decl->filename_trans_rules = filename_trans_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 __attribute__ ((unused)))
|
|
{
|
|
/* 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, const scope_stack_t * stack)
|
|
{
|
|
uint32_t i;
|
|
if (stack == NULL) {
|
|
return 0;
|
|
}
|
|
if (stack->type == 1) {
|
|
const scope_index_t *src_scope = &stack->decl->required;
|
|
scope_index_t *dest_scope = &dest->required;
|
|
for (i = 0; i < SYM_NUM; i++) {
|
|
const 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++) {
|
|
const 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;
|
|
}
|