/* * Media contexts backend for X contexts * * Author : Eamon Walsh <ewalsh@tycho.nsa.gov> */ #include <sys/stat.h> #include <string.h> #include <stdio.h> #include <stdio_ext.h> #include <ctype.h> #include <errno.h> #include <limits.h> #include <fnmatch.h> #include "callbacks.h" #include "label_internal.h" /* * Internals */ /* A context specification. */ typedef struct spec { struct selabel_lookup_rec lr; /* holds contexts for lookup result */ char *key; /* key string */ int type; /* type of record (prop, ext, client) */ int matches; /* number of matches made during operation */ } spec_t; struct saved_data { unsigned int nspec; spec_t *spec_arr; }; static int process_line(const char *path, char *line_buf, int pass, unsigned lineno, struct selabel_handle *rec) { struct saved_data *data = (struct saved_data *)rec->data; int items; char *buf_p; char *type, *key, *context; buf_p = line_buf; while (isspace(*buf_p)) buf_p++; /* Skip comment lines and empty lines. */ if (*buf_p == '#' || *buf_p == 0) return 0; items = sscanf(line_buf, "%ms %ms %ms ", &type, &key, &context); if (items < 3) { selinux_log(SELINUX_WARNING, "%s: line %u is missing fields, skipping\n", path, lineno); if (items > 0) free(type); if (items > 1) free(key); return 0; } if (pass == 1) { /* Convert the type string to a mode format */ if (!strcmp(type, "property")) data->spec_arr[data->nspec].type = SELABEL_X_PROP; else if (!strcmp(type, "extension")) data->spec_arr[data->nspec].type = SELABEL_X_EXT; else if (!strcmp(type, "client")) data->spec_arr[data->nspec].type = SELABEL_X_CLIENT; else if (!strcmp(type, "event")) data->spec_arr[data->nspec].type = SELABEL_X_EVENT; else if (!strcmp(type, "selection")) data->spec_arr[data->nspec].type = SELABEL_X_SELN; else if (!strcmp(type, "poly_property")) data->spec_arr[data->nspec].type = SELABEL_X_POLYPROP; else if (!strcmp(type, "poly_selection")) data->spec_arr[data->nspec].type = SELABEL_X_POLYSELN; else { selinux_log(SELINUX_WARNING, "%s: line %u has invalid object type %s\n", path, lineno, type); return 0; } data->spec_arr[data->nspec].key = key; data->spec_arr[data->nspec].lr.ctx_raw = context; free(type); } data->nspec++; if (pass == 0) { free(type); free(key); free(context); } return 0; } static int init(struct selabel_handle *rec, struct selinux_opt *opts, unsigned n) { FILE *fp; struct saved_data *data = (struct saved_data *)rec->data; const char *path = NULL; char *line_buf = NULL; size_t line_len = 0; int status = -1; unsigned int lineno, pass, maxnspec; struct stat sb; /* Process arguments */ while (n--) switch(opts[n].type) { case SELABEL_OPT_PATH: path = opts[n].value; break; } /* Open the specification file. */ if (!path) path = selinux_x_context_path(); if ((fp = fopen(path, "r")) == NULL) return -1; __fsetlocking(fp, FSETLOCKING_BYCALLER); if (fstat(fileno(fp), &sb) < 0) return -1; if (!S_ISREG(sb.st_mode)) { errno = EINVAL; return -1; } rec->spec_file = strdup(path); /* * Perform two passes over the specification file. * The first pass counts the number of specifications and * performs simple validation of the input. At the end * of the first pass, the spec array is allocated. * The second pass performs detailed validation of the input * and fills in the spec array. */ maxnspec = UINT_MAX / sizeof(spec_t); for (pass = 0; pass < 2; pass++) { lineno = 0; data->nspec = 0; while (getline(&line_buf, &line_len, fp) > 0 && data->nspec < maxnspec) { if (process_line(path, line_buf, pass, ++lineno, rec)) goto finish; } lineno = 0; if (pass == 0) { if (data->nspec == 0) { status = 0; goto finish; } data->spec_arr = malloc(sizeof(spec_t)*data->nspec); if (data->spec_arr == NULL) goto finish; memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec); maxnspec = data->nspec; rewind(fp); } } free(line_buf); status = 0; finish: fclose(fp); return status; } /* * Backend interface routines */ static void close(struct selabel_handle *rec) { struct saved_data *data = (struct saved_data *)rec->data; struct spec *spec, *spec_arr = data->spec_arr; unsigned int i; for (i = 0; i < data->nspec; i++) { spec = &spec_arr[i]; free(spec->key); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); } if (spec_arr) free(spec_arr); memset(data, 0, sizeof(*data)); } static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, const char *key, int type) { struct saved_data *data = (struct saved_data *)rec->data; spec_t *spec_arr = data->spec_arr; unsigned int i; for (i = 0; i < data->nspec; i++) { if (spec_arr[i].type != type) continue; if (!fnmatch(spec_arr[i].key, key, 0)) break; } if (i >= data->nspec) { /* No matching specification. */ errno = ENOENT; return NULL; } spec_arr[i].matches++; return &spec_arr[i].lr; } static void stats(struct selabel_handle *rec) { struct saved_data *data = (struct saved_data *)rec->data; unsigned int i, total = 0; for (i = 0; i < data->nspec; i++) total += data->spec_arr[i].matches; selinux_log(SELINUX_INFO, "%u entries, %u matches made\n", data->nspec, total); } int selabel_x_init(struct selabel_handle *rec, 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 = &close; rec->func_lookup = &lookup; rec->func_stats = &stats; return init(rec, opts, nopts); }