diff options
Diffstat (limited to 'src/label_file.c')
-rw-r--r-- | src/label_file.c | 859 |
1 files changed, 0 insertions, 859 deletions
diff --git a/src/label_file.c b/src/label_file.c deleted file mode 100644 index d3e67c0..0000000 --- a/src/label_file.c +++ /dev/null @@ -1,859 +0,0 @@ -/* - * File contexts backend for labeling system - * - * Author : Eamon Walsh <ewalsh@tycho.nsa.gov> - * Author : Stephen Smalley <sds@tycho.nsa.gov> - */ - -#include <assert.h> -#include <fcntl.h> -#include <stdarg.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include <limits.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include "callbacks.h" -#include "label_internal.h" -#include "label_file.h" - -/* - * Internals, mostly moved over from matchpathcon.c - */ - -/* return the length of the text that is the stem of a file name */ -static int get_stem_from_file_name(const char *const buf) -{ - const char *tmp = strchr(buf + 1, '/'); - - if (!tmp) - return 0; - return tmp - buf; -} - -/* find the stem of a file name, returns the index into stem_arr (or -1 if - * there is no match - IE for a file in the root directory or a regex that is - * too complex for us). Makes buf point to the text AFTER the stem. */ -static int find_stem_from_file(struct saved_data *data, const char **buf) -{ - int i; - int stem_len = get_stem_from_file_name(*buf); - - if (!stem_len) - return -1; - for (i = 0; i < data->num_stems; i++) { - if (stem_len == data->stem_arr[i].len - && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) { - *buf += stem_len; - return i; - } - } - return -1; -} - -/* - * Warn about duplicate specifications. - */ -static int nodups_specs(struct saved_data *data, const char *path) -{ - int rc = 0; - unsigned int ii, jj; - struct spec *curr_spec, *spec_arr = data->spec_arr; - - for (ii = 0; ii < data->nspec; ii++) { - curr_spec = &spec_arr[ii]; - for (jj = ii + 1; jj < data->nspec; jj++) { - if ((!strcmp(spec_arr[jj].regex_str, - curr_spec->regex_str)) - && (!spec_arr[jj].mode || !curr_spec->mode - || spec_arr[jj].mode == curr_spec->mode)) { - rc = -1; - errno = EINVAL; - if (strcmp(spec_arr[jj].lr.ctx_raw, - curr_spec->lr.ctx_raw)) { - selinux_log - (SELINUX_ERROR, - "%s: Multiple different specifications for %s (%s and %s).\n", - path, curr_spec->regex_str, - spec_arr[jj].lr.ctx_raw, - curr_spec->lr.ctx_raw); - } else { - selinux_log - (SELINUX_ERROR, - "%s: Multiple same specifications for %s.\n", - path, curr_spec->regex_str); - } - } - } - } - return rc; -} - -static int load_mmap(struct selabel_handle *rec, const char *path, - struct stat *sb, bool isbinary) -{ - struct saved_data *data = (struct saved_data *)rec->data; - char mmap_path[PATH_MAX + 1]; - int mmapfd; - int rc; - struct stat mmap_stat; - char *addr, *str_buf; - size_t len; - int *stem_map; - struct mmap_area *mmap_area; - uint32_t i, magic, version; - uint32_t entry_len, stem_map_len, regex_array_len; - - if (isbinary) { - len = strlen(path); - if (len >= sizeof(mmap_path)) - return -1; - strcpy(mmap_path, path); - } else { - rc = snprintf(mmap_path, sizeof(mmap_path), "%s.bin", path); - if (rc >= (int)sizeof(mmap_path)) - return -1; - } - - mmapfd = open(mmap_path, O_RDONLY | O_CLOEXEC); - if (mmapfd < 0) - return -1; - - rc = fstat(mmapfd, &mmap_stat); - if (rc < 0) { - close(mmapfd); - return -1; - } - - /* if mmap is old, ignore it */ - if (mmap_stat.st_mtime < sb->st_mtime) { - close(mmapfd); - return -1; - } - - /* ok, read it in... */ - len = mmap_stat.st_size; - len += (sysconf(_SC_PAGE_SIZE) - 1); - len &= ~(sysconf(_SC_PAGE_SIZE) - 1); - - mmap_area = malloc(sizeof(*mmap_area)); - if (!mmap_area) { - close(mmapfd); - return -1; - } - - addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, mmapfd, 0); - close(mmapfd); - if (addr == MAP_FAILED) { - free(mmap_area); - perror("mmap"); - return -1; - } - - /* save where we mmap'd the file to cleanup on close() */ - mmap_area->addr = mmap_area->next_addr = addr; - mmap_area->len = mmap_area->next_len = len; - mmap_area->next = data->mmap_areas; - data->mmap_areas = mmap_area; - - /* check if this looks like an fcontext file */ - rc = next_entry(&magic, mmap_area, sizeof(uint32_t)); - if (rc < 0 || magic != SELINUX_MAGIC_COMPILED_FCONTEXT) - return -1; - - /* check if this version is higher than we understand */ - rc = next_entry(&version, mmap_area, sizeof(uint32_t)); - if (rc < 0 || version > SELINUX_COMPILED_FCONTEXT_MAX_VERS) - return -1; - - if (version >= SELINUX_COMPILED_FCONTEXT_PCRE_VERS) { - if (!regex_version()) { - return -1; - } - len = strlen(regex_version()); - - rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); - if (rc < 0) - return -1; - - /* Check version lengths */ - if (len != entry_len) - return -1; - - /* Check if pcre version mismatch */ - str_buf = malloc(entry_len + 1); - if (!str_buf) - return -1; - - rc = next_entry(str_buf, mmap_area, entry_len); - if (rc < 0) { - free(str_buf); - return -1; - } - - str_buf[entry_len] = '\0'; - if ((strcmp(str_buf, regex_version()) != 0)) { - free(str_buf); - return -1; - } - free(str_buf); - } - - /* allocate the stems_data array */ - rc = next_entry(&stem_map_len, mmap_area, sizeof(uint32_t)); - if (rc < 0 || !stem_map_len) - return -1; - - /* - * map indexed by the stem # in the mmap file and contains the stem - * number in the data stem_arr - */ - stem_map = calloc(stem_map_len, sizeof(*stem_map)); - if (!stem_map) - return -1; - - for (i = 0; i < stem_map_len; i++) { - char *buf; - uint32_t stem_len; - int newid; - - /* the length does not inlude the nul */ - rc = next_entry(&stem_len, mmap_area, sizeof(uint32_t)); - if (rc < 0 || !stem_len) { - rc = -1; - goto err; - } - - /* Check for stem_len wrap around. */ - if (stem_len < UINT32_MAX) { - buf = (char *)mmap_area->next_addr; - /* Check if over-run before null check. */ - rc = next_entry(NULL, mmap_area, (stem_len + 1)); - if (rc < 0) - goto err; - - if (buf[stem_len] != '\0') { - rc = -1; - goto err; - } - } else { - rc = -1; - goto err; - } - - /* store the mapping between old and new */ - newid = find_stem(data, buf, stem_len); - if (newid < 0) { - newid = store_stem(data, buf, stem_len); - if (newid < 0) { - rc = newid; - goto err; - } - data->stem_arr[newid].from_mmap = 1; - } - stem_map[i] = newid; - } - - /* allocate the regex array */ - rc = next_entry(®ex_array_len, mmap_area, sizeof(uint32_t)); - if (rc < 0 || !regex_array_len) { - rc = -1; - goto err; - } - - for (i = 0; i < regex_array_len; i++) { - struct spec *spec; - int32_t stem_id, meta_chars; - uint32_t mode = 0, prefix_len = 0; - - rc = grow_specs(data); - if (rc < 0) - goto err; - - spec = &data->spec_arr[data->nspec]; - spec->from_mmap = 1; - spec->regcomp = 1; - - /* Process context */ - rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); - if (rc < 0 || !entry_len) { - rc = -1; - goto err; - } - - str_buf = malloc(entry_len); - if (!str_buf) { - rc = -1; - goto err; - } - rc = next_entry(str_buf, mmap_area, entry_len); - if (rc < 0) - goto err; - - if (str_buf[entry_len - 1] != '\0') { - free(str_buf); - rc = -1; - goto err; - } - spec->lr.ctx_raw = str_buf; - - if (strcmp(spec->lr.ctx_raw, "<<none>>") && rec->validating) { - if (selabel_validate(rec, &spec->lr) < 0) { - selinux_log(SELINUX_ERROR, - "%s: context %s is invalid\n", mmap_path, spec->lr.ctx_raw); - goto err; - } - } - - /* Process regex string */ - rc = next_entry(&entry_len, mmap_area, sizeof(uint32_t)); - if (rc < 0 || !entry_len) { - rc = -1; - goto err; - } - - spec->regex_str = (char *)mmap_area->next_addr; - rc = next_entry(NULL, mmap_area, entry_len); - if (rc < 0) - goto err; - - if (spec->regex_str[entry_len - 1] != '\0') { - rc = -1; - goto err; - } - - /* Process mode */ - if (version >= SELINUX_COMPILED_FCONTEXT_MODE) - rc = next_entry(&mode, mmap_area, sizeof(uint32_t)); - else - rc = next_entry(&mode, mmap_area, sizeof(mode_t)); - if (rc < 0) - goto err; - - spec->mode = mode; - - /* map the stem id from the mmap file to the data->stem_arr */ - rc = next_entry(&stem_id, mmap_area, sizeof(int32_t)); - if (rc < 0) - goto err; - - if (stem_id < 0 || stem_id >= (int32_t)stem_map_len) - spec->stem_id = -1; - else - spec->stem_id = stem_map[stem_id]; - - /* retrieve the hasMetaChars bit */ - rc = next_entry(&meta_chars, mmap_area, sizeof(uint32_t)); - if (rc < 0) - goto err; - - spec->hasMetaChars = meta_chars; - /* and prefix length for use by selabel_lookup_best_match */ - if (version >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN) { - rc = next_entry(&prefix_len, mmap_area, - sizeof(uint32_t)); - if (rc < 0) - goto err; - - spec->prefix_len = prefix_len; - } - - rc = regex_load_mmap(mmap_area, &spec->regex); - if (rc < 0) - goto err; -#ifndef NO_PERSISTENTLY_STORED_PATTERNS - spec->regcomp = 1; -#else - spec->regcomp = 0; -#endif - - data->nspec++; - } - - /* win */ - rc = 0; -err: - free(stem_map); - - return rc; -} - -static int process_file(const char *path, const char *suffix, - struct selabel_handle *rec, const char *prefix) -{ - FILE *fp; - struct stat sb; - unsigned int lineno; - size_t line_len = 0; - char *line_buf = NULL; - int rc; - char stack_path[PATH_MAX + 1]; - bool isbinary = false; - uint32_t magic; - - /* append the path suffix if we have one */ - if (suffix) { - rc = snprintf(stack_path, sizeof(stack_path), - "%s.%s", path, suffix); - if (rc >= (int)sizeof(stack_path)) { - errno = ENAMETOOLONG; - return -1; - } - path = stack_path; - } - - /* Open the specification file. */ - fp = fopen(path, "r"); - if (fp) { - if (fstat(fileno(fp), &sb) < 0) - return -1; - if (!S_ISREG(sb.st_mode)) { - errno = EINVAL; - return -1; - } - - if (fread(&magic, sizeof magic, 1, fp) != 1) { - errno = EINVAL; - fclose(fp); - return -1; - } - - if (magic == SELINUX_MAGIC_COMPILED_FCONTEXT) { - /* file_contexts.bin format */ - fclose(fp); - fp = NULL; - isbinary = true; - } else { - rewind(fp); - } - } else { - /* - * Text file does not exist, so clear the timestamp - * so that we will always pass the timestamp comparison - * with the bin file in load_mmap(). - */ - sb.st_mtime = 0; - } - - rc = load_mmap(rec, path, &sb, isbinary); - if (rc == 0) - goto out; - - if (!fp) - return -1; /* no text or bin file */ - - /* - * Then do detailed validation of the input and fill the spec array - */ - lineno = 0; - rc = 0; - while (getline(&line_buf, &line_len, fp) > 0) { - rc = process_line(rec, path, prefix, line_buf, ++lineno); - if (rc) - goto out; - } - -out: - free(line_buf); - if (fp) - fclose(fp); - return rc; -} - -static void closef(struct selabel_handle *rec); - -static int init(struct selabel_handle *rec, const struct selinux_opt *opts, - unsigned n) -{ - struct saved_data *data = (struct saved_data *)rec->data; - const char *path = NULL; - const char *prefix = NULL; - int status = -1, baseonly = 0; - - /* Process arguments */ - while (n--) - switch(opts[n].type) { - case SELABEL_OPT_PATH: - path = opts[n].value; - break; - case SELABEL_OPT_SUBSET: - prefix = opts[n].value; - break; - case SELABEL_OPT_BASEONLY: - baseonly = !!opts[n].value; - break; - } - - rec->spec_file = strdup(path); - - /* - * The do detailed validation of the input and fill the spec array - */ - status = process_file(path, NULL, rec, prefix); - if (status) - goto finish; - - if (rec->validating) { - status = nodups_specs(data, path); - if (status) - goto finish; - } - - if (!baseonly) { - status = process_file(path, "homedirs", rec, prefix); - if (status && errno != ENOENT) - goto finish; - - status = process_file(path, "local", rec, prefix); - if (status && errno != ENOENT) - goto finish; - } - - status = sort_specs(data); - -finish: - if (status) - closef(rec); - - return status; -} - -/* - * Backend interface routines - */ -static void closef(struct selabel_handle *rec) -{ - struct saved_data *data = (struct saved_data *)rec->data; - struct mmap_area *area, *last_area; - struct spec *spec; - struct stem *stem; - unsigned int i; - - for (i = 0; i < data->nspec; i++) { - spec = &data->spec_arr[i]; - free(spec->lr.ctx_trans); - free(spec->lr.ctx_raw); - if (spec->from_mmap) - continue; - free(spec->regex_str); - free(spec->type_str); - regex_data_free(spec->regex); - } - - for (i = 0; i < (unsigned int)data->num_stems; i++) { - stem = &data->stem_arr[i]; - if (stem->from_mmap) - continue; - free(stem->buf); - } - - if (data->spec_arr) - free(data->spec_arr); - if (data->stem_arr) - free(data->stem_arr); - - area = data->mmap_areas; - while (area) { - munmap(area->addr, area->len); - last_area = area; - area = area->next; - free(last_area); - } - free(data); -} - -static struct spec *lookup_common(struct selabel_handle *rec, - const char *key, - int type, - bool partial) -{ - struct saved_data *data = (struct saved_data *)rec->data; - struct spec *spec_arr = data->spec_arr; - int i, rc, file_stem; - mode_t mode = (mode_t)type; - const char *buf; - struct spec *ret = NULL; - char *clean_key = NULL; - const char *prev_slash, *next_slash; - unsigned int sofar = 0; - struct regex_error_data regex_error_data; - - if (!data->nspec) { - errno = ENOENT; - goto finish; - } - - /* Remove duplicate slashes */ - if ((next_slash = strstr(key, "//"))) { - clean_key = (char *) malloc(strlen(key) + 1); - if (!clean_key) - goto finish; - prev_slash = key; - while (next_slash) { - memcpy(clean_key + sofar, prev_slash, next_slash - prev_slash); - sofar += next_slash - prev_slash; - prev_slash = next_slash + 1; - next_slash = strstr(prev_slash, "//"); - } - strcpy(clean_key + sofar, prev_slash); - key = clean_key; - } - - buf = key; - file_stem = find_stem_from_file(data, &buf); - mode &= S_IFMT; - - /* - * Check for matching specifications in reverse order, so that - * the last matching specification is used. - */ - for (i = data->nspec - 1; i >= 0; i--) { - struct spec *spec = &spec_arr[i]; - /* if the spec in question matches no stem or has the same - * stem as the file AND if the spec in question has no mode - * specified or if the mode matches the file mode then we do - * a regex check */ - if ((spec->stem_id == -1 || spec->stem_id == file_stem) && - (!mode || !spec->mode || mode == spec->mode)) { - if (compile_regex(data, spec, ®ex_error_data) < 0) - goto finish; - if (spec->stem_id == -1) - rc = regex_match(spec->regex, key, partial); - else - rc = regex_match(spec->regex, buf, partial); - if (rc == REGEX_MATCH) { - spec->matches++; - break; - } else if (partial && rc == REGEX_MATCH_PARTIAL) - break; - - if (rc == REGEX_NO_MATCH) - continue; - - errno = ENOENT; - /* else it's an error */ - goto finish; - } - } - - if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { - /* No matching specification. */ - errno = ENOENT; - goto finish; - } - - errno = 0; - ret = &spec_arr[i]; - -finish: - free(clean_key); - return ret; -} - -static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, - const char *key, int type) -{ - struct spec *spec; - - spec = lookup_common(rec, key, type, false); - if (spec) - return &spec->lr; - return NULL; -} - -static bool partial_match(struct selabel_handle *rec, const char *key) -{ - return lookup_common(rec, key, 0, true) ? true : false; -} - -static struct selabel_lookup_rec *lookup_best_match(struct selabel_handle *rec, - const char *key, - const char **aliases, - int type) -{ - size_t n, i; - int best = -1; - struct spec **specs; - size_t prefix_len = 0; - struct selabel_lookup_rec *lr = NULL; - - if (!aliases || !aliases[0]) - return lookup(rec, key, type); - - for (n = 0; aliases[n]; n++) - ; - - specs = calloc(n+1, sizeof(struct spec *)); - if (!specs) - return NULL; - specs[0] = lookup_common(rec, key, type, false); - if (specs[0]) { - if (!specs[0]->hasMetaChars) { - /* exact match on key */ - lr = &specs[0]->lr; - goto out; - } - best = 0; - prefix_len = specs[0]->prefix_len; - } - for (i = 1; i <= n; i++) { - specs[i] = lookup_common(rec, aliases[i-1], type, false); - if (specs[i]) { - if (!specs[i]->hasMetaChars) { - /* exact match on alias */ - lr = &specs[i]->lr; - goto out; - } - if (specs[i]->prefix_len > prefix_len) { - best = i; - prefix_len = specs[i]->prefix_len; - } - } - } - - if (best >= 0) { - /* longest fixed prefix match on key or alias */ - lr = &specs[best]->lr; - } else { - errno = ENOENT; - } - -out: - free(specs); - return lr; -} - -static enum selabel_cmp_result incomp(struct spec *spec1, struct spec *spec2, const char *reason, int i, int j) -{ - selinux_log(SELINUX_INFO, - "selabel_cmp: mismatched %s on entry %d: (%s, %x, %s) vs entry %d: (%s, %x, %s)\n", - reason, - i, spec1->regex_str, spec1->mode, spec1->lr.ctx_raw, - j, spec2->regex_str, spec2->mode, spec2->lr.ctx_raw); - return SELABEL_INCOMPARABLE; -} - -static enum selabel_cmp_result cmp(struct selabel_handle *h1, - struct selabel_handle *h2) -{ - struct saved_data *data1 = (struct saved_data *)h1->data; - struct saved_data *data2 = (struct saved_data *)h2->data; - unsigned int i, nspec1 = data1->nspec, j, nspec2 = data2->nspec; - struct spec *spec_arr1 = data1->spec_arr, *spec_arr2 = data2->spec_arr; - struct stem *stem_arr1 = data1->stem_arr, *stem_arr2 = data2->stem_arr; - bool skipped1 = false, skipped2 = false; - - i = 0; - j = 0; - while (i < nspec1 && j < nspec2) { - struct spec *spec1 = &spec_arr1[i]; - struct spec *spec2 = &spec_arr2[j]; - - /* - * Because sort_specs() moves exact pathnames to the - * end, we might need to skip over additional regex - * entries that only exist in one of the configurations. - */ - if (!spec1->hasMetaChars && spec2->hasMetaChars) { - j++; - skipped2 = true; - continue; - } - - if (spec1->hasMetaChars && !spec2->hasMetaChars) { - i++; - skipped1 = true; - continue; - } - - if (spec1->regcomp && spec2->regcomp) { - if (regex_cmp(spec1->regex, spec2->regex) == SELABEL_INCOMPARABLE){ - return incomp(spec1, spec2, "regex", i, j); - } - } else { - if (strcmp(spec1->regex_str, spec2->regex_str)) - return incomp(spec1, spec2, "regex_str", i, j); - } - - if (spec1->mode != spec2->mode) - return incomp(spec1, spec2, "mode", i, j); - - if (spec1->stem_id == -1 && spec2->stem_id != -1) - return incomp(spec1, spec2, "stem_id", i, j); - if (spec2->stem_id == -1 && spec1->stem_id != -1) - return incomp(spec1, spec2, "stem_id", i, j); - if (spec1->stem_id != -1 && spec2->stem_id != -1) { - struct stem *stem1 = &stem_arr1[spec1->stem_id]; - struct stem *stem2 = &stem_arr2[spec2->stem_id]; - if (stem1->len != stem2->len || - strncmp(stem1->buf, stem2->buf, stem1->len)) - return incomp(spec1, spec2, "stem", i, j); - } - - if (strcmp(spec1->lr.ctx_raw, spec2->lr.ctx_raw)) - return incomp(spec1, spec2, "ctx_raw", i, j); - - i++; - j++; - } - - if ((skipped1 || i < nspec1) && !skipped2) - return SELABEL_SUPERSET; - if ((skipped2 || j < nspec2) && !skipped1) - return SELABEL_SUBSET; - if (skipped1 && skipped2) - return SELABEL_INCOMPARABLE; - return SELABEL_EQUAL; -} - - -static void stats(struct selabel_handle *rec) -{ - struct saved_data *data = (struct saved_data *)rec->data; - unsigned int i, nspec = data->nspec; - struct spec *spec_arr = data->spec_arr; - - for (i = 0; i < nspec; i++) { - if (spec_arr[i].matches == 0) { - if (spec_arr[i].type_str) { - selinux_log(SELINUX_WARNING, - "Warning! No matches for (%s, %s, %s)\n", - spec_arr[i].regex_str, - spec_arr[i].type_str, - spec_arr[i].lr.ctx_raw); - } else { - selinux_log(SELINUX_WARNING, - "Warning! No matches for (%s, %s)\n", - spec_arr[i].regex_str, - spec_arr[i].lr.ctx_raw); - } - } - } -} - -int selabel_file_init(struct selabel_handle *rec, const struct selinux_opt *opts, - unsigned nopts) -{ - struct saved_data *data; - - data = (struct saved_data *)malloc(sizeof(*data)); - if (!data) - return -1; - memset(data, 0, sizeof(*data)); - - rec->data = data; - rec->func_close = &closef; - rec->func_stats = &stats; - rec->func_lookup = &lookup; - rec->func_partial_match = &partial_match; - rec->func_lookup_best_match = &lookup_best_match; - rec->func_cmp = &cmp; - - return init(rec, opts, nopts); -} |