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