blob: db1438368313f8fa7d80ce628717c67e5eb3126f [file] [log] [blame]
/*
* Author : Stephen Smalley, <sds@tycho.nsa.gov>
*/
/*
* 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.
* Copyright (C) 2017 Mellanox Techonologies 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>
#ifndef IPPROTO_DCCP
#define IPPROTO_DCCP 33
#endif
#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif
#include <arpa/inet.h>
#include <stdlib.h>
#include <limits.h>
#include <inttypes.h>
#include <ctype.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 char source_file[PATH_MAX];
extern int yywarn(const char *msg);
extern int yyerror(const 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;
}
__attribute__ ((format(printf, 1, 2)))
void yyerror2(const 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(const 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;
}
static int read_classes(ebitmap_t *e_classes)
{
char *id;
class_datum_t *cladatum;
while ((id = queue_remove(id_queue))) {
if (!is_id_in_scope(SYM_CLASSES, id)) {
yyerror2("class %s is not within scope", id);
return -1;
}
cladatum = hashtab_search(policydbp->p_classes.table, id);
if (!cladatum) {
yyerror2("unknown class %s", id);
free(id);
return -1;
}
free(id);
if (ebitmap_set_bit(e_classes, cladatum->s.value - 1, TRUE)) {
yyerror("Out of memory");
return -1;
}
}
return 0;
}
int define_default_user(int which)
{
char *id;
class_datum_t *cladatum;
if (pass == 1) {
while ((id = queue_remove(id_queue)))
free(id);
return 0;
}
while ((id = queue_remove(id_queue))) {
if (!is_id_in_scope(SYM_CLASSES, id)) {
yyerror2("class %s is not within scope", id);
return -1;
}
cladatum = hashtab_search(policydbp->p_classes.table, id);
if (!cladatum) {
yyerror2("unknown class %s", id);
return -1;
}
if (cladatum->default_user && cladatum->default_user != which) {
yyerror2("conflicting default user information for class %s", id);
return -1;
}
cladatum->default_user = which;
free(id);
}
return 0;
}
int define_default_role(int which)
{
char *id;
class_datum_t *cladatum;
if (pass == 1) {
while ((id = queue_remove(id_queue)))
free(id);
return 0;
}
while ((id = queue_remove(id_queue))) {
if (!is_id_in_scope(SYM_CLASSES, id)) {
yyerror2("class %s is not within scope", id);
return -1;
}
cladatum = hashtab_search(policydbp->p_classes.table, id);
if (!cladatum) {
yyerror2("unknown class %s", id);
return -1;
}
if (cladatum->default_role && cladatum->default_role != which) {
yyerror2("conflicting default role information for class %s", id);
return -1;
}
cladatum->default_role = which;
free(id);
}
return 0;
}
int define_default_type(int which)
{
char *id;
class_datum_t *cladatum;
if (pass == 1) {
while ((id = queue_remove(id_queue)))
free(id);
return 0;
}
while ((id = queue_remove(id_queue))) {
if (!is_id_in_scope(SYM_CLASSES, id)) {
yyerror2("class %s is not within scope", id);
return -1;
}
cladatum = hashtab_search(policydbp->p_classes.table, id);
if (!cladatum) {
yyerror2("unknown class %s", id);
return -1;
}
if (cladatum->default_type && cladatum->default_type != which) {
yyerror2("conflicting default type information for class %s", id);
return -1;
}
cladatum->default_type = which;
free(id);
}
return 0;
}
int define_default_range(int which)
{
char *id;
class_datum_t *cladatum;
if (pass == 1) {
while ((id = queue_remove(id_queue)))
free(id);
return 0;
}
while ((id = queue_remove(id_queue))) {
if (!is_id_in_scope(SYM_CLASSES, id)) {
yyerror2("class %s is not within scope", id);
return -1;
}
cladatum = hashtab_search(policydbp->p_classes.table, id);
if (!cladatum) {
yyerror2("unknown class %s", id);
return -1;
}
if (cladatum->default_range && cladatum->default_range != which) {
yyerror2("conflicting default range information for class %s", id);
return -1;
}
cladatum->default_range = which;
free(id);
}
return 0;
}
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;
uint32_t 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 __attribute__ ((unused)), 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);
if (!cdatum) {
yyerror2("unknown category %s", id);
free(id);
return -1;
}
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;
}
int expand_attrib(void)
{
char *id;
ebitmap_t attrs;
type_datum_t *attr;
ebitmap_node_t *node;
uint32_t i;
int rc = -1;
int flags = 0;
if (pass == 1) {
for (i = 0; i < 2; i++) {
while ((id = queue_remove(id_queue))) {
free(id);
}
}
return 0;
}
ebitmap_init(&attrs);
while ((id = queue_remove(id_queue))) {
if (!id) {
yyerror("No attribute name for expandattribute statement?");
goto exit;
}
if (!is_id_in_scope(SYM_TYPES, id)) {
yyerror2("attribute %s is not within scope", id);
goto exit;
}
attr = hashtab_search(policydbp->p_types.table, id);
if (!attr) {
yyerror2("attribute %s is not declared", id);
goto exit;
}
if (attr->flavor != TYPE_ATTRIB) {
yyerror2("%s is a type, not an attribute", id);
goto exit;
}
if (ebitmap_set_bit(&attrs, attr->s.value - 1, TRUE)) {
yyerror("Out of memory!");
goto exit;
}
free(id);
}
id = (char *) queue_remove(id_queue);
if (!id) {
yyerror("No option specified for attribute expansion.");
goto exit;
}
if (!strcmp(id, "T")) {
flags = TYPE_FLAGS_EXPAND_ATTR_TRUE;
} else {
flags = TYPE_FLAGS_EXPAND_ATTR_FALSE;
}
ebitmap_for_each_positive_bit(&attrs, node, i) {
attr = hashtab_search(policydbp->p_types.table,
policydbp->sym_val_to_name[SYM_TYPES][i]);
attr->flags |= flags;
if ((attr->flags & TYPE_FLAGS_EXPAND_ATTR_TRUE) &&
(attr->flags & TYPE_FLAGS_EXPAND_ATTR_FALSE)) {
yywarn("Expandattribute option was set to both true and false. "
"Resolving to false.");
attr->flags &= ~TYPE_FLAGS_EXPAND_ATTR_TRUE;
}
}
rc = 0;
exit:
ebitmap_destroy(&attrs);
free(id);
return rc;
}
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;
}
free(id);
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;
}
free(id);
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);
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, 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) {
free(id);
if (!starallowed) {
yyerror("* not allowed in this type of rule");
return -1;
}
/* set TYPE_STAR flag */
set->flags = TYPE_STAR;
*add = 1;
return 0;
}
if (strcmp(id, "~") == 0) {
free(id);
if (!starallowed) {
yyerror("~ not allowed in this type of rule");
return -1;
}
/* complement the set */
set->flags = TYPE_COMP;
*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;
ebitmap_t tclasses;
ebitmap_node_t *node;
avrule_t *avrule;
class_perm_node_t *perm;
uint32_t i;
int 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;
avrule->source_line = source_lineno;
avrule->source_filename = strdup(source_file);
if (!avrule->source_filename) {
yyerror("out of memory");
return -1;
}
while ((id = queue_remove(id_queue))) {
if (set_types(&avrule->stypes, id, &add, 0))
goto bad;
}
add = 1;
while ((id = queue_remove(id_queue))) {
if (set_types(&avrule->ttypes, id, &add, 0))
goto bad;
}
ebitmap_init(&tclasses);
if (read_classes(&tclasses))
goto bad;
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);
free(id);
goto bad;
}
free(id);
ebitmap_for_each_positive_bit(&tclasses, node, i) {
perm = malloc(sizeof(class_perm_node_t));
if (!perm) {
yyerror("out of memory");
goto bad;
}
class_perm_node_init(perm);
perm->tclass = 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_tunable(int is_tunable)
{
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));
if (is_tunable)
datum->flags |= COND_BOOL_FLAGS_TUNABLE;
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?");
return -1;
}
datum->state = (int)(bool_value[0] == 'T') ? 1 : 0;
free(bool_value);
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;
}
typedef struct av_ioctl_range {
uint16_t low;
uint16_t high;
} av_ioctl_range_t;
struct av_ioctl_range_list {
uint8_t omit;
av_ioctl_range_t range;
struct av_ioctl_range_list *next;
};
int avrule_sort_ioctls(struct av_ioctl_range_list **rangehead)
{
struct av_ioctl_range_list *r, *r2, *sorted, *sortedhead = NULL;
/* order list by range.low */
for (r = *rangehead; r != NULL; r = r->next) {
sorted = malloc(sizeof(struct av_ioctl_range_list));
if (sorted == NULL)
goto error;
memcpy(sorted, r, sizeof(struct av_ioctl_range_list));
sorted->next = NULL;
if (sortedhead == NULL) {
sortedhead = sorted;
continue;
}
for (r2 = sortedhead; r2 != NULL; r2 = r2->next) {
if (sorted->range.low < r2->range.low) {
/* range is the new head */
sorted->next = r2;
sortedhead = sorted;
break;
} else if ((r2 ->next != NULL) &&
(r->range.low < r2->next->range.low)) {
/* insert range between elements */
sorted->next = r2->next;
r2->next = sorted;
break;
} else if (r2->next == NULL) {
/* range is the new tail*/
r2->next = sorted;
break;
}
}
}
r = *rangehead;
while (r != NULL) {
r2 = r;
r = r->next;
free(r2);
}
*rangehead = sortedhead;
return 0;
error:
yyerror("out of memory");
return -1;
}
int avrule_merge_ioctls(struct av_ioctl_range_list **rangehead)
{
struct av_ioctl_range_list *r, *tmp;
r = *rangehead;
while (r != NULL && r->next != NULL) {
/* merge */
if ((r->range.high + 1) >= r->next->range.low) {
/* keep the higher of the two */
if (r->range.high < r->next->range.high)
r->range.high = r->next->range.high;
tmp = r->next;
r->next = r->next->next;
free(tmp);
continue;
}
r = r->next;
}
return 0;
}
int avrule_read_ioctls(struct av_ioctl_range_list **rangehead)
{
char *id;
struct av_ioctl_range_list *rnew, *r = NULL;
*rangehead = NULL;
uint8_t omit = 0;
/* read in all the ioctl commands */
while ((id = queue_remove(id_queue))) {
if (strcmp(id,"~") == 0) {
/* these are values to be omitted */
free(id);
omit = 1;
} else if (strcmp(id,"-") == 0) {
/* high value of range */
free(id);
id = queue_remove(id_queue);
r->range.high = (uint16_t) strtoul(id,NULL,0);
if (r->range.high < r->range.low) {
yyerror("Ioctl ranges must be in ascending order.");
return -1;
}
free(id);
} else {
/* read in new low value */
rnew = malloc(sizeof(struct av_ioctl_range_list));
if (rnew == NULL)
goto error;
rnew->next = NULL;
if (*rangehead == NULL) {
*rangehead = rnew;
r = *rangehead;
} else {
r->next = rnew;
r = r->next;
}
rnew->range.low = (uint16_t) strtoul(id,NULL,0);
rnew->range.high = rnew->range.low;
free(id);
}
}
r = *rangehead;
r->omit = omit;
return 0;
error:
yyerror("out of memory");
return -1;
}
/* flip to included ranges */
int avrule_omit_ioctls(struct av_ioctl_range_list **rangehead)
{
struct av_ioctl_range_list *rnew, *r, *newhead, *r2;
rnew = calloc(1, sizeof(struct av_ioctl_range_list));
if (!rnew)
goto error;
newhead = rnew;
r = *rangehead;
r2 = newhead;
if (r->range.low == 0) {
r2->range.low = r->range.high + 1;
r = r->next;
} else {
r2->range.low = 0;
}
while (r) {
r2->range.high = r->range.low - 1;
rnew = calloc(1, sizeof(struct av_ioctl_range_list));
if (!rnew)
goto error;
r2->next = rnew;
r2 = r2->next;
r2->range.low = r->range.high + 1;
if (!r->next)
r2->range.high = 0xffff;
r = r->next;
}
r = *rangehead;
while (r != NULL) {
r2 = r;
r = r->next;
free(r2);
}
*rangehead = newhead;
return 0;
error:
yyerror("out of memory");
return -1;
}
int avrule_ioctl_ranges(struct av_ioctl_range_list **rangelist)
{
struct av_ioctl_range_list *rangehead;
uint8_t omit;
/* read in ranges to include and omit */
if (avrule_read_ioctls(&rangehead))
return -1;
if (rangehead == NULL) {
yyerror("error processing ioctl commands");
return -1;
}
omit = rangehead->omit;
/* sort and merge the input ioctls */
if (avrule_sort_ioctls(&rangehead))
return -1;
if (avrule_merge_ioctls(&rangehead))
return -1;
/* flip ranges if these are ommited*/
if (omit) {
if (avrule_omit_ioctls(&rangehead))
return -1;
}
*rangelist = rangehead;
return 0;
}
int define_te_avtab_xperms_helper(int which, avrule_t ** rule)
{
char *id;
class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL;
class_datum_t *cladatum;
perm_datum_t *perdatum = NULL;
ebitmap_t tclasses;
ebitmap_node_t *node;
avrule_t *avrule;
unsigned int i;
int add = 1, ret = 0;
avrule = (avrule_t *) malloc(sizeof(avrule_t));
if (!avrule) {
yyerror("out of memory");
ret = -1;
goto out;
}
avrule_init(avrule);
avrule->specified = which;
avrule->line = policydb_lineno;
avrule->source_line = source_lineno;
avrule->source_filename = strdup(source_file);
avrule->xperms = NULL;
if (!avrule->source_filename) {
yyerror("out of memory");
return -1;
}
while ((id = queue_remove(id_queue))) {
if (set_types
(&avrule->stypes, id, &add,
which == AVRULE_XPERMS_NEVERALLOW ? 1 : 0)) {
ret = -1;
goto out;
}
}
add = 1;
while ((id = queue_remove(id_queue))) {
if (strcmp(id, "self") == 0) {
free(id);
if (add == 0) {
yyerror("-self is not supported");
ret = -1;
goto out;
}
avrule->flags |= RULE_SELF;
continue;
}
if (set_types
(&avrule->ttypes, id, &add,
which == AVRULE_XPERMS_NEVERALLOW ? 1 : 0)) {
ret = -1;
goto out;
}
}
ebitmap_init(&tclasses);
ret = read_classes(&tclasses);
if (ret)
goto out;
perms = NULL;
id = queue_head(id_queue);
ebitmap_for_each_positive_bit(&tclasses, node, i) {
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->tclass = i + 1;
if (!perms)
perms = cur_perms;
if (tail)
tail->next = cur_perms;
tail = cur_perms;
cladatum = policydbp->class_val_to_struct[i];
perdatum = hashtab_search(cladatum->permissions.table, id);
if (!perdatum) {
if (cladatum->comdatum) {
perdatum = hashtab_search(cladatum->comdatum->
permissions.table,
id);
}
}
if (!perdatum) {
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])) {
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);
}
}
ebitmap_destroy(&tclasses);
avrule->perms = perms;
*rule = avrule;
out:
return ret;
}
/* index of the u32 containing the permission */
#define XPERM_IDX(x) (x >> 5)
/* set bits 0 through x-1 within the u32 */
#define XPERM_SETBITS(x) ((1 << (x & 0x1f)) - 1)
/* low value for this u32 */
#define XPERM_LOW(x) (x << 5)
/* high value for this u32 */
#define XPERM_HIGH(x) (((x + 1) << 5) - 1)
void avrule_xperm_setrangebits(uint16_t low, uint16_t high,
av_extended_perms_t *xperms)
{
unsigned int i;
uint16_t h = high + 1;
/* for each u32 that this low-high range touches, set driver permissions */
for (i = XPERM_IDX(low); i <= XPERM_IDX(high); i++) {
/* set all bits in u32 */
if ((low <= XPERM_LOW(i)) && (high >= XPERM_HIGH(i)))
xperms->perms[i] |= ~0U;
/* set low bits */
else if ((low <= XPERM_LOW(i)) && (high < XPERM_HIGH(i)))
xperms->perms[i] |= XPERM_SETBITS(h);
/* set high bits */
else if ((low > XPERM_LOW(i)) && (high >= XPERM_HIGH(i)))
xperms->perms[i] |= ~0U - XPERM_SETBITS(low);
/* set middle bits */
else if ((low > XPERM_LOW(i)) && (high <= XPERM_HIGH(i)))
xperms->perms[i] |= XPERM_SETBITS(h) - XPERM_SETBITS(low);
}
}
int avrule_xperms_used(av_extended_perms_t *xperms)
{
unsigned int i;
for (i = 0; i < sizeof(xperms->perms)/sizeof(xperms->perms[0]); i++) {
if (xperms->perms[i])
return 1;
}
return 0;
}
/*
* using definitions found in kernel document ioctl-number.txt
* The kernel components of an ioctl command are:
* dir, size, driver, and fucntion. Only the driver and function fields
* are considered here
*/
#define IOC_DRIV(x) (x >> 8)
#define IOC_FUNC(x) (x & 0xff)
#define IOC_CMD(driver, func) ((driver << 8) + func)
int avrule_ioctl_partialdriver(struct av_ioctl_range_list *rangelist,
av_extended_perms_t *complete_driver,
av_extended_perms_t **extended_perms)
{
struct av_ioctl_range_list *r;
av_extended_perms_t *xperms;
uint8_t low, high;
xperms = calloc(1, sizeof(av_extended_perms_t));
if (!xperms) {
yyerror("out of memory");
return - 1;
}
r = rangelist;
while(r) {
low = IOC_DRIV(r->range.low);
high = IOC_DRIV(r->range.high);
if (complete_driver) {
if (!xperm_test(low, complete_driver->perms))
xperm_set(low, xperms->perms);
if (!xperm_test(high, complete_driver->perms))
xperm_set(high, xperms->perms);
} else {
xperm_set(low, xperms->perms);
xperm_set(high, xperms->perms);
}
r = r->next;
}
if (avrule_xperms_used(xperms)) {
*extended_perms = xperms;
} else {
free(xperms);
*extended_perms = NULL;
}
return 0;
}
int avrule_ioctl_completedriver(struct av_ioctl_range_list *rangelist,
av_extended_perms_t **extended_perms)
{
struct av_ioctl_range_list *r;
av_extended_perms_t *xperms;
uint16_t low, high;
xperms = calloc(1, sizeof(av_extended_perms_t));
if (!xperms) {
yyerror("out of memory");
return - 1;
}
r = rangelist;
while(r) {
/*
* Any driver code that has sequence 0x00 - 0xff is a complete code,
*
* if command number = 0xff, then round high up to next code,
* else 0x00 - 0xfe keep current code
* of this range. temporarily u32 for the + 1
* to account for possible rollover before right shift
*/
high = IOC_DRIV((uint32_t) (r->range.high + 1));
/* if 0x00 keep current driver code else 0x01 - 0xff round up to next code*/
low = IOC_DRIV(r->range.low);
if (IOC_FUNC(r->range.low))
low++;
if (high > low)
avrule_xperm_setrangebits(low, high - 1, xperms);
r = r->next;
}
if (avrule_xperms_used(xperms)) {
xperms->driver = 0x00;
xperms->specified = AVRULE_XPERMS_IOCTLDRIVER;
*extended_perms = xperms;
} else {
free(xperms);
*extended_perms = NULL;
}
return 0;
}
int avrule_ioctl_func(struct av_ioctl_range_list *rangelist,
av_extended_perms_t **extended_perms, unsigned int driver)
{
struct av_ioctl_range_list *r;
av_extended_perms_t *xperms;
uint16_t low, high;
*extended_perms = NULL;
xperms = calloc(1, sizeof(av_extended_perms_t));
if (!xperms) {
yyerror("out of memory");
return - 1;
}
r = rangelist;
/* for the passed in driver code, find the ranges that apply */
while (r) {
low = r->range.low;
high = r->range.high;
if ((driver != IOC_DRIV(low)) && (driver != IOC_DRIV(high))) {
r = r->next;
continue;
}
if (driver == IOC_DRIV(low)) {
if (high > IOC_CMD(driver, 0xff))
high = IOC_CMD(driver, 0xff);
} else {
if (low < IOC_CMD(driver, 0))
low = IOC_CMD(driver, 0);
}
low = IOC_FUNC(low);
high = IOC_FUNC(high);
avrule_xperm_setrangebits(low, high, xperms);
xperms->driver = driver;
xperms->specified = AVRULE_XPERMS_IOCTLFUNCTION;
r = r->next;
}
if (avrule_xperms_used(xperms)) {
*extended_perms = xperms;
} else {
free(xperms);
*extended_perms = NULL;
}
return 0;
}
void avrule_ioctl_freeranges(struct av_ioctl_range_list *rangelist)
{
struct av_ioctl_range_list *r, *tmp;
r = rangelist;
while (r) {
tmp = r;
r = r->next;
free(tmp);
}
}
unsigned int xperms_for_each_bit(unsigned int *bit, av_extended_perms_t *xperms)
{
unsigned int i;
for (i = *bit; i < sizeof(xperms->perms)*8; i++) {
if (xperm_test(i,xperms->perms)) {
xperm_clear(i, xperms->perms);
*bit = i;
return 1;
}
}
return 0;
}
int avrule_cpy(avrule_t *dest, avrule_t *src)
{
class_perm_node_t *src_perms;
class_perm_node_t *dest_perms, *dest_tail;
dest_tail = NULL;
avrule_init(dest);
dest->specified = src->specified;
dest->flags = src->flags;
if (type_set_cpy(&dest->stypes, &src->stypes)) {
yyerror("out of memory");
return - 1;
}
if (type_set_cpy(&dest->ttypes, &src->ttypes)) {
yyerror("out of memory");
return - 1;
}
dest->line = src->line;
dest->source_filename = strdup(source_file);
if (!dest->source_filename) {
yyerror("out of memory");
return -1;
}
dest->source_line = src->source_line;
/* increment through the class perms and copy over */
src_perms = src->perms;
while (src_perms) {
dest_perms = (class_perm_node_t *) calloc(1, sizeof(class_perm_node_t));
class_perm_node_init(dest_perms);
if (!dest_perms) {
yyerror("out of memory");
return -1;
}
if (!dest->perms)
dest->perms = dest_perms;
else
dest_tail->next = dest_perms;
dest_perms->tclass = src_perms->tclass;
dest_perms->data = src_perms->data;
dest_perms->next = NULL;
dest_tail = dest_perms;
src_perms = src_perms->next;
}
return 0;
}
int define_te_avtab_ioctl(avrule_t *avrule_template)
{
avrule_t *avrule;
struct av_ioctl_range_list *rangelist;
av_extended_perms_t *complete_driver, *partial_driver, *xperms;
unsigned int i;
/* organize ioctl ranges */
if (avrule_ioctl_ranges(&rangelist))
return -1;
/* create rule for ioctl driver types that are entirely enabled */
if (avrule_ioctl_completedriver(rangelist, &complete_driver))
return -1;
if (complete_driver) {
avrule = (avrule_t *) calloc(1, sizeof(avrule_t));
if (!avrule) {
yyerror("out of memory");
return -1;
}
if (avrule_cpy(avrule, avrule_template))
return -1;
avrule->xperms = complete_driver;
append_avrule(avrule);
}
/* flag ioctl driver codes that are partially enabled */
if (avrule_ioctl_partialdriver(rangelist, complete_driver, &partial_driver))
return -1;
if (!partial_driver || !avrule_xperms_used(partial_driver))
goto done;
/*
* create rule for each partially used driver codes
* "partially used" meaning that the code number e.g. socket 0x89
* has some permission bits set and others not set.
*/
i = 0;
while (xperms_for_each_bit(&i, partial_driver)) {
if (avrule_ioctl_func(rangelist, &xperms, i))
return -1;
if (xperms) {
avrule = (avrule_t *) calloc(1, sizeof(avrule_t));
if (!avrule) {
yyerror("out of memory");
return -1;
}
if (avrule_cpy(avrule, avrule_template))
return -1;
avrule->xperms = xperms;
append_avrule(avrule);
}
}
done:
if (partial_driver)
free(partial_driver);
return 0;
}
int define_te_avtab_extended_perms(int which)
{
char *id;
unsigned int i;
avrule_t *avrule_template;
if (pass == 1) {
for (i = 0; i < 4; i++) {
while ((id = queue_remove(id_queue)))
free(id);
}
return 0;
}
/* populate avrule template with source/target/tclass */
if (define_te_avtab_xperms_helper(which, &avrule_template))
return -1;
id = queue_remove(id_queue);
if (strcmp(id,"ioctl") == 0) {
free(id);
if (define_te_avtab_ioctl(avrule_template))
return -1;
} else {
yyerror("only ioctl extended permissions are supported");
free(id);
return -1;
}
return 0;
}
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;
avrule->source_line = source_lineno;
avrule->source_filename = strdup(source_file);
avrule->xperms = NULL;
if (!avrule->source_filename) {
yyerror("out of memory");
return -1;
}
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);
if (add == 0) {
yyerror("-self is not supported");
ret = -1;
goto out;
}
avrule->flags |= RULE_SELF;
continue;
}
if (set_types
(&avrule->ttypes, id, &add,
which == AVRULE_NEVERALLOW ? 1 : 0)) {
ret = -1;
goto out;
}
}
ebitmap_init(&tclasses);
ret = read_classes(&tclasses);
if (ret)
goto out;
perms = NULL;
ebitmap_for_each_positive_bit(&tclasses, node, i) {
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->tclass = 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_positive_bit(&tclasses, node, i) {
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:
if (ret) {
avrule_destroy(avrule);
free(avrule);
}
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;
}
role = get_local_role(id, role->s.value, (role->flavor == ROLE_ATTRIB));
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(