Home | History | Annotate | Download | only in src
      1 /*
      2  * Media contexts backend for X contexts
      3  *
      4  * Author : Eamon Walsh <ewalsh (at) tycho.nsa.gov>
      5  */
      6 
      7 #include <sys/stat.h>
      8 #include <string.h>
      9 #include <stdio.h>
     10 #include <stdio_ext.h>
     11 #include <ctype.h>
     12 #include <errno.h>
     13 #include <limits.h>
     14 #include <fnmatch.h>
     15 #include "callbacks.h"
     16 #include "label_internal.h"
     17 
     18 /*
     19  * Internals
     20  */
     21 
     22 /* A context specification. */
     23 typedef struct spec {
     24 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
     25 	char *key;		/* key string */
     26 	int type;		/* type of record (prop, ext, client) */
     27 	int matches;		/* number of matches made during operation */
     28 } spec_t;
     29 
     30 struct saved_data {
     31 	unsigned int nspec;
     32 	spec_t *spec_arr;
     33 };
     34 
     35 static int process_line(const char *path, char *line_buf, int pass,
     36 			unsigned lineno, struct selabel_handle *rec)
     37 {
     38 	struct saved_data *data = (struct saved_data *)rec->data;
     39 	int items;
     40 	char *buf_p;
     41 	char *type, *key, *context;
     42 
     43 	buf_p = line_buf;
     44 	while (isspace(*buf_p))
     45 		buf_p++;
     46 	/* Skip comment lines and empty lines. */
     47 	if (*buf_p == '#' || *buf_p == 0)
     48 		return 0;
     49 	items = sscanf(line_buf, "%ms %ms %ms ", &type, &key, &context);
     50 	if (items < 3) {
     51 		selinux_log(SELINUX_WARNING,
     52 			    "%s:  line %u is missing fields, skipping\n", path,
     53 			    lineno);
     54 		if (items > 0)
     55 			free(type);
     56 		if (items > 1)
     57 			free(key);
     58 		return 0;
     59 	}
     60 
     61 	if (pass == 1) {
     62 		/* Convert the type string to a mode format */
     63 		if (!strcmp(type, "property"))
     64 			data->spec_arr[data->nspec].type = SELABEL_X_PROP;
     65 		else if (!strcmp(type, "extension"))
     66 			data->spec_arr[data->nspec].type = SELABEL_X_EXT;
     67 		else if (!strcmp(type, "client"))
     68 			data->spec_arr[data->nspec].type = SELABEL_X_CLIENT;
     69 		else if (!strcmp(type, "event"))
     70 			data->spec_arr[data->nspec].type = SELABEL_X_EVENT;
     71 		else if (!strcmp(type, "selection"))
     72 			data->spec_arr[data->nspec].type = SELABEL_X_SELN;
     73 		else if (!strcmp(type, "poly_property"))
     74 			data->spec_arr[data->nspec].type = SELABEL_X_POLYPROP;
     75 		else if (!strcmp(type, "poly_selection"))
     76 			data->spec_arr[data->nspec].type = SELABEL_X_POLYSELN;
     77 		else {
     78 			selinux_log(SELINUX_WARNING,
     79 				    "%s:  line %u has invalid object type %s\n",
     80 				    path, lineno, type);
     81 			return 0;
     82 		}
     83 		data->spec_arr[data->nspec].key = key;
     84 		data->spec_arr[data->nspec].lr.ctx_raw = context;
     85 		free(type);
     86 	}
     87 
     88 	data->nspec++;
     89 	if (pass == 0) {
     90 		free(type);
     91 		free(key);
     92 		free(context);
     93 	}
     94 	return 0;
     95 }
     96 
     97 static int init(struct selabel_handle *rec, struct selinux_opt *opts,
     98 		unsigned n)
     99 {
    100 	FILE *fp;
    101 	struct saved_data *data = (struct saved_data *)rec->data;
    102 	const char *path = NULL;
    103 	char *line_buf = NULL;
    104 	size_t line_len = 0;
    105 	int status = -1;
    106 	unsigned int lineno, pass, maxnspec;
    107 	struct stat sb;
    108 
    109 	/* Process arguments */
    110 	while (n--)
    111 		switch(opts[n].type) {
    112 		case SELABEL_OPT_PATH:
    113 			path = opts[n].value;
    114 			break;
    115 		}
    116 
    117 	/* Open the specification file. */
    118 	if (!path)
    119 		path = selinux_x_context_path();
    120 	if ((fp = fopen(path, "r")) == NULL)
    121 		return -1;
    122 	__fsetlocking(fp, FSETLOCKING_BYCALLER);
    123 
    124 	if (fstat(fileno(fp), &sb) < 0)
    125 		return -1;
    126 	if (!S_ISREG(sb.st_mode)) {
    127 		errno = EINVAL;
    128 		return -1;
    129 	}
    130 	rec->spec_file = strdup(path);
    131 
    132 	/*
    133 	 * Perform two passes over the specification file.
    134 	 * The first pass counts the number of specifications and
    135 	 * performs simple validation of the input.  At the end
    136 	 * of the first pass, the spec array is allocated.
    137 	 * The second pass performs detailed validation of the input
    138 	 * and fills in the spec array.
    139 	 */
    140 	maxnspec = UINT_MAX / sizeof(spec_t);
    141 	for (pass = 0; pass < 2; pass++) {
    142 		lineno = 0;
    143 		data->nspec = 0;
    144 		while (getline(&line_buf, &line_len, fp) > 0 &&
    145 		       data->nspec < maxnspec) {
    146 			if (process_line(path, line_buf, pass, ++lineno, rec))
    147 				goto finish;
    148 		}
    149 		lineno = 0;
    150 
    151 		if (pass == 0) {
    152 			if (data->nspec == 0) {
    153 				status = 0;
    154 				goto finish;
    155 			}
    156 			data->spec_arr = malloc(sizeof(spec_t)*data->nspec);
    157 			if (data->spec_arr == NULL)
    158 				goto finish;
    159 			memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec);
    160 			maxnspec = data->nspec;
    161 			rewind(fp);
    162 		}
    163 	}
    164 	free(line_buf);
    165 
    166 	status = 0;
    167 finish:
    168 	fclose(fp);
    169 	return status;
    170 }
    171 
    172 /*
    173  * Backend interface routines
    174  */
    175 static void close(struct selabel_handle *rec)
    176 {
    177 	struct saved_data *data = (struct saved_data *)rec->data;
    178 	struct spec *spec, *spec_arr = data->spec_arr;
    179 	unsigned int i;
    180 
    181 	for (i = 0; i < data->nspec; i++) {
    182 		spec = &spec_arr[i];
    183 		free(spec->key);
    184 		free(spec->lr.ctx_raw);
    185 		free(spec->lr.ctx_trans);
    186 	}
    187 
    188 	if (spec_arr)
    189 	    free(spec_arr);
    190 
    191 	memset(data, 0, sizeof(*data));
    192 }
    193 
    194 static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
    195 					 const char *key, int type)
    196 {
    197 	struct saved_data *data = (struct saved_data *)rec->data;
    198 	spec_t *spec_arr = data->spec_arr;
    199 	unsigned int i;
    200 
    201 	for (i = 0; i < data->nspec; i++) {
    202 		if (spec_arr[i].type != type)
    203 			continue;
    204 		if (!fnmatch(spec_arr[i].key, key, 0))
    205 			break;
    206 	}
    207 
    208 	if (i >= data->nspec) {
    209 		/* No matching specification. */
    210 		errno = ENOENT;
    211 		return NULL;
    212 	}
    213 
    214 	spec_arr[i].matches++;
    215 	return &spec_arr[i].lr;
    216 }
    217 
    218 static void stats(struct selabel_handle *rec)
    219 {
    220 	struct saved_data *data = (struct saved_data *)rec->data;
    221 	unsigned int i, total = 0;
    222 
    223 	for (i = 0; i < data->nspec; i++)
    224 		total += data->spec_arr[i].matches;
    225 
    226 	selinux_log(SELINUX_INFO, "%u entries, %u matches made\n",
    227 		  data->nspec, total);
    228 }
    229 
    230 int selabel_x_init(struct selabel_handle *rec, struct selinux_opt *opts,
    231 		   unsigned nopts)
    232 {
    233 	struct saved_data *data;
    234 
    235 	data = (struct saved_data *)malloc(sizeof(*data));
    236 	if (!data)
    237 		return -1;
    238 	memset(data, 0, sizeof(*data));
    239 
    240 	rec->data = data;
    241 	rec->func_close = &close;
    242 	rec->func_lookup = &lookup;
    243 	rec->func_stats = &stats;
    244 
    245 	return init(rec, opts, nopts);
    246 }
    247