/* Copyright (c) 2008-2009 Nall Design Works Copyright 2006 Trusted Computer Solutions, Inc. */ /* Exported Interface int init_translations(void); void finish_context_translations(void); int trans_context(const security_context_t, security_context_t *); int untrans_context(const security_context_t, security_context_t *); */ #include <math.h> #include <glob.h> #include <values.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <stdio_ext.h> #include <ctype.h> #include <selinux/selinux.h> #include <selinux/context.h> #include <syslog.h> #include <errno.h> #include <pcre.h> #include <ctype.h> #include <time.h> #include <sys/time.h> #include "mls_level.h" #include "mcstrans.h" #define N_BUCKETS 1453 #define OVECCOUNT (512*3) #define log_error(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) #ifdef DEBUG #define log_debug(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) #else #define log_debug(fmt, ...) ; #endif static unsigned int maxbit=0; /* Define data structures */ typedef struct context_map { char *raw; char *trans; } context_map_t; typedef struct context_map_node { char *key; context_map_t *map; struct context_map_node *next; } context_map_node_t; typedef struct affix { char *text; struct affix *next; } affix_t; typedef struct word { char *text; ebitmap_t cat; ebitmap_t normal; ebitmap_t inverse; struct word *next; } word_t; typedef struct word_group { char *name; char *whitespace; char *join; affix_t *prefixes; affix_t *suffixes; word_t *words; pcre *prefix_regexp; pcre *word_regexp; pcre *suffix_regexp; ebitmap_t def; word_t **sword; unsigned int sword_len; struct word_group *next; } word_group_t; typedef struct base_classification { char *trans; mls_level_t *level; struct base_classification *next; } base_classification_t; typedef struct domain { char *name; context_map_node_t *raw_to_trans[N_BUCKETS]; context_map_node_t *trans_to_raw[N_BUCKETS]; base_classification_t *base_classifications; word_group_t *groups; pcre *base_classification_regexp; struct domain *next; } domain_t; static domain_t *domains; typedef struct sens_constraint { char op; char *text; unsigned int sens; ebitmap_t cat; struct sens_constraint *next; } sens_constraint_t; static sens_constraint_t *sens_constraints; typedef struct cat_constraint { char op; char *text; int nbits; ebitmap_t mask; ebitmap_t cat; struct cat_constraint *next; } cat_constraint_t; static cat_constraint_t *cat_constraints; unsigned int hash(const char *str) { unsigned int hash = 5381; int c; while ((c = *(unsigned const char *)str++)) hash = ((hash << 5) + hash) + c; return hash; } static int add_to_hashtable(context_map_node_t **table, char *key, context_map_t *map) { unsigned int bucket = hash(key) % N_BUCKETS; context_map_node_t **n; for (n = &table[bucket]; *n; n = &(*n)->next) ; *n = malloc(sizeof(context_map_node_t)); if (! *n) goto err; (*n)->key = key; (*n)->map = map; (*n)->next = NULL; return 0; err: syslog(LOG_ERR, "add_to_hashtable: allocation error"); return -1; } static int numdigits(unsigned int n) { int count = 1; while ((n = n / 10)) count++; return count; } static int parse_category(ebitmap_t *e, const char *raw, int allowinverse) { int inverse = 0; unsigned int low, high; while (*raw) { if (allowinverse && *raw == '~') { inverse = !inverse; raw++; continue; } if (sscanf(raw,"c%u", &low) != 1) return -1; raw += numdigits(low) + 1; if (*raw == '.') { raw++; if (sscanf(raw,"c%u", &high) != 1) return -1; raw += numdigits(high) + 1; } else { high = low; } while (low <= high) { if (low >= maxbit) maxbit = low + 1; if (ebitmap_set_bit(e, low, inverse ? 0 : 1) < 0) return -1; low++; } if (*raw == ',') { raw++; inverse = 0; } else if (*raw != '\0') { return -1; } } return 0; } int parse_ebitmap(ebitmap_t *e, ebitmap_t *def, const char *raw) { int rc = ebitmap_cpy(e, def); if (rc < 0) return rc; rc = parse_category(e, raw, 1); if (rc < 0) return rc; return 0; } mls_level_t * parse_raw(const char *raw) { mls_level_t *mls = calloc(1, sizeof(mls_level_t)); if (!mls) goto err; if (sscanf(raw,"s%u", &mls->sens) != 1) goto err; raw += numdigits(mls->sens) + 1; if (*raw == ':') { raw++; if (parse_category(&mls->cat, raw, 0) < 0) goto err; } else if (*raw != '\0') { goto err; } return mls; err: ebitmap_destroy(&mls->cat); free(mls); return NULL; } void destroy_word(word_t **list, word_t *word) { if (!word) { return; } for (; list && *list; list = &(*list)->next) { if (*list == word) { *list = word->next; break; } } free(word->text); ebitmap_destroy(&word->cat); ebitmap_destroy(&word->normal); ebitmap_destroy(&word->inverse); memset(word, 0, sizeof(word_t)); free(word); } word_t * create_word(word_t **list, const char *text) { word_t *w = calloc(1, sizeof(word_t)); if (!w) { goto err; } w->text = strdup(text); if (!w->text) { goto err; } if (list) { for (; *list; list = &(*list)->next) ; *list = w; } return w; err: log_error("create_word: allocation error %s", strerror(errno)); destroy_word(NULL, w); return NULL; } void destroy_group(word_group_t **list, word_group_t *group) { for (; list && *list; list = &(*list)->next) { if (*list == group) { *list = group->next; break; } } while(group->prefixes) { affix_t *next = group->prefixes->next; free(group->prefixes->text); free(group->prefixes); group->prefixes=next; } while(group->suffixes) { affix_t *next = group->suffixes->next; free(group->suffixes->text); free(group->suffixes); group->suffixes=next; } while(group->words) destroy_word(&group->words, group->words); free(group->whitespace); free(group->name); free(group->sword); free(group->join); pcre_free(group->prefix_regexp); pcre_free(group->word_regexp); pcre_free(group->suffix_regexp); ebitmap_destroy(&group->def); free(group); } word_group_t * create_group(word_group_t **list, const char *name) { word_group_t *group = calloc(1, sizeof(word_group_t)); if (!group) return NULL; group->name = strdup(name); if (!group->name) { goto err; } group->join = strdup(" "); if (!group->join) { goto err; } group->whitespace = strdup(" "); if (!group->whitespace) { goto err; } group->sword = NULL; if (list) { for (; *list; list = &(*list)->next) ; *list = group; } return group; err: log_error("allocation error %s", strerror(errno)); destroy_group(NULL, group); return NULL; } void destroy_domain(domain_t *domain) { int i; unsigned int rt = 0, tr = 0; for (i=0; i < N_BUCKETS; i++) { context_map_node_t *ptr; for (ptr = domain->trans_to_raw[i]; ptr;) { context_map_node_t *t = ptr->next; free(ptr); ptr = t; tr++; } domain->trans_to_raw[i] = NULL; } for (i=0; i < N_BUCKETS; i++) { context_map_node_t *ptr; for (ptr = domain->raw_to_trans[i]; ptr;) { context_map_node_t *t = ptr->next; free(ptr->map->raw); free(ptr->map->trans); free(ptr->map); free(ptr); ptr = t; rt++; } domain->raw_to_trans[i] = NULL; } while (domain->base_classifications) { base_classification_t *next = domain->base_classifications->next; free(domain->base_classifications->trans); ebitmap_destroy(&domain->base_classifications->level->cat); free(domain->base_classifications->level); free(domain->base_classifications); domain->base_classifications = next; } pcre_free(domain->base_classification_regexp); while (domain->groups) destroy_group(&domain->groups, domain->groups); free(domain->name); free(domain); syslog(LOG_INFO, "cache sizes: tr = %u, rt = %u", tr, rt); } domain_t * create_domain(const char *name) { domain_t *domain = calloc(1, sizeof(domain_t)); if (!domain) { goto err; } domain->name = strdup(name); if (!domain->name) { goto err; } domain_t **d = &domains; for (; *d; d = &(*d)->next) ; *d = domain; return domain; err: log_error("allocation error %s", strerror(errno)); destroy_domain(domain); return NULL; } int add_word(word_group_t *group, char *raw, char *trans) { if (strchr(trans,'-')) { log_error("'%s'is invalid because '-' is illegal in modifiers.\n", trans); return -1; } word_t *word = create_word(&group->words, trans); int rc = parse_ebitmap(&word->cat, &group->def, raw); if (rc < 0) { log_error(" syntax error in %s\n", raw); destroy_word(&group->words, word); return -1; } if (ebitmap_andnot(&word->normal, &word->cat, &group->def, maxbit) < 0) return -1; ebitmap_t temp; if (ebitmap_xor(&temp, &word->cat, &group->def) < 0) return -1; if (ebitmap_and(&word->inverse, &temp, &group->def) < 0) return -1; ebitmap_destroy(&temp); return 0; } int add_constraint(char op, char *raw, char *tok) { log_debug("%s\n", "add_constraint"); ebitmap_t empty; ebitmap_init(&empty); if (!raw || !*raw) { syslog(LOG_ERR, "unable to parse line"); return -1; } if (*raw == 's') { sens_constraint_t *constraint = calloc(1, sizeof(sens_constraint_t)); if (!constraint) { log_error("allocation error %s", strerror(errno)); return -1; } if (sscanf(raw,"s%u", &constraint->sens) != 1) { syslog(LOG_ERR, "unable to parse level"); free(constraint); return -1; } if (parse_ebitmap(&constraint->cat, &empty, tok) < 0) { syslog(LOG_ERR, "unable to parse cat"); free(constraint); return -1; } if (asprintf(&constraint->text, "%s%c%s", raw, op, tok) < 0) { log_error("asprintf failed %s", strerror(errno)); return -1; } constraint->op = op; sens_constraint_t **p; for (p= &sens_constraints; *p; p = &(*p)->next) ; *p = constraint; return 0; } else if (*raw == 'c' ) { cat_constraint_t *constraint = calloc(1, sizeof(cat_constraint_t)); if (!constraint) { log_error("allocation error %s", strerror(errno)); return -1; } if (parse_ebitmap(&constraint->mask, &empty, raw) < 0) { syslog(LOG_ERR, "unable to parse mask"); free(constraint); return -1; } if (parse_ebitmap(&constraint->cat, &empty, tok) < 0) { syslog(LOG_ERR, "unable to parse cat"); ebitmap_destroy(&constraint->mask); free(constraint); return -1; } if (asprintf(&constraint->text, "%s%c%s", raw, op, tok) < 0) { log_error("asprintf failed %s", strerror(errno)); return -1; } constraint->nbits = ebitmap_cardinality(&constraint->cat); constraint->op = op; cat_constraint_t **p; for (p= &cat_constraints; *p; p = &(*p)->next) ; *p = constraint; return 0; } else { return -1; } return 0; } int violates_constraints(mls_level_t *l) { int nbits; sens_constraint_t *s; ebitmap_t common; for (s=sens_constraints; s; s=s->next) { if (s->sens == l->sens) { if (ebitmap_and(&common, &s->cat, &l->cat) < 0) return 1; nbits = ebitmap_cardinality(&common); ebitmap_destroy(&common); if (nbits) { char *text = mls_level_to_string(l); syslog(LOG_WARNING, "%s violates %s", text, s->text); free(text); return 1; } } } cat_constraint_t *c; for (c=cat_constraints; c; c=c->next) { if (ebitmap_and(&common, &c->mask, &l->cat) < 0) return 1; nbits = ebitmap_cardinality(&common); ebitmap_destroy(&common); if (nbits > 0) { if (ebitmap_and(&common, &c->cat, &l->cat) < 0) return 1; nbits = ebitmap_cardinality(&common); ebitmap_destroy(&common); if ((c->op == '!' && nbits) || (c->op == '>' && nbits != c->nbits)) { char *text = mls_level_to_string(l); syslog(LOG_WARNING, "%s violates %s (%d,%d)", text, c->text, nbits, c->nbits); free(text); return 1; } } } return 0; } void destroy_sens_constraint(sens_constraint_t **list, sens_constraint_t *constraint) { if (!constraint) { return; } for (; list && *list; list = &(*list)->next) { if (*list == constraint) { *list = constraint->next; break; } } ebitmap_destroy(&constraint->cat); free(constraint->text); memset(constraint, 0, sizeof(sens_constraint_t)); free(constraint); } void destroy_cat_constraint(cat_constraint_t **list, cat_constraint_t *constraint) { if (!constraint) { return; } for (; list && *list; list = &(*list)->next) { if (*list == constraint) { *list = constraint->next; break; } } ebitmap_destroy(&constraint->mask); ebitmap_destroy(&constraint->cat); free(constraint->text); memset(constraint, 0, sizeof(cat_constraint_t)); free(constraint); } static int add_base_classification(domain_t *domain, char *raw, char *trans) { mls_level_t *level = parse_raw(raw); if (level) { base_classification_t **i; base_classification_t *base_classification = calloc(1, sizeof(base_classification_t)); if (!base_classification) { log_error("allocation error %s", strerror(errno)); return -1; } base_classification->trans=strdup(trans); if (!base_classification->trans) { log_error("allocation error %s", strerror(errno)); free(base_classification); return -1; } base_classification->level=level; for (i=&domain->base_classifications; *i; i=&(*i)->next) ; *i = base_classification; return 0; } log_error(" add_base_classification error %s %s\n", raw, trans); return -1; } static int add_cache(domain_t *domain, char *raw, char *trans) { context_map_t *map = calloc(1, sizeof(context_map_t)); if (!map) goto err; map->raw = strdup(raw); if (!map->raw) { goto err; } map->trans = strdup(trans); if (!map->trans) { goto err; } log_debug(" add_cache (%s,%s)\n", raw, trans); if (add_to_hashtable(domain->raw_to_trans, map->raw, map) < 0) goto err; if (add_to_hashtable(domain->trans_to_raw, map->trans, map) < 0) goto err; return 0; err: log_error("%s: allocation error", "add_cache"); return -1; } static context_map_t * find_in_table(context_map_node_t **table, const char *key) { unsigned int bucket = hash(key) % N_BUCKETS; context_map_node_t **n; for (n = &table[bucket]; *n; n = &(*n)->next) if (!strcmp((*n)->key, key)) return (*n)->map; return NULL; } char * trim(char *str, const char *whitespace) { char *p = str + strlen(str); while (p > str && strchr(whitespace, *(p-1)) != NULL) *--p = 0; return str; } char * triml(char *str, const char *whitespace) { char *p = str; while (*p && (strchr(whitespace, *p) != NULL)) p++; return p; } int update(char **p, char *const val) { free (*p); *p = strdup(val); if (!*p) { log_error("allocation error %s", strerror(errno)); return -1; } return 0; } int append(affix_t **affixes, const char *val) { affix_t *affix = calloc(1, sizeof(affix_t)); if (!affix) { goto err; } affix->text = strdup(val); if (!affix->text) goto err; for (;*affixes; affixes = &(*affixes)->next) ; *affixes = affix; return 0; err: log_error("allocation error %s", strerror(errno)); free(affix); return -1; } static int read_translations(const char *filename); /* Process line from translation file. Remove white space and set raw do data before the "=" and tok to data after it Modifies the data pointed to by the buffer parameter */ static int process_trans(char *buffer) { static domain_t *domain; static word_group_t *group; static int base_classification; static int lineno = 0; char op='\0'; lineno++; log_debug("%d: %s", lineno, buffer); /* zap leading whitespace */ buffer = triml(buffer, "\t "); /* Ignore comments */ if (*buffer == '#') return 0; char *comment = strpbrk (buffer, "#"); if (comment) { *comment = '\0'; } /* zap trailing whitespace */ buffer = trim(buffer, "\t \r\n"); if (*buffer == 0) return 0; char *delim = strpbrk (buffer, "=!>"); if (! delim) { syslog(LOG_ERR, "invalid line (no !, = or >) %d", lineno); return -1; } op = *delim; *delim = '\0'; char *raw = buffer; char *tok = delim+1; /* remove trailing/leading whitespace from the split tokens */ trim(raw, "\t "); tok = triml(tok, "\t "); if (! *raw) { syslog(LOG_ERR, "invalid line %d", lineno); return -1; } if (! *tok) { syslog(LOG_ERR, "invalid line %d", lineno); return -1; } /* constraints have different syntax */ if (op == '!' || op == '>') { return add_constraint(op, raw, tok); } if (!strcmp(raw, "Domain")) { domain = create_domain(tok); group = NULL; return 0; } if (!domain) { domain = create_domain("Default"); if (!domain) return -1; group = NULL; } if (!group && (!strcmp(raw, "Whitespace") || !strcmp(raw, "Join") || !strcmp(raw, "Prefix") || !strcmp(raw, "Suffix") || !strcmp(raw, "Default"))) { syslog(LOG_ERR, "expected ModifierGroup declaration on line %d", lineno); return -1; } if (!strcmp(raw, "Include")) { unsigned int n; glob_t g; g.gl_offs = 0; if (glob(tok, GLOB_ERR, NULL, &g) < 0) { globfree(&g); return -1; } for (n=0; n < g.gl_pathc; n++) { if (read_translations(g.gl_pathv[n]) < 0) { globfree(&g); return -1; } } globfree(&g); } else if (!strcmp(raw, "Base")) { base_classification = 1; } else if (!strcmp(raw, "ModifierGroup")) { group = create_group(&domain->groups, tok); if (!group) return -1; base_classification = 0; } else if (!strcmp(raw, "Whitespace")) { if (update (&group->whitespace, tok) < 0) return -1; } else if (!strcmp(raw, "Join")) { if (update (&group->join, tok) < 0) return -1; } else if (!strcmp(raw, "Prefix")) { if (append (&group->prefixes, tok) < 0) return -1; } else if (!strcmp(raw, "Suffix")) { if (append (&group->suffixes, tok) < 0) return -1; } else if (!strcmp(raw, "Default")) { ebitmap_t empty; ebitmap_init(&empty); if (parse_ebitmap(&group->def, &empty, tok) < 0) { syslog(LOG_ERR, "unable to parse Default %d", lineno); return -1; } } else if (group) { if (add_word(group, raw, tok) < 0) { syslog(LOG_ERR, "unable to add base_classification on line %d", lineno); return -1; } } else { if (base_classification) { if (add_base_classification(domain, raw, tok) < 0) { syslog(LOG_ERR, "unable to add base_classification on line %d", lineno); return -1; } } if (add_cache(domain, raw, tok) < 0) return -1; } return 0; } int read_translations(const char *filename) { size_t size = 0; char *buffer = NULL; int rval = 0; FILE *cfg = fopen(filename,"r"); if (!cfg) { syslog(LOG_ERR, "%s file open failed", filename); return -1; } __fsetlocking(cfg, FSETLOCKING_BYCALLER); while (getline(&buffer, &size, cfg) > 0) { if( process_trans(buffer) < 0 ) { syslog(LOG_ERR, "%s file read failed", filename); rval = -1; break; } } free(buffer); fclose(cfg); return rval; } int init_translations(void) { if (is_selinux_mls_enabled() <= 0) return -1; return(read_translations(selinux_translations_path())); } char * extract_range(const security_context_t incon) { context_t con = context_new(incon); if (!con) { syslog(LOG_ERR, "extract_range context_new(%s) failed: %s", incon, strerror(errno)); return NULL; } const char *range = context_range_get(con); if (!range) { syslog(LOG_ERR, "extract_range: context_range_get(%s) failed: %m", incon); context_free(con); return NULL; } char *r = strdup(range); if (!r) { log_error("extract_range: allocation error %s", strerror(errno)); return NULL; } context_free(con); return r; } char * new_context_str(const security_context_t incon, const char *range) { char *rcon = NULL; context_t con = context_new(incon); if (!con) { goto exit; } context_range_set(con, range); rcon = strdup(context_str(con)); if (!rcon) { goto exit; } return rcon; exit: log_error("new_context_str: %s %s", incon, strerror(errno)); return NULL; } char * find_in_hashtable(const char *range, domain_t *domain, context_map_node_t **table) { char *trans = NULL; context_map_t *map = find_in_table(table, range); if (map) { trans = strdup((table == domain->raw_to_trans) ? map->trans: map->raw); if (!trans) { log_error("find_in_hashtable: allocation error %s", strerror(errno)); return NULL; } log_debug(" found %s in hashtable returning %s\n", range, trans); } return trans; } void emit_whitespace(char*buffer, char *whitespace) { strcat(buffer, "["); strcat(buffer, whitespace); strcat(buffer, "]"); } static int string_size(const void *p1, const void *p2) { return strlen(*(char **)p2) - strlen(*(char **)p1); } static int word_size(const void *p1, const void *p2) { word_t *w1 = *(word_t **)p1; word_t *w2 = *(word_t **)p2; int w1_len=strlen(w1->text); int w2_len=strlen(w2->text); if (w1_len == w2_len) return strcmp(w1->text, w2->text); return (w2_len - w1_len); } void build_regexp(pcre **r, char *buffer) { const char *error; int error_offset; if (*r) pcre_free(*r); *r = pcre_compile(buffer, PCRE_CASELESS, &error, &error_offset, NULL); if (error) { log_error("pcre=%s, error=%s\n", buffer, error ? error: "none"); } buffer[0] = '\0'; } int build_regexps(domain_t *domain) { char buffer[1024 * 128]; buffer[0] = '\0'; base_classification_t *bc; word_group_t *g; affix_t *a; word_t *w; size_t n_el, i; for (n_el = 0, bc = domain->base_classifications; bc; bc = bc->next) { n_el++; } char **sortable = calloc(n_el, sizeof(char *)); if (!sortable) { log_error("allocation error %s", strerror(errno)); return -1; } for (i=0, bc = domain->base_classifications; bc; bc = bc->next) { sortable[i++] = bc->trans; } qsort(sortable, n_el, sizeof(char *), string_size); for (i = 0; i < n_el; i++) { strcat(buffer, sortable[i]); if (i < n_el) strcat(buffer,"|"); } free(sortable); log_debug(">>> %s classification regexp=%s\n", domain->name, buffer); build_regexp(&domain->base_classification_regexp, buffer); for (g = domain->groups; g; g = g->next) { if (g->prefixes) { strcat(buffer,"(?:"); for (a = g->prefixes; a; a = a->next) { strcat(buffer, a->text); if (a->next) strcat(buffer,"|"); } strcat(buffer,")"); strcat(buffer,"[ ]+"); log_debug(">>> %s %s prefix regexp=%s\n", domain->name, g->name, buffer); build_regexp(&g->prefix_regexp, buffer); } if (g->prefixes) strcat(buffer, "^"); strcat(buffer, "(?:"); g->sword_len=0; for (w = g->words; w; w = w->next) g->sword_len++; g->sword = calloc(g->sword_len, sizeof(word_t *)); if (!g->sword) { log_error("allocation error %s", strerror(errno)); return -1; } i=0; for (w = g->words; w; w = w->next) g->sword[i++]=w; qsort(g->sword, g->sword_len, sizeof(word_t *), word_size); for (i=0; i < g->sword_len; i++) { if (i) strcat(buffer,"|"); strcat(buffer,"\\b"); strcat(buffer, g->sword[i]->text); strcat(buffer,"\\b"); } if (g->whitespace) { strcat(buffer,"|["); strcat(buffer, g->whitespace); strcat(buffer, "]+"); } strcat(buffer, ")+"); if (g->suffixes) strcat(buffer, "$"); log_debug(">>> %s %s modifier regexp=%s\n", domain->name, g->name, buffer); build_regexp(&g->word_regexp, buffer); if (g->suffixes) { strcat(buffer,"[ ]+"); strcat(buffer,"(?:"); for (a = g->suffixes; a; a = a->next) { strcat(buffer, a->text); if (a->next) strcat(buffer,"|"); } strcat(buffer,")"); log_debug(">>> %s %s suffix regexp=%s\n", domain->name, g->name, buffer); build_regexp(&g->suffix_regexp, buffer); } } return 0; } char * compute_raw_from_trans(const char *level, domain_t *domain) { #ifdef DEBUG struct timeval startTime; gettimeofday(&startTime, 0); #endif int rc = 0; int ovector[OVECCOUNT]; word_group_t *g = NULL; char *work = NULL; char *r = NULL; const char * match = NULL; int work_len; mls_level_t *mraw = NULL; ebitmap_t set, clear, tmp; ebitmap_init(&set); ebitmap_init(&clear); ebitmap_init(&tmp); work = strdup(level); if (!work) { log_error("compute_raw_from_trans: allocation error %s", strerror(errno)); goto err; } work_len = strlen(work); if (!domain->base_classification_regexp) if (build_regexps(domain) < 0) goto err; if (!domain->base_classification_regexp) goto err; log_debug(" compute_raw_from_trans work = %s\n", work); rc = pcre_exec(domain->base_classification_regexp, 0, work, work_len, 0, PCRE_ANCHORED, ovector, OVECCOUNT); if (rc > 0) { match = NULL; pcre_get_substring(work, ovector, rc, 0, &match); log_debug(" compute_raw_from_trans match = %s len = %u\n", match, strlen(match)); base_classification_t *bc; for (bc = domain->base_classifications; bc; bc = bc->next) { if (!strcmp(bc->trans, match)) { log_debug(" compute_raw_from_trans base classification %s matched %s\n", level, bc->trans); mraw = malloc(sizeof(mls_level_t)); if (!mraw) { log_error("allocation error %s", strerror(errno)); goto err; } if (mls_level_cpy(mraw, bc->level) < 0) goto err; break; } } memset(work + ovector[0], '#', ovector[1] - ovector[0]); char *p=work + ovector[0] + ovector[1]; while (*p && (strchr(" ", *p) != NULL)) *p++ = '#'; pcre_free((char *)match); match = NULL; } else { log_debug(" compute_raw_from_trans no base classification matched %s\n", level); } if (mraw == NULL) { goto err; } int complete = 0; int change = 1; while(change && !complete) { change = 0; for (g = domain->groups; g && !change && !complete; g = g->next) { int prefix = 0, suffix = 0; int prefix_offset = 0, prefix_len = 0; int suffix_offset = 0, suffix_len = 0; if (g->prefix_regexp) { rc = pcre_exec(g->prefix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT); if (rc > 0) { prefix = 1; prefix_offset = ovector[0]; prefix_len = ovector[1] - ovector[0]; } } if (g->suffix_regexp) { rc = pcre_exec(g->suffix_regexp, 0, work, work_len, 0, 0, ovector, OVECCOUNT); if (rc > 0) { suffix = 1; suffix_offset = ovector[0]; suffix_len = ovector[1] - ovector[0]; } } /* anchors prefix ^, suffix $ */ if (((!g->prefixes && !g->suffixes) || (g->prefixes && prefix) || (g->suffixes && suffix)) && g->word_regexp) { char *s = work + prefix_offset + prefix_len; int l = (suffix_len ? suffix_offset : work_len) - prefix_len - prefix_offset; rc = pcre_exec(g->word_regexp, 0, s, l, 0, 0, ovector, OVECCOUNT); if (rc > 0) { match = NULL; pcre_get_substring(s, ovector, rc, 0, &match); trim((char *)match, g->whitespace); if (*match) { char *p = triml((char *)match, g->whitespace); while (p && *p) { int plen = strlen(p); unsigned int i; for (i = 0; i < g->sword_len; i++) { word_t *w = g->sword[i]; int wlen = strlen(w->text); if (plen >= wlen && !strncmp(w->text, p, strlen(w->text))){ if (ebitmap_andnot(&set, &w->cat, &g->def, maxbit) < 0) goto err; if (ebitmap_xor(&tmp, &w->cat, &g->def) < 0) goto err; if (ebitmap_and(&clear, &tmp, &g->def) < 0) goto err; if (ebitmap_union(&mraw->cat, &set) < 0) goto err; ebitmap_destroy(&tmp); if (ebitmap_cpy(&tmp, &mraw->cat) < 0) goto err; ebitmap_destroy(&mraw->cat); if (ebitmap_andnot(&mraw->cat, &tmp, &clear, maxbit) < 0) goto err; ebitmap_destroy(&tmp); ebitmap_destroy(&set); ebitmap_destroy(&clear); p += strlen(w->text); change++; break; } } if (i == g->sword_len) { syslog(LOG_ERR, "conversion error"); break; } p = triml(p, g->whitespace); } memset(work + prefix_offset, '#', prefix_len); memset(work + suffix_offset, '#', suffix_len); memset(s + ovector[0], '#', ovector[1] - ovector[0]); } pcre_free((void *)match); match = NULL; } } /* YYY */ complete=1; char *p = work; while(*p) { if (isalnum(*p++)) { complete=0; break; } } } } free(work); if (violates_constraints(mraw)) { complete = 0; } if (complete) r = mls_level_to_string(mraw); mls_level_destroy(mraw); free(mraw); #ifdef DEBUG struct timeval stopTime; gettimeofday(&stopTime, 0); long int ms; if (startTime.tv_usec > stopTime.tv_usec) ms = (stopTime.tv_sec - startTime.tv_sec - 1) * 1000 + (stopTime.tv_usec/1000 + 1000 - startTime.tv_usec/1000); else ms = (stopTime.tv_sec - startTime.tv_sec ) * 1000 + (stopTime.tv_usec/1000 - startTime.tv_usec/1000); log_debug(" compute_raw_from_trans in %ld ms'\n", ms); #endif return r; err: mls_level_destroy(mraw); free(mraw); free(work); pcre_free((void *)match); ebitmap_destroy(&tmp); ebitmap_destroy(&set); ebitmap_destroy(&clear); return NULL; } char * compute_trans_from_raw(const char *level, domain_t *domain) { #ifdef DEBUG struct timeval startTime; gettimeofday(&startTime, 0); #endif word_group_t *g; mls_level_t *l = NULL; char *rval = NULL; word_group_t *groups = NULL; ebitmap_t bit_diff, temp, handled, nothandled, unhandled, orig_unhandled; ebitmap_init(&bit_diff); ebitmap_init(&temp); ebitmap_init(&handled); ebitmap_init(¬handled); ebitmap_init(&unhandled); ebitmap_init(&orig_unhandled); if (!level) goto err; l = parse_raw(level); if (!l) goto err; log_debug(" compute_trans_from_raw raw = %s\n", level); /* YYY */ /* check constraints */ if (violates_constraints(l)) { syslog(LOG_ERR, "%s violates constraints", level); goto err; } int doInverse = l->sens > 0; base_classification_t *bc, *last = NULL; int done = 0; for (bc = domain->base_classifications; bc && !done; bc = bc->next) { if (l->sens == bc->level->sens) { /* skip if alias of last bc */ if (last && last->level->sens == bc->level->sens && ebitmap_cmp(&last->level->cat, &bc->level->cat) == 0) continue; /* compute bits not consumed by base classification */ if (ebitmap_xor(&unhandled, &l->cat, &bc->level->cat) < 0) goto err; if (ebitmap_cpy(&orig_unhandled, &unhandled) < 0) goto err; /* prebuild groups */ for (g = domain->groups; g; g = g->next) { word_group_t **t; for (t = &groups; *t; t = &(*t)->next) if (!strcmp(g->name, (*t)->name)) break; if (! *t) { word_group_t *wg = create_group(&groups, g->name); if (g->prefixes) if (append(&wg->prefixes, g->prefixes->text) < 0) goto err; if (g->suffixes) if (append(&wg->suffixes, g->suffixes->text) < 0) goto err; if (g->join) if (update(&wg->join, g->join) < 0) goto err; } } int loops, hamming, change=1; for (loops = 50; ebitmap_cardinality(&unhandled) && loops > 0 && change; loops--) { change = 0; hamming = 10000; if (ebitmap_xor(&handled, &unhandled, &orig_unhandled) < 0) goto err; if (ebitmap_not(¬handled, &handled, maxbit) < 0) goto err; word_group_t *currentGroup = NULL; word_t *currentWord = NULL; for (g = domain->groups; g && hamming; g = g->next) { word_t *w; for (w = g->words; w && hamming; w = w->next) { int cardinality = ebitmap_cardinality(&w->normal); /* If the word is all inverse bits and the level does not have inverse bits - skip */ if (cardinality && !doInverse) { continue; } /* if only unhandled bits are different */ if (ebitmap_or(&temp, &w->normal, &w->inverse) < 0) goto err; if (ebitmap_and(&bit_diff, &temp, ¬handled) < 0) goto err; ebitmap_destroy(&temp); // xor bit_diff handled? if (ebitmap_and(&temp, &bit_diff, &unhandled) < 0) goto err; if (ebitmap_cmp(&bit_diff, &temp)) { int h = ebitmap_hamming_distance(&bit_diff, &unhandled); if (h < hamming) { hamming = h; currentGroup = g; currentWord = w; } } ebitmap_destroy(&bit_diff); ebitmap_destroy(&temp); } } ebitmap_destroy(&handled); ebitmap_destroy(¬handled); if (currentWord) { if (ebitmap_xor(&bit_diff, ¤tWord->cat, &bc->level->cat) < 0) goto err; if (ebitmap_cpy(&temp, &unhandled) < 0) goto err; ebitmap_destroy(&unhandled); if (ebitmap_andnot(&unhandled, &temp, &bit_diff, maxbit) < 0) goto err; ebitmap_destroy(&bit_diff); ebitmap_destroy(&temp); word_group_t **t; for (t = &groups; *t; t = &(*t)->next) if (!strcmp(currentGroup->name, (*t)->name)) break; create_word(&(*t)->words, currentWord->text); change++; } } done = (ebitmap_cardinality(&unhandled) == 0); ebitmap_destroy(&unhandled); ebitmap_destroy(&orig_unhandled); if (done) { char buffer[9999]; buffer[0] = 0; strcat(buffer, bc->trans); strcat(buffer, " "); for (g=groups; g; g = g->next) { if (g->words && g->prefixes) { strcat(buffer, g->prefixes->text); strcat(buffer, " "); } word_t *w; for (w=g->words; w; w = w->next) { strcat(buffer, w->text); if (w->next) strcat(buffer, g->join); } if (g->words && g->suffixes) { strcat(buffer, " "); strcat(buffer, g->suffixes->text); } word_group_t *n = g->next; while(g->words && n) { if (n->words) { strcat(buffer, " "); break; } n = n->next; } } rval = strdup(buffer); if (!rval) { log_error("compute_trans_from_raw: allocation error %s", strerror(errno)); goto err; } } /* clean up */ while (groups) destroy_group(&groups, groups); } last = bc; } if (l) { mls_level_destroy(l); free(l); } #ifdef DEBUG struct timeval stopTime; gettimeofday(&stopTime, 0); long int ms; if (startTime.tv_usec > stopTime.tv_usec) ms = (stopTime.tv_sec - startTime.tv_sec - 1) * 1000 + (stopTime.tv_usec/1000 + 1000 - startTime.tv_usec/1000); else ms = (stopTime.tv_sec - startTime.tv_sec ) * 1000 + (stopTime.tv_usec/1000 - startTime.tv_usec/1000); log_debug(" compute_trans_from_raw in %ld ms'\n", ms); #endif return rval; err: while (groups) destroy_group(&groups, groups); mls_level_destroy(l); free(l); return NULL; } int trans_context(const security_context_t incon, security_context_t *rcon) { char *trans = NULL; *rcon = NULL; #ifdef DEBUG struct timeval startTime; gettimeofday(&startTime, 0); #endif log_debug(" trans_context input = %s\n", incon); char *range = extract_range(incon); if (!range) return -1; domain_t *domain = domains; for (;domain; domain = domain->next) { trans = find_in_hashtable(range, domain, domain->raw_to_trans); if (trans) break; /* try split and translate */ char *lrange = NULL, *urange = NULL; char *ltrans = NULL, *utrans = NULL; char *dashp = strchr(range,'-'); if (dashp) { *dashp = 0; lrange = range; urange = dashp+1; } else { trans = compute_trans_from_raw(range, domain); if (trans) if (add_cache(domain, range, trans) < 0) { free(range); return -1; } } if (lrange && urange) { ltrans = find_in_hashtable(lrange, domain, domain->raw_to_trans); if (! ltrans) { ltrans = compute_trans_from_raw(lrange, domain); if (ltrans) { if (add_cache(domain, lrange, ltrans) < 0) { free(range); return -1; } } else { ltrans = strdup(lrange); if (! ltrans) { log_error("strdup failed %s", strerror(errno)); free(range); return -1; } } } utrans = find_in_hashtable(urange, domain, domain->raw_to_trans); if (! utrans) { utrans = compute_trans_from_raw(urange, domain); if (utrans) { if (add_cache(domain, urange, utrans) < 0) { free(ltrans); free(range); return -1; } } else { utrans = strdup(urange); if (! utrans) { log_error("strdup failed %s", strerror(errno)); free(ltrans); free(range); return -1; } } } if (strcmp(ltrans, utrans) == 0) { if (asprintf(&trans, "%s", ltrans) < 0) { log_error("asprintf failed %s", strerror(errno)); free(utrans); free(ltrans); free(range); return -1; } } else { if (asprintf(&trans, "%s-%s", ltrans, utrans) < 0) { log_error("asprintf failed %s", strerror(errno)); free(utrans); free(ltrans); free(range); return -1; } } free(ltrans); free(utrans); *dashp = '-'; break; } if (dashp) *dashp = '-'; } if (trans) { *rcon = new_context_str(incon, trans); free(trans); } else { *rcon = new_context_str(incon, range); } free(range); #ifdef DEBUG struct timeval stopTime; gettimeofday(&stopTime, 0); long int ms; if (startTime.tv_usec > stopTime.tv_usec) ms = (stopTime.tv_sec - startTime.tv_sec - 1) * 1000 + (stopTime.tv_usec/1000 + 1000 - startTime.tv_usec/1000); else ms = (stopTime.tv_sec - startTime.tv_sec ) * 1000 + (stopTime.tv_usec/1000 - startTime.tv_usec/1000); log_debug(" trans_context input='%s' output='%s in %ld ms'\n", incon, *rcon, ms); #endif return 0; } int untrans_context(const security_context_t incon, security_context_t *rcon) { char *raw = NULL; *rcon = NULL; #ifdef DEBUG struct timeval startTime; gettimeofday(&startTime, 0); #endif log_debug(" untrans_context incon = %s\n", incon); char *range = extract_range(incon); if (!range) return -1; log_debug(" untrans_context range = %s\n", range); domain_t *domain = domains; for (;domain; domain = domain->next) { raw = find_in_hashtable(range, domain, domain->trans_to_raw); if (raw) break; /* try split and translate */ char *lrange = NULL, *urange = NULL; char *lraw = NULL, *uraw = NULL; char *dashp = strchr(range,'-'); if (dashp) { *dashp = 0; lrange = range; urange = dashp+1; } else { raw = compute_raw_from_trans(range, domain); if (raw) { char *canonical = find_in_hashtable(raw, domain, domain->raw_to_trans); if (!canonical) { canonical = compute_trans_from_raw(raw, domain); if (canonical && strcmp(canonical, range)) if (add_cache(domain, raw, canonical) < 0) { free(range); return -1; } } if (canonical) free(canonical); if (add_cache(domain, raw, range) < 0) { free(range); return -1; } } else { log_debug("untrans_context unable to compute raw context %s\n", range); } } if (lrange && urange) { lraw = find_in_hashtable(lrange, domain, domain->trans_to_raw); if (! lraw) { lraw = compute_raw_from_trans(lrange, domain); if (lraw) { char *canonical = find_in_hashtable(lraw, domain, domain->raw_to_trans); if (!canonical) { canonical = compute_trans_from_raw(lraw, domain); if (canonical) if (add_cache(domain, lraw, canonical) < 0) { free(lraw); free(range); return -1; } } if (canonical) free(canonical); if (add_cache(domain, lraw, lrange) < 0) { free(lraw); free(range); return -1; } } else { lraw = strdup(lrange); if (! lraw) { log_error("strdup failed %s", strerror(errno)); free(range); return -1; } } } uraw = find_in_hashtable(urange, domain, domain->trans_to_raw); if (! uraw) { uraw = compute_raw_from_trans(urange, domain); if (uraw) { char *canonical = find_in_hashtable(uraw, domain, domain->raw_to_trans); if (!canonical) { canonical = compute_trans_from_raw(uraw, domain); if (canonical) if (add_cache(domain, uraw, canonical) < 0) { free(uraw); free(lraw); free(range); return -1; } } if (canonical) free(canonical); if (add_cache(domain, uraw, urange) < 0) { free(uraw); free(lraw); free(range); return -1; } } else { uraw = strdup(urange); if (! uraw) { log_error("strdup failed %s", strerror(errno)); free(lraw); free(range); return -1; } } } if (strcmp(lraw, uraw) == 0) { if (asprintf(&raw, "%s", lraw) < 0) { log_error("asprintf failed %s", strerror(errno)); free(uraw); free(lraw); free(range); return -1; } } else { if (asprintf(&raw, "%s-%s", lraw, uraw) < 0) { log_error("asprintf failed %s", strerror(errno)); free(uraw); free(lraw); free(range); return -1; } } free(lraw); free(uraw); *dashp = '-'; break; } if (dashp) *dashp = '-'; } if (raw) { *rcon = new_context_str(incon, raw); free(raw); } else { *rcon = new_context_str(incon, range); } free(range); #ifdef DEBUG struct timeval stopTime; gettimeofday(&stopTime, 0); long int ms; if (startTime.tv_usec > stopTime.tv_usec) ms = (stopTime.tv_sec - startTime.tv_sec - 1) * 1000 + (stopTime.tv_usec/1000 + 1000 - startTime.tv_usec/1000); else ms = (stopTime.tv_sec - startTime.tv_sec ) * 1000 + (stopTime.tv_usec/1000 - startTime.tv_usec/1000); log_debug(" untrans_context input='%s' output='%s' n %ld ms\n", incon, *rcon, ms); #endif return 0; } void finish_context_translations(void) { while(domains) { domain_t *next = domains->next; destroy_domain(domains); domains = next; } while(sens_constraints) { sens_constraint_t *next = sens_constraints->next; destroy_sens_constraint(&sens_constraints, sens_constraints); sens_constraints = next; } while(cat_constraints) { cat_constraint_t *next = cat_constraints->next; destroy_cat_constraint(&cat_constraints, cat_constraints); cat_constraints = next; } }