4626 lines
92 KiB
C
4626 lines
92 KiB
C
/*
|
|
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
|
|
*/
|
|
|
|
/*
|
|
* Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
|
|
*
|
|
* Support for enhanced MLS infrastructure.
|
|
*
|
|
* Updated: David Caplan, <dac@tresys.com>
|
|
*
|
|
* Added conditional policy language extensions
|
|
*
|
|
* Updated: Joshua Brindle <jbrindle@tresys.com>
|
|
* Karl MacMillan <kmacmillan@mentalrootkit.com>
|
|
* Jason Tang <jtang@tresys.com>
|
|
*
|
|
* Added support for binary policy modules
|
|
*
|
|
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
|
* Copyright (C) 2003 - 2008 Tresys Technology, LLC
|
|
* Copyright (C) 2007 Red Hat Inc.
|
|
* 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.
|
|
*/
|
|
|
|
/* FLASK */
|
|
|
|
#include <sys/types.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sepol/policydb/expand.h>
|
|
#include <sepol/policydb/policydb.h>
|
|
#include <sepol/policydb/services.h>
|
|
#include <sepol/policydb/conditional.h>
|
|
#include <sepol/policydb/flask.h>
|
|
#include <sepol/policydb/hierarchy.h>
|
|
#include <sepol/policydb/polcaps.h>
|
|
#include "queue.h"
|
|
#include "checkpolicy.h"
|
|
#include "module_compiler.h"
|
|
#include "policy_define.h"
|
|
|
|
policydb_t *policydbp;
|
|
queue_t id_queue = 0;
|
|
unsigned int pass;
|
|
char *curfile = 0;
|
|
int mlspol = 0;
|
|
|
|
extern unsigned long policydb_lineno;
|
|
extern unsigned long source_lineno;
|
|
extern unsigned int policydb_errors;
|
|
|
|
extern int yywarn(char *msg);
|
|
extern int yyerror(char *msg);
|
|
|
|
#define ERRORMSG_LEN 255
|
|
static char errormsg[ERRORMSG_LEN + 1] = {0};
|
|
|
|
static int id_has_dot(char *id);
|
|
static int parse_security_context(context_struct_t *c);
|
|
|
|
/* initialize all of the state variables for the scanner/parser */
|
|
void init_parser(int pass_number)
|
|
{
|
|
policydb_lineno = 1;
|
|
source_lineno = 1;
|
|
policydb_errors = 0;
|
|
pass = pass_number;
|
|
}
|
|
|
|
void yyerror2(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsnprintf(errormsg, ERRORMSG_LEN, fmt, ap);
|
|
yyerror(errormsg);
|
|
va_end(ap);
|
|
}
|
|
|
|
int insert_separator(int push)
|
|
{
|
|
int error;
|
|
|
|
if (push)
|
|
error = queue_push(id_queue, 0);
|
|
else
|
|
error = queue_insert(id_queue, 0);
|
|
|
|
if (error) {
|
|
yyerror("queue overflow");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int insert_id(char *id, int push)
|
|
{
|
|
char *newid = 0;
|
|
int error;
|
|
|
|
newid = (char *)malloc(strlen(id) + 1);
|
|
if (!newid) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
strcpy(newid, id);
|
|
if (push)
|
|
error = queue_push(id_queue, (queue_element_t) newid);
|
|
else
|
|
error = queue_insert(id_queue, (queue_element_t) newid);
|
|
|
|
if (error) {
|
|
yyerror("queue overflow");
|
|
free(newid);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* If the identifier has a dot within it and that its first character
|
|
is not a dot then return 1, else return 0. */
|
|
static int id_has_dot(char *id)
|
|
{
|
|
if (strchr(id, '.') >= id + 1) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int define_class(void)
|
|
{
|
|
char *id = 0;
|
|
class_datum_t *datum = 0;
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
if (pass == 2) {
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no class name for class definition?");
|
|
return -1;
|
|
}
|
|
datum = (class_datum_t *) malloc(sizeof(class_datum_t));
|
|
if (!datum) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
memset(datum, 0, sizeof(class_datum_t));
|
|
ret = declare_symbol(SYM_CLASSES, id, datum, &value, &value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto bad;
|
|
}
|
|
case -2:{
|
|
yyerror2("duplicate declaration of class %s", id);
|
|
goto bad;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare class here");
|
|
goto bad;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
datum->s.value = value;
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (datum)
|
|
free(datum);
|
|
return -1;
|
|
}
|
|
|
|
int define_permissive(void)
|
|
{
|
|
char *type = NULL;
|
|
struct type_datum *t;
|
|
int rc = 0;
|
|
|
|
type = queue_remove(id_queue);
|
|
|
|
if (!type) {
|
|
yyerror2("forgot to include type in permissive definition?");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (pass == 1)
|
|
goto out;
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, type)) {
|
|
yyerror2("type %s is not within scope", type);
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
t = hashtab_search(policydbp->p_types.table, type);
|
|
if (!t) {
|
|
yyerror2("type is not defined: %s", type);
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (t->flavor == TYPE_ATTRIB) {
|
|
yyerror2("attributes may not be permissive: %s\n", type);
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
t->flags |= TYPE_FLAGS_PERMISSIVE;
|
|
|
|
out:
|
|
free(type);
|
|
return rc;
|
|
}
|
|
|
|
int define_polcap(void)
|
|
{
|
|
char *id = 0;
|
|
int capnum;
|
|
|
|
if (pass == 2) {
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no capability name for policycap definition?");
|
|
goto bad;
|
|
}
|
|
|
|
/* Check for valid cap name -> number mapping */
|
|
capnum = sepol_polcap_getnum(id);
|
|
if (capnum < 0) {
|
|
yyerror2("invalid policy capability name %s", id);
|
|
goto bad;
|
|
}
|
|
|
|
/* Store it */
|
|
if (ebitmap_set_bit(&policydbp->policycaps, capnum, TRUE)) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
|
|
free(id);
|
|
return 0;
|
|
|
|
bad:
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
int define_initial_sid(void)
|
|
{
|
|
char *id = 0;
|
|
ocontext_t *newc = 0, *c, *head;
|
|
|
|
if (pass == 2) {
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no sid name for SID definition?");
|
|
return -1;
|
|
}
|
|
newc = (ocontext_t *) malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
newc->u.name = id;
|
|
context_init(&newc->context[0]);
|
|
head = policydbp->ocontexts[OCON_ISID];
|
|
|
|
for (c = head; c; c = c->next) {
|
|
if (!strcmp(newc->u.name, c->u.name)) {
|
|
yyerror2("duplicate initial SID %s", id);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (head) {
|
|
newc->sid[0] = head->sid[0] + 1;
|
|
} else {
|
|
newc->sid[0] = 1;
|
|
}
|
|
newc->next = head;
|
|
policydbp->ocontexts[OCON_ISID] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (newc)
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_common_perms(void)
|
|
{
|
|
char *id = 0, *perm = 0;
|
|
common_datum_t *comdatum = 0;
|
|
perm_datum_t *perdatum = 0;
|
|
int ret;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no common name for common perm definition?");
|
|
return -1;
|
|
}
|
|
comdatum = hashtab_search(policydbp->p_commons.table, id);
|
|
if (comdatum) {
|
|
yyerror2("duplicate declaration for common %s\n", id);
|
|
return -1;
|
|
}
|
|
comdatum = (common_datum_t *) malloc(sizeof(common_datum_t));
|
|
if (!comdatum) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
memset(comdatum, 0, sizeof(common_datum_t));
|
|
ret = hashtab_insert(policydbp->p_commons.table,
|
|
(hashtab_key_t) id, (hashtab_datum_t) comdatum);
|
|
|
|
if (ret == SEPOL_EEXIST) {
|
|
yyerror("duplicate common definition");
|
|
goto bad;
|
|
}
|
|
if (ret == SEPOL_ENOMEM) {
|
|
yyerror("hash table overflow");
|
|
goto bad;
|
|
}
|
|
comdatum->s.value = policydbp->p_commons.nprim + 1;
|
|
if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE)) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
policydbp->p_commons.nprim++;
|
|
while ((perm = queue_remove(id_queue))) {
|
|
perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t));
|
|
if (!perdatum) {
|
|
yyerror("out of memory");
|
|
goto bad_perm;
|
|
}
|
|
memset(perdatum, 0, sizeof(perm_datum_t));
|
|
perdatum->s.value = comdatum->permissions.nprim + 1;
|
|
|
|
if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) {
|
|
yyerror
|
|
("too many permissions to fit in an access vector");
|
|
goto bad_perm;
|
|
}
|
|
ret = hashtab_insert(comdatum->permissions.table,
|
|
(hashtab_key_t) perm,
|
|
(hashtab_datum_t) perdatum);
|
|
|
|
if (ret == SEPOL_EEXIST) {
|
|
yyerror2("duplicate permission %s in common %s", perm,
|
|
id);
|
|
goto bad_perm;
|
|
}
|
|
if (ret == SEPOL_ENOMEM) {
|
|
yyerror("hash table overflow");
|
|
goto bad_perm;
|
|
}
|
|
comdatum->permissions.nprim++;
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (comdatum)
|
|
free(comdatum);
|
|
return -1;
|
|
|
|
bad_perm:
|
|
if (perm)
|
|
free(perm);
|
|
if (perdatum)
|
|
free(perdatum);
|
|
return -1;
|
|
}
|
|
|
|
int define_av_perms(int inherits)
|
|
{
|
|
char *id;
|
|
class_datum_t *cladatum;
|
|
common_datum_t *comdatum;
|
|
perm_datum_t *perdatum = 0, *perdatum2 = 0;
|
|
int ret;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no tclass name for av perm definition?");
|
|
return -1;
|
|
}
|
|
cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table,
|
|
(hashtab_key_t) id);
|
|
if (!cladatum) {
|
|
yyerror2("class %s is not defined", id);
|
|
goto bad;
|
|
}
|
|
free(id);
|
|
|
|
if (cladatum->comdatum || cladatum->permissions.nprim) {
|
|
yyerror("duplicate access vector definition");
|
|
return -1;
|
|
}
|
|
if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
if (inherits) {
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror
|
|
("no inherits name for access vector definition?");
|
|
return -1;
|
|
}
|
|
comdatum =
|
|
(common_datum_t *) hashtab_search(policydbp->p_commons.
|
|
table,
|
|
(hashtab_key_t) id);
|
|
|
|
if (!comdatum) {
|
|
yyerror2("common %s is not defined", id);
|
|
goto bad;
|
|
}
|
|
cladatum->comkey = id;
|
|
cladatum->comdatum = comdatum;
|
|
|
|
/*
|
|
* Class-specific permissions start with values
|
|
* after the last common permission.
|
|
*/
|
|
cladatum->permissions.nprim += comdatum->permissions.nprim;
|
|
}
|
|
while ((id = queue_remove(id_queue))) {
|
|
perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t));
|
|
if (!perdatum) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
memset(perdatum, 0, sizeof(perm_datum_t));
|
|
perdatum->s.value = ++cladatum->permissions.nprim;
|
|
|
|
if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) {
|
|
yyerror
|
|
("too many permissions to fit in an access vector");
|
|
goto bad;
|
|
}
|
|
if (inherits) {
|
|
/*
|
|
* Class-specific permissions and
|
|
* common permissions exist in the same
|
|
* name space.
|
|
*/
|
|
perdatum2 =
|
|
(perm_datum_t *) hashtab_search(cladatum->comdatum->
|
|
permissions.table,
|
|
(hashtab_key_t) id);
|
|
if (perdatum2) {
|
|
yyerror2("permission %s conflicts with an "
|
|
"inherited permission", id);
|
|
goto bad;
|
|
}
|
|
}
|
|
ret = hashtab_insert(cladatum->permissions.table,
|
|
(hashtab_key_t) id,
|
|
(hashtab_datum_t) perdatum);
|
|
|
|
if (ret == SEPOL_EEXIST) {
|
|
yyerror2("duplicate permission %s", id);
|
|
goto bad;
|
|
}
|
|
if (ret == SEPOL_ENOMEM) {
|
|
yyerror("hash table overflow");
|
|
goto bad;
|
|
}
|
|
if (add_perm_to_class(perdatum->s.value, cladatum->s.value)) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (perdatum)
|
|
free(perdatum);
|
|
return -1;
|
|
}
|
|
|
|
int define_sens(void)
|
|
{
|
|
char *id;
|
|
mls_level_t *level = 0;
|
|
level_datum_t *datum = 0, *aliasdatum = 0;
|
|
int ret;
|
|
uint32_t value; /* dummy variable -- its value is never used */
|
|
|
|
if (!mlspol) {
|
|
yyerror("sensitivity definition in non-MLS configuration");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no sensitivity name for sensitivity definition?");
|
|
return -1;
|
|
}
|
|
if (id_has_dot(id)) {
|
|
yyerror("sensitivity identifiers may not contain periods");
|
|
goto bad;
|
|
}
|
|
level = (mls_level_t *) malloc(sizeof(mls_level_t));
|
|
if (!level) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
mls_level_init(level);
|
|
level->sens = 0; /* actual value set in define_dominance */
|
|
ebitmap_init(&level->cat); /* actual value set in define_level */
|
|
|
|
datum = (level_datum_t *) malloc(sizeof(level_datum_t));
|
|
if (!datum) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
level_datum_init(datum);
|
|
datum->isalias = FALSE;
|
|
datum->level = level;
|
|
|
|
ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto bad;
|
|
}
|
|
case -2:{
|
|
yyerror("duplicate declaration of sensitivity level");
|
|
goto bad;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare sensitivity level here");
|
|
goto bad;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (id_has_dot(id)) {
|
|
yyerror("sensitivity aliases may not contain periods");
|
|
goto bad_alias;
|
|
}
|
|
aliasdatum = (level_datum_t *) malloc(sizeof(level_datum_t));
|
|
if (!aliasdatum) {
|
|
yyerror("out of memory");
|
|
goto bad_alias;
|
|
}
|
|
level_datum_init(aliasdatum);
|
|
aliasdatum->isalias = TRUE;
|
|
aliasdatum->level = level;
|
|
|
|
ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto bad_alias;
|
|
}
|
|
case -2:{
|
|
yyerror
|
|
("duplicate declaration of sensitivity alias");
|
|
goto bad_alias;
|
|
}
|
|
case -1:{
|
|
yyerror
|
|
("could not declare sensitivity alias here");
|
|
goto bad_alias;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (level)
|
|
free(level);
|
|
if (datum) {
|
|
level_datum_destroy(datum);
|
|
free(datum);
|
|
}
|
|
return -1;
|
|
|
|
bad_alias:
|
|
if (id)
|
|
free(id);
|
|
if (aliasdatum) {
|
|
level_datum_destroy(aliasdatum);
|
|
free(aliasdatum);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int define_dominance(void)
|
|
{
|
|
level_datum_t *datum;
|
|
int order;
|
|
char *id;
|
|
|
|
if (!mlspol) {
|
|
yyerror("dominance definition in non-MLS configuration");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
order = 0;
|
|
while ((id = (char *)queue_remove(id_queue))) {
|
|
datum =
|
|
(level_datum_t *) hashtab_search(policydbp->p_levels.table,
|
|
(hashtab_key_t) id);
|
|
if (!datum) {
|
|
yyerror2("unknown sensitivity %s used in dominance "
|
|
"definition", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
if (datum->level->sens != 0) {
|
|
yyerror2("sensitivity %s occurs multiply in dominance "
|
|
"definition", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
datum->level->sens = ++order;
|
|
|
|
/* no need to keep sensitivity name */
|
|
free(id);
|
|
}
|
|
|
|
if (order != policydbp->p_levels.nprim) {
|
|
yyerror
|
|
("all sensitivities must be specified in dominance definition");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int define_category(void)
|
|
{
|
|
char *id;
|
|
cat_datum_t *datum = 0, *aliasdatum = 0;
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
if (!mlspol) {
|
|
yyerror("category definition in non-MLS configuration");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no category name for category definition?");
|
|
return -1;
|
|
}
|
|
if (id_has_dot(id)) {
|
|
yyerror("category identifiers may not contain periods");
|
|
goto bad;
|
|
}
|
|
datum = (cat_datum_t *) malloc(sizeof(cat_datum_t));
|
|
if (!datum) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
cat_datum_init(datum);
|
|
datum->isalias = FALSE;
|
|
|
|
ret = declare_symbol(SYM_CATS, id, datum, &value, &value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto bad;
|
|
}
|
|
case -2:{
|
|
yyerror("duplicate declaration of category");
|
|
goto bad;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare category here");
|
|
goto bad;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
datum->s.value = value;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (id_has_dot(id)) {
|
|
yyerror("category aliases may not contain periods");
|
|
goto bad_alias;
|
|
}
|
|
aliasdatum = (cat_datum_t *) malloc(sizeof(cat_datum_t));
|
|
if (!aliasdatum) {
|
|
yyerror("out of memory");
|
|
goto bad_alias;
|
|
}
|
|
cat_datum_init(aliasdatum);
|
|
aliasdatum->isalias = TRUE;
|
|
aliasdatum->s.value = datum->s.value;
|
|
|
|
ret =
|
|
declare_symbol(SYM_CATS, id, aliasdatum, NULL,
|
|
&datum->s.value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto bad_alias;
|
|
}
|
|
case -2:{
|
|
yyerror
|
|
("duplicate declaration of category aliases");
|
|
goto bad_alias;
|
|
}
|
|
case -1:{
|
|
yyerror
|
|
("could not declare category aliases here");
|
|
goto bad_alias;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
if (id)
|
|
free(id);
|
|
if (datum) {
|
|
cat_datum_destroy(datum);
|
|
free(datum);
|
|
}
|
|
return -1;
|
|
|
|
bad_alias:
|
|
if (id)
|
|
free(id);
|
|
if (aliasdatum) {
|
|
cat_datum_destroy(aliasdatum);
|
|
free(aliasdatum);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int clone_level(hashtab_key_t key, hashtab_datum_t datum, void *arg)
|
|
{
|
|
level_datum_t *levdatum = (level_datum_t *) datum;
|
|
mls_level_t *level = (mls_level_t *) arg, *newlevel;
|
|
|
|
if (levdatum->level == level) {
|
|
levdatum->defined = 1;
|
|
if (!levdatum->isalias)
|
|
return 0;
|
|
newlevel = (mls_level_t *) malloc(sizeof(mls_level_t));
|
|
if (!newlevel)
|
|
return -1;
|
|
if (mls_level_cpy(newlevel, level)) {
|
|
free(newlevel);
|
|
return -1;
|
|
}
|
|
levdatum->level = newlevel;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int define_level(void)
|
|
{
|
|
char *id;
|
|
level_datum_t *levdatum;
|
|
|
|
if (!mlspol) {
|
|
yyerror("level definition in non-MLS configuration");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no level name for level definition?");
|
|
return -1;
|
|
}
|
|
levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table,
|
|
(hashtab_key_t) id);
|
|
if (!levdatum) {
|
|
yyerror2("unknown sensitivity %s used in level definition", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
if (ebitmap_length(&levdatum->level->cat)) {
|
|
yyerror2("sensitivity %s used in multiple level definitions",
|
|
id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
|
|
levdatum->defined = 1;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
cat_datum_t *cdatum;
|
|
int range_start, range_end, i;
|
|
|
|
if (id_has_dot(id)) {
|
|
char *id_start = id;
|
|
char *id_end = strchr(id, '.');
|
|
|
|
*(id_end++) = '\0';
|
|
|
|
cdatum =
|
|
(cat_datum_t *) hashtab_search(policydbp->p_cats.
|
|
table,
|
|
(hashtab_key_t)
|
|
id_start);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_start);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
range_start = cdatum->s.value - 1;
|
|
cdatum =
|
|
(cat_datum_t *) hashtab_search(policydbp->p_cats.
|
|
table,
|
|
(hashtab_key_t)
|
|
id_end);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_end);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
range_end = cdatum->s.value - 1;
|
|
|
|
if (range_end < range_start) {
|
|
yyerror2("category range is invalid");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
} else {
|
|
cdatum =
|
|
(cat_datum_t *) hashtab_search(policydbp->p_cats.
|
|
table,
|
|
(hashtab_key_t) id);
|
|
range_start = range_end = cdatum->s.value - 1;
|
|
}
|
|
|
|
for (i = range_start; i <= range_end; i++) {
|
|
if (ebitmap_set_bit(&levdatum->level->cat, i, TRUE)) {
|
|
yyerror("out of memory");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
free(id);
|
|
}
|
|
|
|
if (hashtab_map
|
|
(policydbp->p_levels.table, clone_level, levdatum->level)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_attrib(void)
|
|
{
|
|
if (pass == 2) {
|
|
free(queue_remove(id_queue));
|
|
return 0;
|
|
}
|
|
|
|
if (declare_type(TRUE, TRUE) == NULL) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int add_aliases_to_type(type_datum_t * type)
|
|
{
|
|
char *id;
|
|
type_datum_t *aliasdatum = NULL;
|
|
int ret;
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (id_has_dot(id)) {
|
|
free(id);
|
|
yyerror
|
|
("type alias identifiers may not contain periods");
|
|
return -1;
|
|
}
|
|
aliasdatum = (type_datum_t *) malloc(sizeof(type_datum_t));
|
|
if (!aliasdatum) {
|
|
free(id);
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
memset(aliasdatum, 0, sizeof(type_datum_t));
|
|
aliasdatum->s.value = type->s.value;
|
|
|
|
ret = declare_symbol(SYM_TYPES, id, aliasdatum,
|
|
NULL, &aliasdatum->s.value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto cleanup;
|
|
}
|
|
case -2:{
|
|
yyerror2("duplicate declaration of alias %s",
|
|
id);
|
|
goto cleanup;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare alias here");
|
|
goto cleanup;
|
|
}
|
|
case 0: break;
|
|
case 1:{
|
|
/* ret == 1 means the alias was required and therefore already
|
|
* has a value. Set it up as an alias with a different primary. */
|
|
type_datum_destroy(aliasdatum);
|
|
free(aliasdatum);
|
|
|
|
aliasdatum = hashtab_search(policydbp->symtab[SYM_TYPES].table, id);
|
|
assert(aliasdatum);
|
|
|
|
aliasdatum->primary = type->s.value;
|
|
aliasdatum->flavor = TYPE_ALIAS;
|
|
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
cleanup:
|
|
free(id);
|
|
type_datum_destroy(aliasdatum);
|
|
free(aliasdatum);
|
|
return -1;
|
|
}
|
|
|
|
int define_typealias(void)
|
|
{
|
|
char *id;
|
|
type_datum_t *t;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no type name for typealias definition?");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
t = hashtab_search(policydbp->p_types.table, id);
|
|
if (!t || t->flavor == TYPE_ATTRIB) {
|
|
yyerror2("unknown type %s, or it was already declared as an "
|
|
"attribute", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
return add_aliases_to_type(t);
|
|
}
|
|
|
|
int define_typeattribute(void)
|
|
{
|
|
char *id;
|
|
type_datum_t *t, *attr;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no type name for typeattribute definition?");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
t = hashtab_search(policydbp->p_types.table, id);
|
|
if (!t || t->flavor == TYPE_ATTRIB) {
|
|
yyerror2("unknown type %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("attribute %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
attr = hashtab_search(policydbp->p_types.table, id);
|
|
if (!attr) {
|
|
/* treat it as a fatal error */
|
|
yyerror2("attribute %s is not declared", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if (attr->flavor != TYPE_ATTRIB) {
|
|
yyerror2("%s is a type, not an attribute", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
|
|
if (ebitmap_set_bit(&attr->types, (t->s.value - 1), TRUE)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int define_typebounds_helper(char *bounds_id, char *type_id)
|
|
{
|
|
type_datum_t *bounds, *type;
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, bounds_id)) {
|
|
yyerror2("type %s is not within scope", bounds_id);
|
|
return -1;
|
|
}
|
|
|
|
bounds = hashtab_search(policydbp->p_types.table, bounds_id);
|
|
if (!bounds || bounds->flavor == TYPE_ATTRIB) {
|
|
yyerror2("hoge unknown type %s", bounds_id);
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, type_id)) {
|
|
yyerror2("type %s is not within scope", type_id);
|
|
return -1;
|
|
}
|
|
|
|
type = hashtab_search(policydbp->p_types.table, type_id);
|
|
if (!type || type->flavor == TYPE_ATTRIB) {
|
|
yyerror2("type %s is not declared", type_id);
|
|
return -1;
|
|
}
|
|
|
|
if (type->flavor == TYPE_TYPE && !type->primary) {
|
|
type = policydbp->type_val_to_struct[type->s.value - 1];
|
|
} else if (type->flavor == TYPE_ALIAS) {
|
|
type = policydbp->type_val_to_struct[type->primary - 1];
|
|
}
|
|
|
|
if (!type->bounds)
|
|
type->bounds = bounds->s.value;
|
|
else if (type->bounds != bounds->s.value) {
|
|
yyerror2("type %s has inconsistent master {%s,%s}",
|
|
type_id,
|
|
policydbp->p_type_val_to_name[type->bounds - 1],
|
|
policydbp->p_type_val_to_name[bounds->s.value - 1]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_typebounds(void)
|
|
{
|
|
char *bounds, *id;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
bounds = (char *) queue_remove(id_queue);
|
|
if (!bounds) {
|
|
yyerror("no type name for typebounds definition?");
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (define_typebounds_helper(bounds, id))
|
|
return -1;
|
|
free(id);
|
|
}
|
|
free(bounds);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_type(int alias)
|
|
{
|
|
char *id;
|
|
type_datum_t *datum, *attr;
|
|
|
|
if (pass == 2) {
|
|
/*
|
|
* If type name contains ".", we have to define boundary
|
|
* relationship implicitly to keep compatibility with
|
|
* old name based hierarchy.
|
|
*/
|
|
if ((id = queue_remove(id_queue))) {
|
|
char *bounds, *delim;
|
|
|
|
if ((delim = strrchr(id, '.'))
|
|
&& (bounds = strdup(id))) {
|
|
bounds[(size_t)(delim - id)] = '\0';
|
|
|
|
if (define_typebounds_helper(bounds, id))
|
|
return -1;
|
|
free(bounds);
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
if (alias) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
if ((datum = declare_type(TRUE, FALSE)) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (alias) {
|
|
if (add_aliases_to_type(datum) == -1) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("attribute %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
attr = hashtab_search(policydbp->p_types.table, id);
|
|
if (!attr) {
|
|
/* treat it as a fatal error */
|
|
yyerror2("attribute %s is not declared", id);
|
|
return -1;
|
|
}
|
|
|
|
if (attr->flavor != TYPE_ATTRIB) {
|
|
yyerror2("%s is a type, not an attribute", id);
|
|
return -1;
|
|
}
|
|
|
|
if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
|
|
if (ebitmap_set_bit(&attr->types, datum->s.value - 1, TRUE)) {
|
|
yyerror("Out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct val_to_name {
|
|
unsigned int val;
|
|
char *name;
|
|
};
|
|
|
|
/* Adds a type, given by its textual name, to a typeset. If *add is
|
|
0, then add the type to the negative set; otherwise if *add is 1
|
|
then add it to the positive side. */
|
|
static int set_types(type_set_t * set, char *id, int *add, char starallowed)
|
|
{
|
|
type_datum_t *t;
|
|
|
|
if (strcmp(id, "*") == 0) {
|
|
if (!starallowed) {
|
|
yyerror("* not allowed in this type of rule");
|
|
return -1;
|
|
}
|
|
/* set TYPE_STAR flag */
|
|
set->flags = TYPE_STAR;
|
|
free(id);
|
|
*add = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(id, "~") == 0) {
|
|
if (!starallowed) {
|
|
yyerror("~ not allowed in this type of rule");
|
|
return -1;
|
|
}
|
|
/* complement the set */
|
|
set->flags = TYPE_COMP;
|
|
free(id);
|
|
*add = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(id, "-") == 0) {
|
|
*add = 0;
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
t = hashtab_search(policydbp->p_types.table, id);
|
|
if (!t) {
|
|
yyerror2("unknown type %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if (*add == 0) {
|
|
if (ebitmap_set_bit(&set->negset, t->s.value - 1, TRUE))
|
|
goto oom;
|
|
} else {
|
|
if (ebitmap_set_bit(&set->types, t->s.value - 1, TRUE))
|
|
goto oom;
|
|
}
|
|
free(id);
|
|
*add = 1;
|
|
return 0;
|
|
oom:
|
|
yyerror("Out of memory");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
int define_compute_type_helper(int which, avrule_t ** rule)
|
|
{
|
|
char *id;
|
|
type_datum_t *datum;
|
|
class_datum_t *cladatum;
|
|
ebitmap_t tclasses;
|
|
ebitmap_node_t *node;
|
|
avrule_t *avrule;
|
|
class_perm_node_t *perm;
|
|
int i, add = 1;
|
|
|
|
avrule = malloc(sizeof(avrule_t));
|
|
if (!avrule) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
avrule_init(avrule);
|
|
avrule->specified = which;
|
|
avrule->line = policydb_lineno;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&avrule->stypes, id, &add, 0))
|
|
return -1;
|
|
}
|
|
add = 1;
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&avrule->ttypes, id, &add, 0))
|
|
return -1;
|
|
}
|
|
|
|
ebitmap_init(&tclasses);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
cladatum = hashtab_search(policydbp->p_classes.table, id);
|
|
if (!cladatum) {
|
|
yyerror2("unknown class %s", id);
|
|
goto bad;
|
|
}
|
|
if (ebitmap_set_bit(&tclasses, cladatum->s.value - 1, TRUE)) {
|
|
yyerror("Out of memory");
|
|
goto bad;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no newtype?");
|
|
goto bad;
|
|
}
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
datum = (type_datum_t *) hashtab_search(policydbp->p_types.table,
|
|
(hashtab_key_t) id);
|
|
if (!datum || datum->flavor == TYPE_ATTRIB) {
|
|
yyerror2("unknown type %s", id);
|
|
goto bad;
|
|
}
|
|
|
|
ebitmap_for_each_bit(&tclasses, node, i) {
|
|
if (ebitmap_node_get_bit(node, i)) {
|
|
perm = malloc(sizeof(class_perm_node_t));
|
|
if (!perm) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
class_perm_node_init(perm);
|
|
perm->class = i + 1;
|
|
perm->data = datum->s.value;
|
|
perm->next = avrule->perms;
|
|
avrule->perms = perm;
|
|
}
|
|
}
|
|
ebitmap_destroy(&tclasses);
|
|
|
|
*rule = avrule;
|
|
return 0;
|
|
|
|
bad:
|
|
avrule_destroy(avrule);
|
|
free(avrule);
|
|
return -1;
|
|
}
|
|
|
|
int define_compute_type(int which)
|
|
{
|
|
char *id;
|
|
avrule_t *avrule;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
if (define_compute_type_helper(which, &avrule))
|
|
return -1;
|
|
|
|
append_avrule(avrule);
|
|
return 0;
|
|
}
|
|
|
|
avrule_t *define_cond_compute_type(int which)
|
|
{
|
|
char *id;
|
|
avrule_t *avrule;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return (avrule_t *) 1;
|
|
}
|
|
|
|
if (define_compute_type_helper(which, &avrule))
|
|
return COND_ERR;
|
|
|
|
return avrule;
|
|
}
|
|
|
|
int define_bool(void)
|
|
{
|
|
char *id, *bool_value;
|
|
cond_bool_datum_t *datum;
|
|
int ret;
|
|
uint32_t value;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no identifier for bool definition?");
|
|
return -1;
|
|
}
|
|
if (id_has_dot(id)) {
|
|
free(id);
|
|
yyerror("boolean identifiers may not contain periods");
|
|
return -1;
|
|
}
|
|
datum = (cond_bool_datum_t *) malloc(sizeof(cond_bool_datum_t));
|
|
if (!datum) {
|
|
yyerror("out of memory");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
memset(datum, 0, sizeof(cond_bool_datum_t));
|
|
ret = declare_symbol(SYM_BOOLS, id, datum, &value, &value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto cleanup;
|
|
}
|
|
case -2:{
|
|
yyerror2("duplicate declaration of boolean %s", id);
|
|
goto cleanup;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare boolean here");
|
|
goto cleanup;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
datum->s.value = value;
|
|
|
|
bool_value = (char *)queue_remove(id_queue);
|
|
if (!bool_value) {
|
|
yyerror("no default value for bool definition?");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
datum->state = (int)(bool_value[0] == 'T') ? 1 : 0;
|
|
return 0;
|
|
cleanup:
|
|
cond_destroy_bool(id, datum, NULL);
|
|
return -1;
|
|
}
|
|
|
|
avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl)
|
|
{
|
|
if (pass == 1) {
|
|
/* return something so we get through pass 1 */
|
|
return (avrule_t *) 1;
|
|
}
|
|
|
|
if (sl == NULL) {
|
|
/* This is a require block, return previous list */
|
|
return avlist;
|
|
}
|
|
|
|
/* prepend the new avlist to the pre-existing one */
|
|
sl->next = avlist;
|
|
return sl;
|
|
}
|
|
|
|
int define_te_avtab_helper(int which, avrule_t ** rule)
|
|
{
|
|
char *id;
|
|
class_datum_t *cladatum;
|
|
perm_datum_t *perdatum = NULL;
|
|
class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL;
|
|
ebitmap_t tclasses;
|
|
ebitmap_node_t *node;
|
|
avrule_t *avrule;
|
|
unsigned int i;
|
|
int add = 1, ret = 0;
|
|
int suppress = 0;
|
|
|
|
avrule = (avrule_t *) malloc(sizeof(avrule_t));
|
|
if (!avrule) {
|
|
yyerror("memory error");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
avrule_init(avrule);
|
|
avrule->specified = which;
|
|
avrule->line = policydb_lineno;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types
|
|
(&avrule->stypes, id, &add,
|
|
which == AVRULE_NEVERALLOW ? 1 : 0)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
add = 1;
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (strcmp(id, "self") == 0) {
|
|
free(id);
|
|
avrule->flags |= RULE_SELF;
|
|
continue;
|
|
}
|
|
if (set_types
|
|
(&avrule->ttypes, id, &add,
|
|
which == AVRULE_NEVERALLOW ? 1 : 0)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ebitmap_init(&tclasses);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
cladatum = hashtab_search(policydbp->p_classes.table, id);
|
|
if (!cladatum) {
|
|
yyerror2("unknown class %s used in rule", id);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (ebitmap_set_bit(&tclasses, cladatum->s.value - 1, TRUE)) {
|
|
yyerror("Out of memory");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
perms = NULL;
|
|
ebitmap_for_each_bit(&tclasses, node, i) {
|
|
if (!ebitmap_node_get_bit(node, i))
|
|
continue;
|
|
cur_perms =
|
|
(class_perm_node_t *) malloc(sizeof(class_perm_node_t));
|
|
if (!cur_perms) {
|
|
yyerror("out of memory");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
class_perm_node_init(cur_perms);
|
|
cur_perms->class = i + 1;
|
|
if (!perms)
|
|
perms = cur_perms;
|
|
if (tail)
|
|
tail->next = cur_perms;
|
|
tail = cur_perms;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
cur_perms = perms;
|
|
ebitmap_for_each_bit(&tclasses, node, i) {
|
|
if (!ebitmap_node_get_bit(node, i))
|
|
continue;
|
|
cladatum = policydbp->class_val_to_struct[i];
|
|
|
|
if (strcmp(id, "*") == 0) {
|
|
/* set all permissions in the class */
|
|
cur_perms->data = ~0U;
|
|
goto next;
|
|
}
|
|
|
|
if (strcmp(id, "~") == 0) {
|
|
/* complement the set */
|
|
if (which == AVRULE_DONTAUDIT)
|
|
yywarn("dontaudit rule with a ~?");
|
|
cur_perms->data = ~cur_perms->data;
|
|
goto next;
|
|
}
|
|
|
|
perdatum =
|
|
hashtab_search(cladatum->permissions.table, id);
|
|
if (!perdatum) {
|
|
if (cladatum->comdatum) {
|
|
perdatum =
|
|
hashtab_search(cladatum->comdatum->
|
|
permissions.table,
|
|
id);
|
|
}
|
|
}
|
|
if (!perdatum) {
|
|
if (!suppress)
|
|
yyerror2("permission %s is not defined"
|
|
" for class %s", id,
|
|
policydbp->p_class_val_to_name[i]);
|
|
continue;
|
|
} else
|
|
if (!is_perm_in_scope
|
|
(id, policydbp->p_class_val_to_name[i])) {
|
|
if (!suppress) {
|
|
yyerror2("permission %s of class %s is"
|
|
" not within scope", id,
|
|
policydbp->p_class_val_to_name[i]);
|
|
}
|
|
continue;
|
|
} else {
|
|
cur_perms->data |= 1U << (perdatum->s.value - 1);
|
|
}
|
|
next:
|
|
cur_perms = cur_perms->next;
|
|
}
|
|
|
|
free(id);
|
|
}
|
|
|
|
ebitmap_destroy(&tclasses);
|
|
|
|
avrule->perms = perms;
|
|
*rule = avrule;
|
|
|
|
out:
|
|
return ret;
|
|
|
|
}
|
|
|
|
avrule_t *define_cond_te_avtab(int which)
|
|
{
|
|
char *id;
|
|
avrule_t *avrule;
|
|
int i;
|
|
|
|
if (pass == 1) {
|
|
for (i = 0; i < 4; i++) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
}
|
|
return (avrule_t *) 1; /* any non-NULL value */
|
|
}
|
|
|
|
if (define_te_avtab_helper(which, &avrule))
|
|
return COND_ERR;
|
|
|
|
return avrule;
|
|
}
|
|
|
|
int define_te_avtab(int which)
|
|
{
|
|
char *id;
|
|
avrule_t *avrule;
|
|
int i;
|
|
|
|
if (pass == 1) {
|
|
for (i = 0; i < 4; i++) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (define_te_avtab_helper(which, &avrule))
|
|
return -1;
|
|
|
|
/* append this avrule to the end of the current rules list */
|
|
append_avrule(avrule);
|
|
return 0;
|
|
}
|
|
|
|
/* The role-types rule is no longer used to declare regular role or
|
|
* role attribute, but solely aimed for declaring role-types associations.
|
|
*/
|
|
int define_role_types(void)
|
|
{
|
|
role_datum_t *role;
|
|
char *id;
|
|
int add = 1;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no role name for role-types rule?");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
role = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!role) {
|
|
yyerror2("unknown role %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&role->types, id, &add, 0))
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_attrib_role(void)
|
|
{
|
|
if (pass == 2) {
|
|
free(queue_remove(id_queue));
|
|
return 0;
|
|
}
|
|
|
|
/* Declare a role attribute */
|
|
if (declare_role(TRUE) == NULL)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_role_attr(void)
|
|
{
|
|
char *id;
|
|
role_datum_t *r, *attr;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
/* Declare a regular role */
|
|
if ((r = declare_role(FALSE)) == NULL)
|
|
return -1;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("attribute %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
attr = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!attr) {
|
|
/* treat it as a fatal error */
|
|
yyerror2("role attribute %s is not declared", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if (attr->flavor != ROLE_ATTRIB) {
|
|
yyerror2("%s is a regular role, not an attribute", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if ((attr = get_local_role(id, attr->s.value, 1)) == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
|
|
if (ebitmap_set_bit(&attr->roles, (r->s.value - 1), TRUE)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_roleattribute(void)
|
|
{
|
|
char *id;
|
|
role_datum_t *r, *attr;
|
|
|
|
if (pass == 2) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no role name for roleattribute definition?");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
r = hashtab_search(policydbp->p_roles.table, id);
|
|
/* We support adding one role attribute into another */
|
|
if (!r) {
|
|
yyerror2("unknown role %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("attribute %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
attr = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!attr) {
|
|
/* treat it as a fatal error */
|
|
yyerror2("role attribute %s is not declared", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if (attr->flavor != ROLE_ATTRIB) {
|
|
yyerror2("%s is a regular role, not an attribute", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if ((attr = get_local_role(id, attr->s.value, 1)) == NULL) {
|
|
yyerror("Out of memory!");
|
|
return -1;
|
|
}
|
|
|
|
if (ebitmap_set_bit(&attr->roles, (r->s.value - 1), TRUE)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
role_datum_t *merge_roles_dom(role_datum_t * r1, role_datum_t * r2)
|
|
{
|
|
role_datum_t *new;
|
|
|
|
if (pass == 1) {
|
|
return (role_datum_t *) 1; /* any non-NULL value */
|
|
}
|
|
|
|
new = malloc(sizeof(role_datum_t));
|
|
if (!new) {
|
|
yyerror("out of memory");
|
|
return NULL;
|
|
}
|
|
memset(new, 0, sizeof(role_datum_t));
|
|
new->s.value = 0; /* temporary role */
|
|
if (ebitmap_or(&new->dominates, &r1->dominates, &r2->dominates)) {
|
|
yyerror("out of memory");
|
|
return NULL;
|
|
}
|
|
if (ebitmap_or(&new->types.types, &r1->types.types, &r2->types.types)) {
|
|
yyerror("out of memory");
|
|
return NULL;
|
|
}
|
|
if (!r1->s.value) {
|
|
/* free intermediate result */
|
|
type_set_destroy(&r1->types);
|
|
ebitmap_destroy(&r1->dominates);
|
|
free(r1);
|
|
}
|
|
if (!r2->s.value) {
|
|
/* free intermediate result */
|
|
yyerror("right hand role is temporary?");
|
|
type_set_destroy(&r2->types);
|
|
ebitmap_destroy(&r2->dominates);
|
|
free(r2);
|
|
}
|
|
return new;
|
|
}
|
|
|
|
/* This function eliminates the ordering dependency of role dominance rule */
|
|
static int dominate_role_recheck(hashtab_key_t key, hashtab_datum_t datum,
|
|
void *arg)
|
|
{
|
|
role_datum_t *rdp = (role_datum_t *) arg;
|
|
role_datum_t *rdatum = (role_datum_t *) datum;
|
|
ebitmap_node_t *node;
|
|
int i;
|
|
|
|
/* Don't bother to process against self role */
|
|
if (rdatum->s.value == rdp->s.value)
|
|
return 0;
|
|
|
|
/* If a dominating role found */
|
|
if (ebitmap_get_bit(&(rdatum->dominates), rdp->s.value - 1)) {
|
|
ebitmap_t types;
|
|
ebitmap_init(&types);
|
|
if (type_set_expand(&rdp->types, &types, policydbp, 1)) {
|
|
ebitmap_destroy(&types);
|
|
return -1;
|
|
}
|
|
/* raise types and dominates from dominated role */
|
|
ebitmap_for_each_bit(&rdp->dominates, node, i) {
|
|
if (ebitmap_node_get_bit(node, i))
|
|
if (ebitmap_set_bit
|
|
(&rdatum->dominates, i, TRUE))
|
|
goto oom;
|
|
}
|
|
ebitmap_for_each_bit(&types, node, i) {
|
|
if (ebitmap_node_get_bit(node, i))
|
|
if (ebitmap_set_bit
|
|
(&rdatum->types.types, i, TRUE))
|
|
goto oom;
|
|
}
|
|
ebitmap_destroy(&types);
|
|
}
|
|
|
|
/* go through all the roles */
|
|
return 0;
|
|
oom:
|
|
yyerror("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
role_datum_t *define_role_dom(role_datum_t * r)
|
|
{
|
|
role_datum_t *role;
|
|
char *role_id;
|
|
ebitmap_node_t *node;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
if (pass == 1) {
|
|
role_id = queue_remove(id_queue);
|
|
free(role_id);
|
|
return (role_datum_t *) 1; /* any non-NULL value */
|
|
}
|
|
|
|
yywarn("Role dominance has been deprecated");
|
|
|
|
role_id = queue_remove(id_queue);
|
|
if (!is_id_in_scope(SYM_ROLES, role_id)) {
|
|
yyerror2("role %s is not within scope", role_id);
|
|
free(role_id);
|
|
return NULL;
|
|
}
|
|
role = (role_datum_t *) hashtab_search(policydbp->p_roles.table,
|
|
role_id);
|
|
if (!role) {
|
|
role = (role_datum_t *) malloc(sizeof(role_datum_t));
|
|
if (!role) {
|
|
yyerror("out of memory");
|
|
free(role_id);
|
|
return NULL;
|
|
}
|
|
memset(role, 0, sizeof(role_datum_t));
|
|
ret =
|
|
declare_symbol(SYM_ROLES, (hashtab_key_t) role_id,
|
|
(hashtab_datum_t) role, &role->s.value,
|
|
&role->s.value);
|
|
switch (ret) {
|
|
case -3:{
|
|
yyerror("Out of memory!");
|
|
goto cleanup;
|
|
}
|
|
case -2:{
|
|
yyerror2("duplicate declaration of role %s",
|
|
role_id);
|
|
goto cleanup;
|
|
}
|
|
case -1:{
|
|
yyerror("could not declare role here");
|
|
goto cleanup;
|
|
}
|
|
case 0:
|
|
case 1:{
|
|
break;
|
|
}
|
|
default:{
|
|
assert(0); /* should never get here */
|
|
}
|
|
}
|
|
if (ebitmap_set_bit(&role->dominates, role->s.value - 1, TRUE)) {
|
|
yyerror("Out of memory!");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (r) {
|
|
ebitmap_t types;
|
|
ebitmap_init(&types);
|
|
ebitmap_for_each_bit(&r->dominates, node, i) {
|
|
if (ebitmap_node_get_bit(node, i))
|
|
if (ebitmap_set_bit(&role->dominates, i, TRUE))
|
|
goto oom;
|
|
}
|
|
if (type_set_expand(&r->types, &types, policydbp, 1)) {
|
|
ebitmap_destroy(&types);
|
|
return NULL;
|
|
}
|
|
ebitmap_for_each_bit(&types, node, i) {
|
|
if (ebitmap_node_get_bit(node, i))
|
|
if (ebitmap_set_bit
|
|
(&role->types.types, i, TRUE))
|
|
goto oom;
|
|
}
|
|
ebitmap_destroy(&types);
|
|
if (!r->s.value) {
|
|
/* free intermediate result */
|
|
type_set_destroy(&r->types);
|
|
ebitmap_destroy(&r->dominates);
|
|
free(r);
|
|
}
|
|
/*
|
|
* Now go through all the roles and escalate this role's
|
|
* dominates and types if a role dominates this role.
|
|
*/
|
|
hashtab_map(policydbp->p_roles.table,
|
|
dominate_role_recheck, role);
|
|
}
|
|
return role;
|
|
cleanup:
|
|
free(role_id);
|
|
role_datum_destroy(role);
|
|
free(role);
|
|
return NULL;
|
|
oom:
|
|
yyerror("Out of memory");
|
|
goto cleanup;
|
|
}
|
|
|
|
static int role_val_to_name_helper(hashtab_key_t key, hashtab_datum_t datum,
|
|
void *p)
|
|
{
|
|
struct val_to_name *v = p;
|
|
role_datum_t *roldatum;
|
|
|
|
roldatum = (role_datum_t *) datum;
|
|
|
|
if (v->val == roldatum->s.value) {
|
|
v->name = key;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static char *role_val_to_name(unsigned int val)
|
|
{
|
|
struct val_to_name v;
|
|
int rc;
|
|
|
|
v.val = val;
|
|
rc = hashtab_map(policydbp->p_roles.table, role_val_to_name_helper, &v);
|
|
if (rc)
|
|
return v.name;
|
|
return NULL;
|
|
}
|
|
|
|
static int set_roles(role_set_t * set, char *id)
|
|
{
|
|
role_datum_t *r;
|
|
|
|
if (strcmp(id, "*") == 0) {
|
|
free(id);
|
|
yyerror("* is not allowed for role sets");
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(id, "~") == 0) {
|
|
free(id);
|
|
yyerror("~ is not allowed for role sets");
|
|
return -1;
|
|
}
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
r = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!r) {
|
|
yyerror2("unknown role %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
if (ebitmap_set_bit(&set->roles, r->s.value - 1, TRUE)) {
|
|
yyerror("out of memory");
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
int define_role_trans(int class_specified)
|
|
{
|
|
char *id;
|
|
role_datum_t *role;
|
|
role_set_t roles;
|
|
type_set_t types;
|
|
class_datum_t *cladatum;
|
|
ebitmap_t e_types, e_roles, e_classes;
|
|
ebitmap_node_t *tnode, *rnode, *cnode;
|
|
struct role_trans *tr = NULL;
|
|
struct role_trans_rule *rule = NULL;
|
|
unsigned int i, j, k;
|
|
int add = 1;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
if (class_specified)
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
role_set_init(&roles);
|
|
ebitmap_init(&e_roles);
|
|
type_set_init(&types);
|
|
ebitmap_init(&e_types);
|
|
ebitmap_init(&e_classes);
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_roles(&roles, id))
|
|
return -1;
|
|
}
|
|
add = 1;
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&types, id, &add, 0))
|
|
return -1;
|
|
}
|
|
|
|
if (class_specified) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
cladatum = hashtab_search(policydbp->p_classes.table,
|
|
id);
|
|
if (!cladatum) {
|
|
yyerror2("unknow class %s", id);
|
|
return -1;
|
|
}
|
|
|
|
ebitmap_set_bit(&e_classes, cladatum->s.value - 1, TRUE);
|
|
free(id);
|
|
}
|
|
} else {
|
|
cladatum = hashtab_search(policydbp->p_classes.table,
|
|
"process");
|
|
if (!cladatum) {
|
|
yyerror2("could not find process class for "
|
|
"legacy role_transition statement");
|
|
return -1;
|
|
}
|
|
|
|
ebitmap_set_bit(&e_classes, cladatum->s.value - 1, TRUE);
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no new role in transition definition?");
|
|
goto bad;
|
|
}
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
role = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!role) {
|
|
yyerror2("unknown role %s used in transition definition", id);
|
|
goto bad;
|
|
}
|
|
|
|
if (role->flavor != ROLE_ROLE) {
|
|
yyerror2("the new role %s must be a regular role", id);
|
|
goto bad;
|
|
}
|
|
|
|
/* This ebitmap business is just to ensure that there are not conflicting role_trans rules */
|
|
if (role_set_expand(&roles, &e_roles, policydbp, NULL, NULL))
|
|
goto bad;
|
|
|
|
if (type_set_expand(&types, &e_types, policydbp, 1))
|
|
goto bad;
|
|
|
|
ebitmap_for_each_bit(&e_roles, rnode, i) {
|
|
if (!ebitmap_node_get_bit(rnode, i))
|
|
continue;
|
|
ebitmap_for_each_bit(&e_types, tnode, j) {
|
|
if (!ebitmap_node_get_bit(tnode, j))
|
|
continue;
|
|
ebitmap_for_each_bit(&e_classes, cnode, k) {
|
|
if (!ebitmap_node_get_bit(cnode, k))
|
|
continue;
|
|
for (tr = policydbp->role_tr; tr;
|
|
tr = tr->next) {
|
|
if (tr->role == (i + 1) &&
|
|
tr->type == (j + 1) &&
|
|
tr->tclass == (k + 1)) {
|
|
yyerror2("duplicate role "
|
|
"transition for "
|
|
"(%s,%s,%s)",
|
|
role_val_to_name(i+1),
|
|
policydbp->p_type_val_to_name[j],
|
|
policydbp->p_class_val_to_name[k]);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
tr = malloc(sizeof(struct role_trans));
|
|
if (!tr) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(tr, 0, sizeof(struct role_trans));
|
|
tr->role = i + 1;
|
|
tr->type = j + 1;
|
|
tr->tclass = k + 1;
|
|
tr->new_role = role->s.value;
|
|
tr->next = policydbp->role_tr;
|
|
policydbp->role_tr = tr;
|
|
}
|
|
}
|
|
}
|
|
/* Now add the real rule */
|
|
rule = malloc(sizeof(struct role_trans_rule));
|
|
if (!rule) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(rule, 0, sizeof(struct role_trans_rule));
|
|
rule->roles = roles;
|
|
rule->types = types;
|
|
rule->classes = e_classes;
|
|
rule->new_role = role->s.value;
|
|
|
|
append_role_trans(rule);
|
|
|
|
ebitmap_destroy(&e_roles);
|
|
ebitmap_destroy(&e_types);
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
return -1;
|
|
}
|
|
|
|
int define_role_allow(void)
|
|
{
|
|
char *id;
|
|
struct role_allow_rule *ra = 0;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
ra = malloc(sizeof(role_allow_rule_t));
|
|
if (!ra) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
role_allow_rule_init(ra);
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_roles(&ra->roles, id))
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_roles(&ra->new_roles, id))
|
|
return -1;
|
|
}
|
|
|
|
append_role_allow(ra);
|
|
return 0;
|
|
}
|
|
|
|
avrule_t *define_cond_filename_trans(void)
|
|
{
|
|
yyerror("type transitions with a filename not allowed inside "
|
|
"conditionals\n");
|
|
return COND_ERR;
|
|
}
|
|
|
|
int define_filename_trans(void)
|
|
{
|
|
char *id, *name = NULL;
|
|
type_set_t stypes, ttypes;
|
|
ebitmap_t e_stypes, e_ttypes;
|
|
ebitmap_t e_tclasses;
|
|
ebitmap_node_t *snode, *tnode, *cnode;
|
|
filename_trans_t *ft;
|
|
filename_trans_rule_t *ftr;
|
|
class_datum_t *cladatum;
|
|
type_datum_t *typdatum;
|
|
uint32_t otype;
|
|
unsigned int c, s, t;
|
|
int add;
|
|
|
|
if (pass == 1) {
|
|
/* stype */
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
/* ttype */
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
/* tclass */
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
/* otype */
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
/* name */
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
|
|
add = 1;
|
|
type_set_init(&stypes);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&stypes, id, &add, 0))
|
|
goto bad;
|
|
}
|
|
|
|
add =1;
|
|
type_set_init(&ttypes);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&ttypes, id, &add, 0))
|
|
goto bad;
|
|
}
|
|
|
|
ebitmap_init(&e_tclasses);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
cladatum = hashtab_search(policydbp->p_classes.table, id);
|
|
if (!cladatum) {
|
|
yyerror2("unknown class %s", id);
|
|
goto bad;
|
|
}
|
|
if (ebitmap_set_bit(&e_tclasses, cladatum->s.value - 1, TRUE)) {
|
|
yyerror("Out of memory");
|
|
goto bad;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no otype in transition definition?");
|
|
goto bad;
|
|
}
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
typdatum = hashtab_search(policydbp->p_types.table, id);
|
|
if (!typdatum) {
|
|
yyerror2("unknown type %s used in transition definition", id);
|
|
goto bad;
|
|
}
|
|
free(id);
|
|
otype = typdatum->s.value;
|
|
|
|
name = queue_remove(id_queue);
|
|
if (!name) {
|
|
yyerror("no pathname specified in filename_trans definition?");
|
|
goto bad;
|
|
}
|
|
|
|
/* We expand the class set into seperate rules. We expand the types
|
|
* just to make sure there are not duplicates. They will get turned
|
|
* into seperate rules later */
|
|
ebitmap_init(&e_stypes);
|
|
if (type_set_expand(&stypes, &e_stypes, policydbp, 1))
|
|
goto bad;
|
|
|
|
ebitmap_init(&e_ttypes);
|
|
if (type_set_expand(&ttypes, &e_ttypes, policydbp, 1))
|
|
goto bad;
|
|
|
|
ebitmap_for_each_bit(&e_tclasses, cnode, c) {
|
|
if (!ebitmap_node_get_bit(cnode, c))
|
|
continue;
|
|
ebitmap_for_each_bit(&e_stypes, snode, s) {
|
|
if (!ebitmap_node_get_bit(snode, s))
|
|
continue;
|
|
ebitmap_for_each_bit(&e_ttypes, tnode, t) {
|
|
if (!ebitmap_node_get_bit(tnode, t))
|
|
continue;
|
|
|
|
for (ft = policydbp->filename_trans; ft; ft = ft->next) {
|
|
if (ft->stype == (s + 1) &&
|
|
ft->ttype == (t + 1) &&
|
|
ft->tclass == (c + 1) &&
|
|
!strcmp(ft->name, name)) {
|
|
yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s",
|
|
name,
|
|
policydbp->p_type_val_to_name[s],
|
|
policydbp->p_type_val_to_name[t],
|
|
policydbp->p_class_val_to_name[c]);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
ft = malloc(sizeof(*ft));
|
|
if (!ft) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
memset(ft, 0, sizeof(*ft));
|
|
|
|
ft->next = policydbp->filename_trans;
|
|
policydbp->filename_trans = ft;
|
|
|
|
ft->name = strdup(name);
|
|
if (!ft->name) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
ft->stype = s + 1;
|
|
ft->ttype = t + 1;
|
|
ft->tclass = c + 1;
|
|
ft->otype = otype;
|
|
}
|
|
}
|
|
|
|
/* Now add the real rule since we didn't find any duplicates */
|
|
ftr = malloc(sizeof(*ftr));
|
|
if (!ftr) {
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
filename_trans_rule_init(ftr);
|
|
append_filename_trans(ftr);
|
|
|
|
ftr->name = strdup(name);
|
|
ftr->stypes = stypes;
|
|
ftr->ttypes = ttypes;
|
|
ftr->tclass = c + 1;
|
|
ftr->otype = otype;
|
|
}
|
|
|
|
free(name);
|
|
ebitmap_destroy(&e_stypes);
|
|
ebitmap_destroy(&e_ttypes);
|
|
ebitmap_destroy(&e_tclasses);
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(name);
|
|
return -1;
|
|
}
|
|
|
|
static constraint_expr_t *constraint_expr_clone(constraint_expr_t * expr)
|
|
{
|
|
constraint_expr_t *h = NULL, *l = NULL, *e, *newe;
|
|
for (e = expr; e; e = e->next) {
|
|
newe = malloc(sizeof(*newe));
|
|
if (!newe)
|
|
goto oom;
|
|
if (constraint_expr_init(newe) == -1) {
|
|
free(newe);
|
|
goto oom;
|
|
}
|
|
if (l)
|
|
l->next = newe;
|
|
else
|
|
h = newe;
|
|
l = newe;
|
|
newe->expr_type = e->expr_type;
|
|
newe->attr = e->attr;
|
|
newe->op = e->op;
|
|
if (newe->expr_type == CEXPR_NAMES) {
|
|
if (newe->attr & CEXPR_TYPE) {
|
|
if (type_set_cpy
|
|
(newe->type_names, e->type_names))
|
|
goto oom;
|
|
} else {
|
|
if (ebitmap_cpy(&newe->names, &e->names))
|
|
goto oom;
|
|
}
|
|
}
|
|
}
|
|
|
|
return h;
|
|
oom:
|
|
e = h;
|
|
while (e) {
|
|
l = e;
|
|
e = e->next;
|
|
constraint_expr_destroy(l);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int define_constraint(constraint_expr_t * expr)
|
|
{
|
|
struct constraint_node *node;
|
|
char *id;
|
|
class_datum_t *cladatum;
|
|
perm_datum_t *perdatum;
|
|
ebitmap_t classmap;
|
|
ebitmap_node_t *enode;
|
|
constraint_expr_t *e;
|
|
unsigned int i;
|
|
int depth;
|
|
unsigned char useexpr = 1;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
depth = -1;
|
|
for (e = expr; e; e = e->next) {
|
|
switch (e->expr_type) {
|
|
case CEXPR_NOT:
|
|
if (depth < 0) {
|
|
yyerror("illegal constraint expression");
|
|
return -1;
|
|
}
|
|
break;
|
|
case CEXPR_AND:
|
|
case CEXPR_OR:
|
|
if (depth < 1) {
|
|
yyerror("illegal constraint expression");
|
|
return -1;
|
|
}
|
|
depth--;
|
|
break;
|
|
case CEXPR_ATTR:
|
|
case CEXPR_NAMES:
|
|
if (e->attr & CEXPR_XTARGET) {
|
|
yyerror("illegal constraint expression");
|
|
return -1; /* only for validatetrans rules */
|
|
}
|
|
if (depth == (CEXPR_MAXDEPTH - 1)) {
|
|
yyerror("constraint expression is too deep");
|
|
return -1;
|
|
}
|
|
depth++;
|
|
break;
|
|
default:
|
|
yyerror("illegal constraint expression");
|
|
return -1;
|
|
}
|
|
}
|
|
if (depth != 0) {
|
|
yyerror("illegal constraint expression");
|
|
return -1;
|
|
}
|
|
|
|
ebitmap_init(&classmap);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
cladatum =
|
|
(class_datum_t *) hashtab_search(policydbp->p_classes.table,
|
|
(hashtab_key_t) id);
|
|
if (!cladatum) {
|
|
yyerror2("class %s is not defined", id);
|
|
ebitmap_destroy(&classmap);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
if (ebitmap_set_bit(&classmap, cladatum->s.value - 1, TRUE)) {
|
|
yyerror("out of memory");
|
|
ebitmap_destroy(&classmap);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
node = malloc(sizeof(struct constraint_node));
|
|
if (!node) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(node, 0, sizeof(constraint_node_t));
|
|
if (useexpr) {
|
|
node->expr = expr;
|
|
useexpr = 0;
|
|
} else {
|
|
node->expr = constraint_expr_clone(expr);
|
|
}
|
|
if (!node->expr) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
node->permissions = 0;
|
|
|
|
node->next = cladatum->constraints;
|
|
cladatum->constraints = node;
|
|
|
|
free(id);
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
ebitmap_for_each_bit(&classmap, enode, i) {
|
|
if (ebitmap_node_get_bit(enode, i)) {
|
|
cladatum = policydbp->class_val_to_struct[i];
|
|
node = cladatum->constraints;
|
|
|
|
perdatum =
|
|
(perm_datum_t *) hashtab_search(cladatum->
|
|
permissions.
|
|
table,
|
|
(hashtab_key_t)
|
|
id);
|
|
if (!perdatum) {
|
|
if (cladatum->comdatum) {
|
|
perdatum =
|
|
(perm_datum_t *)
|
|
hashtab_search(cladatum->
|
|
comdatum->
|
|
permissions.
|
|
table,
|
|
(hashtab_key_t)
|
|
id);
|
|
}
|
|
if (!perdatum) {
|
|
yyerror2("permission %s is not"
|
|
" defined", id);
|
|
free(id);
|
|
ebitmap_destroy(&classmap);
|
|
return -1;
|
|
}
|
|
}
|
|
node->permissions |=
|
|
(1 << (perdatum->s.value - 1));
|
|
}
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
ebitmap_destroy(&classmap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_validatetrans(constraint_expr_t * expr)
|
|
{
|
|
struct constraint_node *node;
|
|
char *id;
|
|
class_datum_t *cladatum;
|
|
ebitmap_t classmap;
|
|
constraint_expr_t *e;
|
|
int depth;
|
|
unsigned char useexpr = 1;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
return 0;
|
|
}
|
|
|
|
depth = -1;
|
|
for (e = expr; e; e = e->next) {
|
|
switch (e->expr_type) {
|
|
case CEXPR_NOT:
|
|
if (depth < 0) {
|
|
yyerror("illegal validatetrans expression");
|
|
return -1;
|
|
}
|
|
break;
|
|
case CEXPR_AND:
|
|
case CEXPR_OR:
|
|
if (depth < 1) {
|
|
yyerror("illegal validatetrans expression");
|
|
return -1;
|
|
}
|
|
depth--;
|
|
break;
|
|
case CEXPR_ATTR:
|
|
case CEXPR_NAMES:
|
|
if (depth == (CEXPR_MAXDEPTH - 1)) {
|
|
yyerror("validatetrans expression is too deep");
|
|
return -1;
|
|
}
|
|
depth++;
|
|
break;
|
|
default:
|
|
yyerror("illegal validatetrans expression");
|
|
return -1;
|
|
}
|
|
}
|
|
if (depth != 0) {
|
|
yyerror("illegal validatetrans expression");
|
|
return -1;
|
|
}
|
|
|
|
ebitmap_init(&classmap);
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
cladatum =
|
|
(class_datum_t *) hashtab_search(policydbp->p_classes.table,
|
|
(hashtab_key_t) id);
|
|
if (!cladatum) {
|
|
yyerror2("class %s is not defined", id);
|
|
ebitmap_destroy(&classmap);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
if (ebitmap_set_bit(&classmap, (cladatum->s.value - 1), TRUE)) {
|
|
yyerror("out of memory");
|
|
ebitmap_destroy(&classmap);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
node = malloc(sizeof(struct constraint_node));
|
|
if (!node) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(node, 0, sizeof(constraint_node_t));
|
|
if (useexpr) {
|
|
node->expr = expr;
|
|
useexpr = 0;
|
|
} else {
|
|
node->expr = constraint_expr_clone(expr);
|
|
}
|
|
node->permissions = 0;
|
|
|
|
node->next = cladatum->validatetrans;
|
|
cladatum->validatetrans = node;
|
|
|
|
free(id);
|
|
}
|
|
|
|
ebitmap_destroy(&classmap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2)
|
|
{
|
|
struct constraint_expr *expr, *e1 = NULL, *e2;
|
|
user_datum_t *user;
|
|
role_datum_t *role;
|
|
ebitmap_t negset;
|
|
char *id;
|
|
uint32_t val;
|
|
int add = 1;
|
|
|
|
if (pass == 1) {
|
|
if (expr_type == CEXPR_NAMES) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
}
|
|
return 1; /* any non-NULL value */
|
|
}
|
|
|
|
if ((expr = malloc(sizeof(*expr))) == NULL ||
|
|
constraint_expr_init(expr) == -1) {
|
|
yyerror("out of memory");
|
|
free(expr);
|
|
return 0;
|
|
}
|
|
expr->expr_type = expr_type;
|
|
|
|
switch (expr_type) {
|
|
case CEXPR_NOT:
|
|
e1 = NULL;
|
|
e2 = (struct constraint_expr *)arg1;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror("illegal constraint expression");
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
e1->next = expr;
|
|
return arg1;
|
|
case CEXPR_AND:
|
|
case CEXPR_OR:
|
|
e1 = NULL;
|
|
e2 = (struct constraint_expr *)arg1;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror("illegal constraint expression");
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
e1->next = (struct constraint_expr *)arg2;
|
|
|
|
e1 = NULL;
|
|
e2 = (struct constraint_expr *)arg2;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror("illegal constraint expression");
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
e1->next = expr;
|
|
return arg1;
|
|
case CEXPR_ATTR:
|
|
expr->attr = arg1;
|
|
expr->op = arg2;
|
|
return (uintptr_t) expr;
|
|
case CEXPR_NAMES:
|
|
add = 1;
|
|
expr->attr = arg1;
|
|
expr->op = arg2;
|
|
ebitmap_init(&negset);
|
|
while ((id = (char *)queue_remove(id_queue))) {
|
|
if (expr->attr & CEXPR_USER) {
|
|
if (!is_id_in_scope(SYM_USERS, id)) {
|
|
yyerror2("user %s is not within scope",
|
|
id);
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
user =
|
|
(user_datum_t *) hashtab_search(policydbp->
|
|
p_users.
|
|
table,
|
|
(hashtab_key_t)
|
|
id);
|
|
if (!user) {
|
|
yyerror2("unknown user %s", id);
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
val = user->s.value;
|
|
} else if (expr->attr & CEXPR_ROLE) {
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope",
|
|
id);
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
role =
|
|
(role_datum_t *) hashtab_search(policydbp->
|
|
p_roles.
|
|
table,
|
|
(hashtab_key_t)
|
|
id);
|
|
if (!role) {
|
|
yyerror2("unknown role %s", id);
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
val = role->s.value;
|
|
} else if (expr->attr & CEXPR_TYPE) {
|
|
if (set_types(expr->type_names, id, &add, 0)) {
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
continue;
|
|
} else {
|
|
yyerror("invalid constraint expression");
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
if (ebitmap_set_bit(&expr->names, val - 1, TRUE)) {
|
|
yyerror("out of memory");
|
|
ebitmap_destroy(&expr->names);
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
free(id);
|
|
}
|
|
ebitmap_destroy(&negset);
|
|
return (uintptr_t) expr;
|
|
default:
|
|
yyerror("invalid constraint expression");
|
|
constraint_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
|
|
yyerror("invalid constraint expression");
|
|
free(expr);
|
|
return 0;
|
|
}
|
|
|
|
int define_conditional(cond_expr_t * expr, avrule_t * t, avrule_t * f)
|
|
{
|
|
cond_expr_t *e;
|
|
int depth;
|
|
cond_node_t cn, *cn_old;
|
|
|
|
/* expression cannot be NULL */
|
|
if (!expr) {
|
|
yyerror("illegal conditional expression");
|
|
return -1;
|
|
}
|
|
if (!t) {
|
|
if (!f) {
|
|
/* empty is fine, destroy expression and return */
|
|
cond_expr_destroy(expr);
|
|
return 0;
|
|
}
|
|
/* Invert */
|
|
t = f;
|
|
f = 0;
|
|
expr = define_cond_expr(COND_NOT, expr, 0);
|
|
if (!expr) {
|
|
yyerror("unable to invert");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* verify expression */
|
|
depth = -1;
|
|
for (e = expr; e; e = e->next) {
|
|
switch (e->expr_type) {
|
|
case COND_NOT:
|
|
if (depth < 0) {
|
|
yyerror
|
|
("illegal conditional expression; Bad NOT");
|
|
return -1;
|
|
}
|
|
break;
|
|
case COND_AND:
|
|
case COND_OR:
|
|
case COND_XOR:
|
|
case COND_EQ:
|
|
case COND_NEQ:
|
|
if (depth < 1) {
|
|
yyerror
|
|
("illegal conditional expression; Bad binary op");
|
|
return -1;
|
|
}
|
|
depth--;
|
|
break;
|
|
case COND_BOOL:
|
|
if (depth == (COND_EXPR_MAXDEPTH - 1)) {
|
|
yyerror
|
|
("conditional expression is like totally too deep");
|
|
return -1;
|
|
}
|
|
depth++;
|
|
break;
|
|
default:
|
|
yyerror("illegal conditional expression");
|
|
return -1;
|
|
}
|
|
}
|
|
if (depth != 0) {
|
|
yyerror("illegal conditional expression");
|
|
return -1;
|
|
}
|
|
|
|
/* use tmp conditional node to partially build new node */
|
|
memset(&cn, 0, sizeof(cn));
|
|
cn.expr = expr;
|
|
cn.avtrue_list = t;
|
|
cn.avfalse_list = f;
|
|
|
|
/* normalize/precompute expression */
|
|
if (cond_normalize_expr(policydbp, &cn) < 0) {
|
|
yyerror("problem normalizing conditional expression");
|
|
return -1;
|
|
}
|
|
|
|
/* get the existing conditional node, or create a new one */
|
|
cn_old = get_current_cond_list(&cn);
|
|
if (!cn_old) {
|
|
return -1;
|
|
}
|
|
|
|
append_cond_list(&cn);
|
|
|
|
/* note that there is no check here for duplicate rules, nor
|
|
* check that rule already exists in base -- that will be
|
|
* handled during conditional expansion, in expand.c */
|
|
|
|
cn.avtrue_list = NULL;
|
|
cn.avfalse_list = NULL;
|
|
cond_node_destroy(&cn);
|
|
|
|
return 0;
|
|
}
|
|
|
|
cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void *arg2)
|
|
{
|
|
struct cond_expr *expr, *e1 = NULL, *e2;
|
|
cond_bool_datum_t *bool_var;
|
|
char *id;
|
|
|
|
/* expressions are handled in the second pass */
|
|
if (pass == 1) {
|
|
if (expr_type == COND_BOOL) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
free(id);
|
|
}
|
|
}
|
|
return (cond_expr_t *) 1; /* any non-NULL value */
|
|
}
|
|
|
|
/* create a new expression struct */
|
|
expr = malloc(sizeof(struct cond_expr));
|
|
if (!expr) {
|
|
yyerror("out of memory");
|
|
return NULL;
|
|
}
|
|
memset(expr, 0, sizeof(cond_expr_t));
|
|
expr->expr_type = expr_type;
|
|
|
|
/* create the type asked for */
|
|
switch (expr_type) {
|
|
case COND_NOT:
|
|
e1 = NULL;
|
|
e2 = (struct cond_expr *)arg1;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror("illegal conditional NOT expression");
|
|
free(expr);
|
|
return NULL;
|
|
}
|
|
e1->next = expr;
|
|
return (struct cond_expr *)arg1;
|
|
case COND_AND:
|
|
case COND_OR:
|
|
case COND_XOR:
|
|
case COND_EQ:
|
|
case COND_NEQ:
|
|
e1 = NULL;
|
|
e2 = (struct cond_expr *)arg1;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror
|
|
("illegal left side of conditional binary op expression");
|
|
free(expr);
|
|
return NULL;
|
|
}
|
|
e1->next = (struct cond_expr *)arg2;
|
|
|
|
e1 = NULL;
|
|
e2 = (struct cond_expr *)arg2;
|
|
while (e2) {
|
|
e1 = e2;
|
|
e2 = e2->next;
|
|
}
|
|
if (!e1 || e1->next) {
|
|
yyerror
|
|
("illegal right side of conditional binary op expression");
|
|
free(expr);
|
|
return NULL;
|
|
}
|
|
e1->next = expr;
|
|
return (struct cond_expr *)arg1;
|
|
case COND_BOOL:
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("bad conditional; expected boolean id");
|
|
free(id);
|
|
free(expr);
|
|
return NULL;
|
|
}
|
|
if (!is_id_in_scope(SYM_BOOLS, id)) {
|
|
yyerror2("boolean %s is not within scope", id);
|
|
free(id);
|
|
free(expr);
|
|
return NULL;
|
|
}
|
|
bool_var =
|
|
(cond_bool_datum_t *) hashtab_search(policydbp->p_bools.
|
|
table,
|
|
(hashtab_key_t) id);
|
|
if (!bool_var) {
|
|
yyerror2("unknown boolean %s in conditional expression",
|
|
id);
|
|
free(expr);
|
|
free(id);
|
|
return NULL;
|
|
}
|
|
expr->bool = bool_var->s.value;
|
|
free(id);
|
|
return expr;
|
|
default:
|
|
yyerror("illegal conditional expression");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static int set_user_roles(role_set_t * set, char *id)
|
|
{
|
|
role_datum_t *r;
|
|
unsigned int i;
|
|
ebitmap_node_t *node;
|
|
|
|
if (strcmp(id, "*") == 0) {
|
|
free(id);
|
|
yyerror("* is not allowed in user declarations");
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(id, "~") == 0) {
|
|
free(id);
|
|
yyerror("~ is not allowed in user declarations");
|
|
return -1;
|
|
}
|
|
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
r = hashtab_search(policydbp->p_roles.table, id);
|
|
if (!r) {
|
|
yyerror2("unknown role %s", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
|
|
/* set the role and every role it dominates */
|
|
ebitmap_for_each_bit(&r->dominates, node, i) {
|
|
if (ebitmap_node_get_bit(node, i))
|
|
if (ebitmap_set_bit(&set->roles, i, TRUE))
|
|
goto oom;
|
|
}
|
|
free(id);
|
|
return 0;
|
|
oom:
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
|
|
static int parse_categories(char *id, level_datum_t * levdatum, ebitmap_t * cats)
|
|
{
|
|
cat_datum_t *cdatum;
|
|
int range_start, range_end, i;
|
|
|
|
if (id_has_dot(id)) {
|
|
char *id_start = id;
|
|
char *id_end = strchr(id, '.');
|
|
|
|
*(id_end++) = '\0';
|
|
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t)
|
|
id_start);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_start);
|
|
return -1;
|
|
}
|
|
range_start = cdatum->s.value - 1;
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t) id_end);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_end);
|
|
return -1;
|
|
}
|
|
range_end = cdatum->s.value - 1;
|
|
|
|
if (range_end < range_start) {
|
|
yyerror2("category range is invalid");
|
|
return -1;
|
|
}
|
|
} else {
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t) id);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id);
|
|
return -1;
|
|
}
|
|
range_start = range_end = cdatum->s.value - 1;
|
|
}
|
|
|
|
for (i = range_start; i <= range_end; i++) {
|
|
if (!ebitmap_get_bit(&levdatum->level->cat, i)) {
|
|
uint32_t level_value = levdatum->level->sens - 1;
|
|
policydb_index_others(NULL, policydbp, 0);
|
|
yyerror2("category %s can not be associated "
|
|
"with level %s",
|
|
policydbp->p_cat_val_to_name[i],
|
|
policydbp->p_sens_val_to_name[level_value]);
|
|
return -1;
|
|
}
|
|
if (ebitmap_set_bit(cats, i, TRUE)) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_semantic_categories(char *id, level_datum_t * levdatum,
|
|
mls_semantic_cat_t ** cats)
|
|
{
|
|
cat_datum_t *cdatum;
|
|
mls_semantic_cat_t *newcat;
|
|
unsigned int range_start, range_end;
|
|
|
|
if (id_has_dot(id)) {
|
|
char *id_start = id;
|
|
char *id_end = strchr(id, '.');
|
|
|
|
*(id_end++) = '\0';
|
|
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t)
|
|
id_start);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_start);
|
|
return -1;
|
|
}
|
|
range_start = cdatum->s.value;
|
|
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t) id_end);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id_end);
|
|
return -1;
|
|
}
|
|
range_end = cdatum->s.value;
|
|
} else {
|
|
cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table,
|
|
(hashtab_key_t) id);
|
|
if (!cdatum) {
|
|
yyerror2("unknown category %s", id);
|
|
return -1;
|
|
}
|
|
range_start = range_end = cdatum->s.value;
|
|
}
|
|
|
|
newcat = (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
|
|
if (!newcat) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
|
|
mls_semantic_cat_init(newcat);
|
|
newcat->next = *cats;
|
|
newcat->low = range_start;
|
|
newcat->high = range_end;
|
|
|
|
*cats = newcat;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_user(void)
|
|
{
|
|
char *id;
|
|
user_datum_t *usrdatum;
|
|
level_datum_t *levdatum;
|
|
int l;
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
if (mlspol) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
for (l = 0; l < 2; l++) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
free(id);
|
|
}
|
|
id = queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
free(id);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if ((usrdatum = declare_user()) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_user_roles(&usrdatum->roles, id))
|
|
continue;
|
|
}
|
|
|
|
if (mlspol) {
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no default level specified for user");
|
|
return -1;
|
|
}
|
|
|
|
levdatum = (level_datum_t *)
|
|
hashtab_search(policydbp->p_levels.table,
|
|
(hashtab_key_t) id);
|
|
if (!levdatum) {
|
|
yyerror2("unknown sensitivity %s used in user"
|
|
" level definition", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
|
|
usrdatum->dfltlevel.sens = levdatum->level->sens;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (parse_semantic_categories(id, levdatum,
|
|
&usrdatum->dfltlevel.cat)) {
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
|
|
for (l = 0; l < 2; l++) {
|
|
levdatum = (level_datum_t *)
|
|
hashtab_search(policydbp->p_levels.table,
|
|
(hashtab_key_t) id);
|
|
if (!levdatum) {
|
|
yyerror2("unknown sensitivity %s used in user"
|
|
" range definition", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
|
|
usrdatum->range.level[l].sens = levdatum->level->sens;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (parse_semantic_categories(id, levdatum,
|
|
&usrdatum->range.level[l].cat)) {
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
}
|
|
|
|
if (l == 0) {
|
|
if (mls_semantic_level_cpy(&usrdatum->range.level[1],
|
|
&usrdatum->range.level[0])) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int parse_security_context(context_struct_t * c)
|
|
{
|
|
char *id;
|
|
role_datum_t *role;
|
|
type_datum_t *typdatum;
|
|
user_datum_t *usrdatum;
|
|
level_datum_t *levdatum;
|
|
int l;
|
|
|
|
if (pass == 1) {
|
|
id = queue_remove(id_queue);
|
|
free(id); /* user */
|
|
id = queue_remove(id_queue);
|
|
free(id); /* role */
|
|
id = queue_remove(id_queue);
|
|
free(id); /* type */
|
|
if (mlspol) {
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
for (l = 0; l < 2; l++) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
free(id);
|
|
}
|
|
id = queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
free(id);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
context_init(c);
|
|
|
|
/* extract the user */
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no effective user?");
|
|
goto bad;
|
|
}
|
|
if (!is_id_in_scope(SYM_USERS, id)) {
|
|
yyerror2("user %s is not within scope", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
usrdatum = (user_datum_t *) hashtab_search(policydbp->p_users.table,
|
|
(hashtab_key_t) id);
|
|
if (!usrdatum) {
|
|
yyerror2("user %s is not defined", id);
|
|
free(id);
|
|
goto bad;
|
|
}
|
|
c->user = usrdatum->s.value;
|
|
|
|
/* no need to keep the user name */
|
|
free(id);
|
|
|
|
/* extract the role */
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no role name for sid context definition?");
|
|
return -1;
|
|
}
|
|
if (!is_id_in_scope(SYM_ROLES, id)) {
|
|
yyerror2("role %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
role = (role_datum_t *) hashtab_search(policydbp->p_roles.table,
|
|
(hashtab_key_t) id);
|
|
if (!role) {
|
|
yyerror2("role %s is not defined", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
c->role = role->s.value;
|
|
|
|
/* no need to keep the role name */
|
|
free(id);
|
|
|
|
/* extract the type */
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no type name for sid context definition?");
|
|
return -1;
|
|
}
|
|
if (!is_id_in_scope(SYM_TYPES, id)) {
|
|
yyerror2("type %s is not within scope", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
typdatum = (type_datum_t *) hashtab_search(policydbp->p_types.table,
|
|
(hashtab_key_t) id);
|
|
if (!typdatum || typdatum->flavor == TYPE_ATTRIB) {
|
|
yyerror2("type %s is not defined or is an attribute", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
c->type = typdatum->s.value;
|
|
|
|
/* no need to keep the type name */
|
|
free(id);
|
|
|
|
if (mlspol) {
|
|
/* extract the low sensitivity */
|
|
id = (char *)queue_head(id_queue);
|
|
if (!id) {
|
|
yyerror("no sensitivity name for sid context"
|
|
" definition?");
|
|
return -1;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
for (l = 0; l < 2; l++) {
|
|
levdatum = (level_datum_t *)
|
|
hashtab_search(policydbp->p_levels.table,
|
|
(hashtab_key_t) id);
|
|
if (!levdatum) {
|
|
yyerror2("Sensitivity %s is not defined", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
c->range.level[l].sens = levdatum->level->sens;
|
|
|
|
/* extract low category set */
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (parse_categories(id, levdatum,
|
|
&c->range.level[l].cat)) {
|
|
free(id);
|
|
return -1;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
/* extract high sensitivity */
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
}
|
|
|
|
if (l == 0) {
|
|
c->range.level[1].sens = c->range.level[0].sens;
|
|
if (ebitmap_cpy(&c->range.level[1].cat,
|
|
&c->range.level[0].cat)) {
|
|
|
|
yyerror("out of memory");
|
|
goto bad;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!policydb_context_isvalid(policydbp, c)) {
|
|
yyerror("invalid security context");
|
|
goto bad;
|
|
}
|
|
return 0;
|
|
|
|
bad:
|
|
context_destroy(c);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int define_initial_sid_context(void)
|
|
{
|
|
char *id;
|
|
ocontext_t *c, *head;
|
|
|
|
if (pass == 1) {
|
|
id = (char *)queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no sid name for SID context definition?");
|
|
return -1;
|
|
}
|
|
head = policydbp->ocontexts[OCON_ISID];
|
|
for (c = head; c; c = c->next) {
|
|
if (!strcmp(id, c->u.name))
|
|
break;
|
|
}
|
|
|
|
if (!c) {
|
|
yyerror2("SID %s is not defined", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
if (c->context[0].user) {
|
|
yyerror2("The context for SID %s is multiply defined", id);
|
|
free(id);
|
|
return -1;
|
|
}
|
|
/* no need to keep the sid name */
|
|
free(id);
|
|
|
|
if (parse_security_context(&c->context[0]))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_fs_context(unsigned int major, unsigned int minor)
|
|
{
|
|
ocontext_t *newc, *c, *head;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("fscon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
parse_security_context(NULL);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = (ocontext_t *) malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.name = (char *)malloc(6);
|
|
if (!newc->u.name) {
|
|
yyerror("out of memory");
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
sprintf(newc->u.name, "%02x:%02x", major, minor);
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
if (parse_security_context(&newc->context[1])) {
|
|
context_destroy(&newc->context[0]);
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
head = policydbp->ocontexts[OCON_FS];
|
|
|
|
for (c = head; c; c = c->next) {
|
|
if (!strcmp(newc->u.name, c->u.name)) {
|
|
yyerror2("duplicate entry for file system %s",
|
|
newc->u.name);
|
|
context_destroy(&newc->context[0]);
|
|
context_destroy(&newc->context[1]);
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
newc->next = head;
|
|
policydbp->ocontexts[OCON_FS] = newc;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int define_pirq_context(unsigned int pirq)
|
|
{
|
|
ocontext_t *newc, *c, *l, *head;
|
|
char *id;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_XEN) {
|
|
yyerror("pirqcon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
id = (char *) queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.pirq = pirq;
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
head = policydbp->ocontexts[OCON_XEN_PIRQ];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
unsigned int pirq2;
|
|
|
|
pirq2 = c->u.pirq;
|
|
if (pirq == pirq2) {
|
|
yyerror2("duplicate pirqcon entry for %d ", pirq);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_XEN_PIRQ] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_iomem_context(unsigned long low, unsigned long high)
|
|
{
|
|
ocontext_t *newc, *c, *l, *head;
|
|
char *id;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_XEN) {
|
|
yyerror("iomemcon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
id = (char *)queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.iomem.low_iomem = low;
|
|
newc->u.iomem.high_iomem = high;
|
|
|
|
if (low > high) {
|
|
yyerror2("low memory 0x%x exceeds high memory 0x%x", low, high);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
head = policydbp->ocontexts[OCON_XEN_IOMEM];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
unsigned int low2, high2;
|
|
|
|
low2 = c->u.iomem.low_iomem;
|
|
high2 = c->u.iomem.high_iomem;
|
|
if (low <= high2 && low2 <= high) {
|
|
yyerror2("iomemcon entry for 0x%x-0x%x overlaps with "
|
|
"earlier entry 0x%x-0x%x", low, high,
|
|
low2, high2);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_XEN_IOMEM] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_ioport_context(unsigned long low, unsigned long high)
|
|
{
|
|
ocontext_t *newc, *c, *l, *head;
|
|
char *id;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_XEN) {
|
|
yyerror("ioportcon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
id = (char *)queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.ioport.low_ioport = low;
|
|
newc->u.ioport.high_ioport = high;
|
|
|
|
if (low > high) {
|
|
yyerror2("low ioport 0x%x exceeds high ioport 0x%x", low, high);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
head = policydbp->ocontexts[OCON_XEN_IOPORT];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
unsigned int low2, high2;
|
|
|
|
low2 = c->u.ioport.low_ioport;
|
|
high2 = c->u.ioport.high_ioport;
|
|
if (low <= high2 && low2 <= high) {
|
|
yyerror2("ioportcon entry for 0x%x-0x%x overlaps with"
|
|
"earlier entry 0x%x-0x%x", low, high,
|
|
low2, high2);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_XEN_IOPORT] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_pcidevice_context(unsigned long device)
|
|
{
|
|
ocontext_t *newc, *c, *l, *head;
|
|
char *id;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_XEN) {
|
|
yyerror("pcidevicecon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
id = (char *) queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.device = device;
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
head = policydbp->ocontexts[OCON_XEN_PCIDEVICE];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
unsigned int device2;
|
|
|
|
device2 = c->u.device;
|
|
if (device == device2) {
|
|
yyerror2("duplicate pcidevicecon entry for 0x%x ",
|
|
device);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_XEN_PCIDEVICE] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_port_context(unsigned int low, unsigned int high)
|
|
{
|
|
ocontext_t *newc, *c, *l, *head;
|
|
unsigned int protocol;
|
|
char *id;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("portcon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
id = (char *)queue_remove(id_queue);
|
|
free(id);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
if ((strcmp(id, "tcp") == 0) || (strcmp(id, "TCP") == 0)) {
|
|
protocol = IPPROTO_TCP;
|
|
} else if ((strcmp(id, "udp") == 0) || (strcmp(id, "UDP") == 0)) {
|
|
protocol = IPPROTO_UDP;
|
|
} else {
|
|
yyerror2("unrecognized protocol %s", id);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
newc->u.port.protocol = protocol;
|
|
newc->u.port.low_port = low;
|
|
newc->u.port.high_port = high;
|
|
|
|
if (low > high) {
|
|
yyerror2("low port %d exceeds high port %d", low, high);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
/* Preserve the matching order specified in the configuration. */
|
|
head = policydbp->ocontexts[OCON_PORT];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
unsigned int prot2, low2, high2;
|
|
|
|
prot2 = c->u.port.protocol;
|
|
low2 = c->u.port.low_port;
|
|
high2 = c->u.port.high_port;
|
|
if (protocol != prot2)
|
|
continue;
|
|
if (low == low2 && high == high2) {
|
|
yyerror2("duplicate portcon entry for %s %d-%d ", id,
|
|
low, high);
|
|
goto bad;
|
|
}
|
|
if (low2 <= low && high2 >= high) {
|
|
yyerror2("portcon entry for %s %d-%d hidden by earlier "
|
|
"entry for %d-%d", id, low, high, low2, high2);
|
|
goto bad;
|
|
}
|
|
}
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_PORT] = newc;
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_netif_context(void)
|
|
{
|
|
ocontext_t *newc, *c, *head;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("netifcon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
free(queue_remove(id_queue));
|
|
parse_security_context(NULL);
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = (ocontext_t *) malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.name = (char *)queue_remove(id_queue);
|
|
if (!newc->u.name) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
if (parse_security_context(&newc->context[1])) {
|
|
context_destroy(&newc->context[0]);
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
head = policydbp->ocontexts[OCON_NETIF];
|
|
|
|
for (c = head; c; c = c->next) {
|
|
if (!strcmp(newc->u.name, c->u.name)) {
|
|
yyerror2("duplicate entry for network interface %s",
|
|
newc->u.name);
|
|
context_destroy(&newc->context[0]);
|
|
context_destroy(&newc->context[1]);
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
newc->next = head;
|
|
policydbp->ocontexts[OCON_NETIF] = newc;
|
|
return 0;
|
|
}
|
|
|
|
int define_ipv4_node_context()
|
|
{
|
|
char *id;
|
|
int rc = 0;
|
|
struct in_addr addr, mask;
|
|
ocontext_t *newc, *c, *l, *head;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("nodecon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
free(queue_remove(id_queue));
|
|
free(queue_remove(id_queue));
|
|
parse_security_context(NULL);
|
|
goto out;
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("failed to read ipv4 address");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
rc = inet_pton(AF_INET, id, &addr);
|
|
free(id);
|
|
if (rc < 1) {
|
|
yyerror("failed to parse ipv4 address");
|
|
if (rc == 0)
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("failed to read ipv4 address");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
rc = inet_pton(AF_INET, id, &mask);
|
|
free(id);
|
|
if (rc < 1) {
|
|
yyerror("failed to parse ipv4 mask");
|
|
if (rc == 0)
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
newc->u.node.addr = addr.s_addr;
|
|
newc->u.node.mask = mask.s_addr;
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
/* Create order of most specific to least retaining
|
|
the order specified in the configuration. */
|
|
head = policydbp->ocontexts[OCON_NODE];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
if (newc->u.node.mask > c->u.node.mask)
|
|
break;
|
|
}
|
|
|
|
newc->next = c;
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_NODE] = newc;
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
int define_ipv6_node_context(void)
|
|
{
|
|
char *id;
|
|
int rc = 0;
|
|
struct in6_addr addr, mask;
|
|
ocontext_t *newc, *c, *l, *head;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("nodecon not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
free(queue_remove(id_queue));
|
|
free(queue_remove(id_queue));
|
|
parse_security_context(NULL);
|
|
goto out;
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("failed to read ipv6 address");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
rc = inet_pton(AF_INET6, id, &addr);
|
|
free(id);
|
|
if (rc < 1) {
|
|
yyerror("failed to parse ipv6 address");
|
|
if (rc == 0)
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
id = queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("failed to read ipv6 address");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
rc = inet_pton(AF_INET6, id, &mask);
|
|
free(id);
|
|
if (rc < 1) {
|
|
yyerror("failed to parse ipv6 mask");
|
|
if (rc == 0)
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
newc = malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
memcpy(&newc->u.node6.addr[0], &addr.s6_addr32[0], 16);
|
|
memcpy(&newc->u.node6.mask[0], &mask.s6_addr32[0], 16);
|
|
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc);
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* Create order of most specific to least retaining
|
|
the order specified in the configuration. */
|
|
head = policydbp->ocontexts[OCON_NODE6];
|
|
for (l = NULL, c = head; c; l = c, c = c->next) {
|
|
if (memcmp(&newc->u.node6.mask, &c->u.node6.mask, 16) > 0)
|
|
break;
|
|
}
|
|
|
|
newc->next = c;
|
|
|
|
if (l)
|
|
l->next = newc;
|
|
else
|
|
policydbp->ocontexts[OCON_NODE6] = newc;
|
|
|
|
rc = 0;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
int define_fs_use(int behavior)
|
|
{
|
|
ocontext_t *newc, *c, *head;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("fsuse not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
free(queue_remove(id_queue));
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
newc = (ocontext_t *) malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.name = (char *)queue_remove(id_queue);
|
|
if (!newc->u.name) {
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
newc->v.behavior = behavior;
|
|
if (parse_security_context(&newc->context[0])) {
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
head = policydbp->ocontexts[OCON_FSUSE];
|
|
|
|
for (c = head; c; c = c->next) {
|
|
if (!strcmp(newc->u.name, c->u.name)) {
|
|
yyerror2("duplicate fs_use entry for filesystem type %s",
|
|
newc->u.name);
|
|
context_destroy(&newc->context[0]);
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
newc->next = head;
|
|
policydbp->ocontexts[OCON_FSUSE] = newc;
|
|
return 0;
|
|
}
|
|
|
|
int define_genfs_context_helper(char *fstype, int has_type)
|
|
{
|
|
struct genfs *genfs_p, *genfs, *newgenfs;
|
|
ocontext_t *newc, *c, *head, *p;
|
|
char *type = NULL;
|
|
int len, len2;
|
|
|
|
if (policydbp->target_platform != SEPOL_TARGET_SELINUX) {
|
|
yyerror("genfs not supported for target");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
free(fstype);
|
|
free(queue_remove(id_queue));
|
|
if (has_type)
|
|
free(queue_remove(id_queue));
|
|
parse_security_context(NULL);
|
|
return 0;
|
|
}
|
|
|
|
for (genfs_p = NULL, genfs = policydbp->genfs;
|
|
genfs; genfs_p = genfs, genfs = genfs->next) {
|
|
if (strcmp(fstype, genfs->fstype) <= 0)
|
|
break;
|
|
}
|
|
|
|
if (!genfs || strcmp(fstype, genfs->fstype)) {
|
|
newgenfs = malloc(sizeof(struct genfs));
|
|
if (!newgenfs) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newgenfs, 0, sizeof(struct genfs));
|
|
newgenfs->fstype = fstype;
|
|
newgenfs->next = genfs;
|
|
if (genfs_p)
|
|
genfs_p->next = newgenfs;
|
|
else
|
|
policydbp->genfs = newgenfs;
|
|
genfs = newgenfs;
|
|
}
|
|
|
|
newc = (ocontext_t *) malloc(sizeof(ocontext_t));
|
|
if (!newc) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
memset(newc, 0, sizeof(ocontext_t));
|
|
|
|
newc->u.name = (char *)queue_remove(id_queue);
|
|
if (!newc->u.name)
|
|
goto fail;
|
|
if (has_type) {
|
|
type = (char *)queue_remove(id_queue);
|
|
if (!type)
|
|
goto fail;
|
|
if (type[1] != 0) {
|
|
yyerror2("invalid type %s", type);
|
|
goto fail;
|
|
}
|
|
switch (type[0]) {
|
|
case 'b':
|
|
newc->v.sclass = SECCLASS_BLK_FILE;
|
|
break;
|
|
case 'c':
|
|
newc->v.sclass = SECCLASS_CHR_FILE;
|
|
break;
|
|
case 'd':
|
|
newc->v.sclass = SECCLASS_DIR;
|
|
break;
|
|
case 'p':
|
|
newc->v.sclass = SECCLASS_FIFO_FILE;
|
|
break;
|
|
case 'l':
|
|
newc->v.sclass = SECCLASS_LNK_FILE;
|
|
break;
|
|
case 's':
|
|
newc->v.sclass = SECCLASS_SOCK_FILE;
|
|
break;
|
|
case '-':
|
|
newc->v.sclass = SECCLASS_FILE;
|
|
break;
|
|
default:
|
|
yyerror2("invalid type %s", type);
|
|
goto fail;
|
|
}
|
|
}
|
|
if (parse_security_context(&newc->context[0]))
|
|
goto fail;
|
|
|
|
head = genfs->head;
|
|
|
|
for (p = NULL, c = head; c; p = c, c = c->next) {
|
|
if (!strcmp(newc->u.name, c->u.name) &&
|
|
(!newc->v.sclass || !c->v.sclass
|
|
|| newc->v.sclass == c->v.sclass)) {
|
|
yyerror2("duplicate entry for genfs entry (%s, %s)",
|
|
fstype, newc->u.name);
|
|
goto fail;
|
|
}
|
|
len = strlen(newc->u.name);
|
|
len2 = strlen(c->u.name);
|
|
if (len > len2)
|
|
break;
|
|
}
|
|
|
|
newc->next = c;
|
|
if (p)
|
|
p->next = newc;
|
|
else
|
|
genfs->head = newc;
|
|
return 0;
|
|
fail:
|
|
if (type)
|
|
free(type);
|
|
context_destroy(&newc->context[0]);
|
|
if (fstype)
|
|
free(fstype);
|
|
if (newc->u.name)
|
|
free(newc->u.name);
|
|
free(newc);
|
|
return -1;
|
|
}
|
|
|
|
int define_genfs_context(int has_type)
|
|
{
|
|
return define_genfs_context_helper(queue_remove(id_queue), has_type);
|
|
}
|
|
|
|
int define_range_trans(int class_specified)
|
|
{
|
|
char *id;
|
|
level_datum_t *levdatum = 0;
|
|
class_datum_t *cladatum;
|
|
range_trans_rule_t *rule;
|
|
int l, add = 1;
|
|
|
|
if (!mlspol) {
|
|
yyerror("range_transition rule in non-MLS configuration");
|
|
return -1;
|
|
}
|
|
|
|
if (pass == 1) {
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
if (class_specified)
|
|
while ((id = queue_remove(id_queue)))
|
|
free(id);
|
|
id = queue_remove(id_queue);
|
|
free(id);
|
|
for (l = 0; l < 2; l++) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
free(id);
|
|
}
|
|
id = queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
free(id);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
rule = malloc(sizeof(struct range_trans_rule));
|
|
if (!rule) {
|
|
yyerror("out of memory");
|
|
return -1;
|
|
}
|
|
range_trans_rule_init(rule);
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&rule->stypes, id, &add, 0))
|
|
goto out;
|
|
}
|
|
add = 1;
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (set_types(&rule->ttypes, id, &add, 0))
|
|
goto out;
|
|
}
|
|
|
|
if (class_specified) {
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (!is_id_in_scope(SYM_CLASSES, id)) {
|
|
yyerror2("class %s is not within scope", id);
|
|
free(id);
|
|
goto out;
|
|
}
|
|
cladatum = hashtab_search(policydbp->p_classes.table,
|
|
id);
|
|
if (!cladatum) {
|
|
yyerror2("unknown class %s", id);
|
|
goto out;
|
|
}
|
|
|
|
ebitmap_set_bit(&rule->tclasses, cladatum->s.value - 1,
|
|
TRUE);
|
|
free(id);
|
|
}
|
|
} else {
|
|
cladatum = hashtab_search(policydbp->p_classes.table,
|
|
"process");
|
|
if (!cladatum) {
|
|
yyerror2("could not find process class for "
|
|
"legacy range_transition statement");
|
|
goto out;
|
|
}
|
|
|
|
ebitmap_set_bit(&rule->tclasses, cladatum->s.value - 1, TRUE);
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id) {
|
|
yyerror("no range in range_transition definition?");
|
|
goto out;
|
|
}
|
|
for (l = 0; l < 2; l++) {
|
|
levdatum = hashtab_search(policydbp->p_levels.table, id);
|
|
if (!levdatum) {
|
|
yyerror2("unknown level %s used in range_transition "
|
|
"definition", id);
|
|
free(id);
|
|
goto out;
|
|
}
|
|
free(id);
|
|
|
|
rule->trange.level[l].sens = levdatum->level->sens;
|
|
|
|
while ((id = queue_remove(id_queue))) {
|
|
if (parse_semantic_categories(id, levdatum,
|
|
&rule->trange.level[l].cat)) {
|
|
free(id);
|
|
goto out;
|
|
}
|
|
free(id);
|
|
}
|
|
|
|
id = (char *)queue_remove(id_queue);
|
|
if (!id)
|
|
break;
|
|
}
|
|
if (l == 0) {
|
|
if (mls_semantic_level_cpy(&rule->trange.level[1],
|
|
&rule->trange.level[0])) {
|
|
yyerror("out of memory");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
append_range_trans(rule);
|
|
return 0;
|
|
|
|
out:
|
|
range_trans_rule_destroy(rule);
|
|
return -1;
|
|
}
|
|
|
|
/* FLASK */
|