| /* |
| * 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> |
| #ifndef IPPROTO_DCCP |
| #define IPPROTO_DCCP 33 |
| #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); |
| return -1; |
| } |
| if (ebitmap_set_bit(e_classes, cladatum->s.value - 1, TRUE)) { |
| yyerror("Out of memory"); |
| return -1; |
| } |
| free(id); |
| } |
| 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); |
| 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; |
| 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); |
| 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"); |
| 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?"); |
| 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; |
| } |
| |
| 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; |
| omit = rangehead->omit; |
| if (rangehead == NULL) { |
| yyerror("error processing ioctl commands"); |
| return -1; |
| } |
| /* 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); |
| 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_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->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) { |
| if (define_te_avtab_ioctl(avrule_template)) |
| return -1; |
| free(id); |
| } else { |
| yyerror("only ioctl extended permissions are supported"); |
| 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); |
| 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_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->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_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"); |
| free(new); |
| return NULL; |
| } |
| if (ebitmap_or(&new->types.types, &r1->types.types, &r2->types.types)) { |
| yyerror("out of memory"); |
| free(new); |
| 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 __attribute__ ((unused)), |
| 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; |
| uint32_t 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) { |
| if (read_classes(&e_classes)) |
| return -1; |
| } else { |
| cladatum = hashtab_search(policydbp->p_classes.table, |
|