Home | History | Annotate | Download | only in src
      1 #ifndef _SELABEL_FILE_H_
      2 #define _SELABEL_FILE_H_
      3 
      4 #include <sys/stat.h>
      5 
      6 #include "callbacks.h"
      7 #include "label_internal.h"
      8 
      9 #define SELINUX_MAGIC_COMPILED_FCONTEXT	0xf97cff8a
     10 
     11 /* Version specific changes */
     12 #define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS	1
     13 #define SELINUX_COMPILED_FCONTEXT_PCRE_VERS	2
     14 #define SELINUX_COMPILED_FCONTEXT_MODE		3
     15 #define SELINUX_COMPILED_FCONTEXT_PREFIX_LEN	4
     16 
     17 #define SELINUX_COMPILED_FCONTEXT_MAX_VERS	SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
     18 
     19 /* Prior to version 8.20, libpcre did not have pcre_free_study() */
     20 #if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20))
     21 #define pcre_free_study  pcre_free
     22 #endif
     23 
     24 /* A file security context specification. */
     25 struct spec {
     26 	struct selabel_lookup_rec lr;	/* holds contexts for lookup result */
     27 	char *regex_str;	/* regular expession string for diagnostics */
     28 	char *type_str;		/* type string for diagnostic messages */
     29 	pcre *regex;		/* compiled regular expression */
     30 	union {
     31 		pcre_extra *sd;	/* pointer to extra compiled stuff */
     32 		pcre_extra lsd;	/* used to hold the mmap'd version */
     33 	};
     34 	mode_t mode;		/* mode format value */
     35 	int matches;		/* number of matching pathnames */
     36 	int stem_id;		/* indicates which stem-compression item */
     37 	char hasMetaChars;	/* regular expression has meta-chars */
     38 	char regcomp;		/* regex_str has been compiled to regex */
     39 	char from_mmap;		/* this spec is from an mmap of the data */
     40 	size_t prefix_len;      /* length of fixed path prefix */
     41 };
     42 
     43 /* A regular expression stem */
     44 struct stem {
     45 	char *buf;
     46 	int len;
     47 	char from_mmap;
     48 };
     49 
     50 /* Where we map the file in during selabel_open() */
     51 struct mmap_area {
     52 	void *addr;	/* Start addr + len used to release memory at close */
     53 	size_t len;
     54 	void *next_addr;	/* Incremented by next_entry() */
     55 	size_t next_len;	/* Decremented by next_entry() */
     56 	struct mmap_area *next;
     57 };
     58 
     59 /* Our stored configuration */
     60 struct saved_data {
     61 	/*
     62 	 * The array of specifications, initially in the same order as in
     63 	 * the specification file. Sorting occurs based on hasMetaChars.
     64 	 */
     65 	struct spec *spec_arr;
     66 	unsigned int nspec;
     67 	unsigned int alloc_specs;
     68 
     69 	/*
     70 	 * The array of regular expression stems.
     71 	 */
     72 	struct stem *stem_arr;
     73 	int num_stems;
     74 	int alloc_stems;
     75 	struct mmap_area *mmap_areas;
     76 };
     77 
     78 static inline pcre_extra *get_pcre_extra(struct spec *spec)
     79 {
     80 	if (spec->from_mmap)
     81 		return &spec->lsd;
     82 	else
     83 		return spec->sd;
     84 }
     85 
     86 static inline mode_t string_to_mode(char *mode)
     87 {
     88 	size_t len;
     89 
     90 	if (!mode)
     91 		return 0;
     92 	len = strlen(mode);
     93 	if (mode[0] != '-' || len != 2)
     94 		return -1;
     95 	switch (mode[1]) {
     96 	case 'b':
     97 		return S_IFBLK;
     98 	case 'c':
     99 		return S_IFCHR;
    100 	case 'd':
    101 		return S_IFDIR;
    102 	case 'p':
    103 		return S_IFIFO;
    104 	case 'l':
    105 		return S_IFLNK;
    106 	case 's':
    107 		return S_IFSOCK;
    108 	case '-':
    109 		return S_IFREG;
    110 	default:
    111 		return -1;
    112 	}
    113 	/* impossible to get here */
    114 	return 0;
    115 }
    116 
    117 static inline int grow_specs(struct saved_data *data)
    118 {
    119 	struct spec *specs;
    120 	size_t new_specs, total_specs;
    121 
    122 	if (data->nspec < data->alloc_specs)
    123 		return 0;
    124 
    125 	new_specs = data->nspec + 16;
    126 	total_specs = data->nspec + new_specs;
    127 
    128 	specs = realloc(data->spec_arr, total_specs * sizeof(*specs));
    129 	if (!specs) {
    130 		perror("realloc");
    131 		return -1;
    132 	}
    133 
    134 	/* blank the new entries */
    135 	memset(&specs[data->nspec], 0, new_specs * sizeof(*specs));
    136 
    137 	data->spec_arr = specs;
    138 	data->alloc_specs = total_specs;
    139 	return 0;
    140 }
    141 
    142 /* Determine if the regular expression specification has any meta characters. */
    143 static inline void spec_hasMetaChars(struct spec *spec)
    144 {
    145 	char *c;
    146 	int len;
    147 	char *end;
    148 
    149 	c = spec->regex_str;
    150 	len = strlen(spec->regex_str);
    151 	end = c + len;
    152 
    153 	spec->hasMetaChars = 0;
    154 	spec->prefix_len = len;
    155 
    156 	/* Look at each character in the RE specification string for a
    157 	 * meta character. Return when any meta character reached. */
    158 	while (c < end) {
    159 		switch (*c) {
    160 		case '.':
    161 		case '^':
    162 		case '$':
    163 		case '?':
    164 		case '*':
    165 		case '+':
    166 		case '|':
    167 		case '[':
    168 		case '(':
    169 		case '{':
    170 			spec->hasMetaChars = 1;
    171 			spec->prefix_len = c - spec->regex_str;
    172 			return;
    173 		case '\\':	/* skip the next character */
    174 			c++;
    175 			break;
    176 		default:
    177 			break;
    178 
    179 		}
    180 		c++;
    181 	}
    182 }
    183 
    184 /* Move exact pathname specifications to the end. */
    185 static inline int sort_specs(struct saved_data *data)
    186 {
    187 	struct spec *spec_copy;
    188 	struct spec spec;
    189 	unsigned int i;
    190 	int front, back;
    191 	size_t len = sizeof(*spec_copy);
    192 
    193 	spec_copy = malloc(len * data->nspec);
    194 	if (!spec_copy)
    195 		return -1;
    196 
    197 	/* first move the exact pathnames to the back */
    198 	front = 0;
    199 	back = data->nspec - 1;
    200 	for (i = 0; i < data->nspec; i++) {
    201 		if (data->spec_arr[i].hasMetaChars)
    202 			memcpy(&spec_copy[front++], &data->spec_arr[i], len);
    203 		else
    204 			memcpy(&spec_copy[back--], &data->spec_arr[i], len);
    205 	}
    206 
    207 	/*
    208 	 * now the exact pathnames are at the end, but they are in the reverse
    209 	 * order. Since 'front' is now the first of the 'exact' we can run
    210 	 * that part of the array switching the front and back element.
    211 	 */
    212 	back = data->nspec - 1;
    213 	while (front < back) {
    214 		/* save the front */
    215 		memcpy(&spec, &spec_copy[front], len);
    216 		/* move the back to the front */
    217 		memcpy(&spec_copy[front], &spec_copy[back], len);
    218 		/* put the old front in the back */
    219 		memcpy(&spec_copy[back], &spec, len);
    220 		front++;
    221 		back--;
    222 	}
    223 
    224 	free(data->spec_arr);
    225 	data->spec_arr = spec_copy;
    226 
    227 	return 0;
    228 }
    229 
    230 /* Return the length of the text that can be considered the stem, returns 0
    231  * if there is no identifiable stem */
    232 static inline int get_stem_from_spec(const char *const buf)
    233 {
    234 	const char *tmp = strchr(buf + 1, '/');
    235 	const char *ind;
    236 
    237 	if (!tmp)
    238 		return 0;
    239 
    240 	for (ind = buf; ind < tmp; ind++) {
    241 		if (strchr(".^$?*+|[({", (int)*ind))
    242 			return 0;
    243 	}
    244 	return tmp - buf;
    245 }
    246 
    247 /*
    248  * return the stemid given a string and a length
    249  */
    250 static inline int find_stem(struct saved_data *data, const char *buf,
    251 						    int stem_len)
    252 {
    253 	int i;
    254 
    255 	for (i = 0; i < data->num_stems; i++) {
    256 		if (stem_len == data->stem_arr[i].len &&
    257 		    !strncmp(buf, data->stem_arr[i].buf, stem_len))
    258 			return i;
    259 	}
    260 
    261 	return -1;
    262 }
    263 
    264 /* returns the index of the new stored object */
    265 static inline int store_stem(struct saved_data *data, char *buf, int stem_len)
    266 {
    267 	int num = data->num_stems;
    268 
    269 	if (data->alloc_stems == num) {
    270 		struct stem *tmp_arr;
    271 
    272 		data->alloc_stems = data->alloc_stems * 2 + 16;
    273 		tmp_arr = realloc(data->stem_arr,
    274 				  sizeof(*tmp_arr) * data->alloc_stems);
    275 		if (!tmp_arr)
    276 			return -1;
    277 		data->stem_arr = tmp_arr;
    278 	}
    279 	data->stem_arr[num].len = stem_len;
    280 	data->stem_arr[num].buf = buf;
    281 	data->stem_arr[num].from_mmap = 0;
    282 	data->num_stems++;
    283 
    284 	return num;
    285 }
    286 
    287 /* find the stem of a file spec, returns the index into stem_arr for a new
    288  * or existing stem, (or -1 if there is no possible stem - IE for a file in
    289  * the root directory or a regex that is too complex for us). */
    290 static inline int find_stem_from_spec(struct saved_data *data, const char *buf)
    291 {
    292 	int stem_len = get_stem_from_spec(buf);
    293 	int stemid;
    294 	char *stem;
    295 
    296 	if (!stem_len)
    297 		return -1;
    298 
    299 	stemid = find_stem(data, buf, stem_len);
    300 	if (stemid >= 0)
    301 		return stemid;
    302 
    303 	/* not found, allocate a new one */
    304 	stem = strndup(buf, stem_len);
    305 	if (!stem)
    306 		return -1;
    307 
    308 	return store_stem(data, stem, stem_len);
    309 }
    310 
    311 /* This will always check for buffer over-runs and either read the next entry
    312  * if buf != NULL or skip over the entry (as these areas are mapped in the
    313  * current buffer). */
    314 static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
    315 {
    316 	if (bytes > fp->next_len)
    317 		return -1;
    318 
    319 	if (buf)
    320 		memcpy(buf, fp->next_addr, bytes);
    321 
    322 	fp->next_addr = (char *)fp->next_addr + bytes;
    323 	fp->next_len -= bytes;
    324 	return 0;
    325 }
    326 
    327 static inline int compile_regex(struct saved_data *data, struct spec *spec,
    328 					    const char **errbuf)
    329 {
    330 	const char *tmperrbuf;
    331 	char *reg_buf, *anchored_regex, *cp;
    332 	struct stem *stem_arr = data->stem_arr;
    333 	size_t len;
    334 	int erroff;
    335 
    336 	if (spec->regcomp)
    337 		return 0; /* already done */
    338 
    339 	/* Skip the fixed stem. */
    340 	reg_buf = spec->regex_str;
    341 	if (spec->stem_id >= 0)
    342 		reg_buf += stem_arr[spec->stem_id].len;
    343 
    344 	/* Anchor the regular expression. */
    345 	len = strlen(reg_buf);
    346 	cp = anchored_regex = malloc(len + 3);
    347 	if (!anchored_regex)
    348 		return -1;
    349 
    350 	/* Create ^...$ regexp.  */
    351 	*cp++ = '^';
    352 	memcpy(cp, reg_buf, len);
    353 	cp += len;
    354 	*cp++ = '$';
    355 	*cp = '\0';
    356 
    357 	/* Compile the regular expression. */
    358 	spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf,
    359 						    &erroff, NULL);
    360 	free(anchored_regex);
    361 	if (!spec->regex) {
    362 		if (errbuf)
    363 			*errbuf = tmperrbuf;
    364 		return -1;
    365 	}
    366 
    367 	spec->sd = pcre_study(spec->regex, 0, &tmperrbuf);
    368 	if (!spec->sd && tmperrbuf) {
    369 		if (errbuf)
    370 			*errbuf = tmperrbuf;
    371 		return -1;
    372 	}
    373 
    374 	/* Done. */
    375 	spec->regcomp = 1;
    376 
    377 	return 0;
    378 }
    379 
    380 /* This service is used by label_file.c process_file() and
    381  * utils/sefcontext_compile.c */
    382 static inline int process_line(struct selabel_handle *rec,
    383 			const char *path, const char *prefix,
    384 			char *line_buf, unsigned lineno)
    385 {
    386 	int items, len, rc;
    387 	char *regex = NULL, *type = NULL, *context = NULL;
    388 	struct saved_data *data = (struct saved_data *)rec->data;
    389 	struct spec *spec_arr;
    390 	unsigned int nspec = data->nspec;
    391 	const char *errbuf = NULL;
    392 
    393 	items = read_spec_entries(line_buf, 3, &regex, &type, &context);
    394 	if (items <= 0)
    395 		return items;
    396 
    397 	if (items < 2) {
    398 		COMPAT_LOG(SELINUX_ERROR,
    399 			    "%s:  line %u is missing fields\n", path,
    400 			    lineno);
    401 		if (items == 1)
    402 			free(regex);
    403 		errno = EINVAL;
    404 		return -1;
    405 	} else if (items == 2) {
    406 		/* The type field is optional. */
    407 		context = type;
    408 		type = 0;
    409 	}
    410 
    411 	len = get_stem_from_spec(regex);
    412 	if (len && prefix && strncmp(prefix, regex, len)) {
    413 		/* Stem of regex does not match requested prefix, discard. */
    414 		free(regex);
    415 		free(type);
    416 		free(context);
    417 		return 0;
    418 	}
    419 
    420 	rc = grow_specs(data);
    421 	if (rc)
    422 		return rc;
    423 
    424 	spec_arr = data->spec_arr;
    425 
    426 	/* process and store the specification in spec. */
    427 	spec_arr[nspec].stem_id = find_stem_from_spec(data, regex);
    428 	spec_arr[nspec].regex_str = regex;
    429 
    430 	spec_arr[nspec].type_str = type;
    431 	spec_arr[nspec].mode = 0;
    432 
    433 	spec_arr[nspec].lr.ctx_raw = context;
    434 
    435 	/*
    436 	 * bump data->nspecs to cause closef() to cover it in its free
    437 	 * but do not bump nspec since it's used below.
    438 	 */
    439 	data->nspec++;
    440 
    441 	if (rec->validating &&
    442 			    compile_regex(data, &spec_arr[nspec], &errbuf)) {
    443 		COMPAT_LOG(SELINUX_ERROR,
    444 			   "%s:  line %u has invalid regex %s:  %s\n",
    445 			   path, lineno, regex,
    446 			   (errbuf ? errbuf : "out of memory"));
    447 		errno = EINVAL;
    448 		return -1;
    449 	}
    450 
    451 	if (type) {
    452 		mode_t mode = string_to_mode(type);
    453 
    454 		if (mode == (mode_t)-1) {
    455 			COMPAT_LOG(SELINUX_ERROR,
    456 				   "%s:  line %u has invalid file type %s\n",
    457 				   path, lineno, type);
    458 			errno = EINVAL;
    459 			return -1;
    460 		}
    461 		spec_arr[nspec].mode = mode;
    462 	}
    463 
    464 	/* Determine if specification has
    465 	 * any meta characters in the RE */
    466 	spec_hasMetaChars(&spec_arr[nspec]);
    467 
    468 	if (strcmp(context, "<<none>>") && rec->validating)
    469 		compat_validate(rec, &spec_arr[nspec].lr, path, lineno);
    470 
    471 	return 0;
    472 }
    473 
    474 #endif /* _SELABEL_FILE_H_ */
    475