Home | History | Annotate | Download | only in utils
      1 #include <ctype.h>
      2 #include <errno.h>
      3 #include <pcre.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <unistd.h>
      8 #include <sys/types.h>
      9 #include <sys/stat.h>
     10 #include <getopt.h>
     11 #include <limits.h>
     12 #include <selinux/selinux.h>
     13 
     14 #include "../src/label_file.h"
     15 
     16 static int validate_context(char __attribute__ ((unused)) **ctx)
     17 {
     18 	return 0;
     19 }
     20 
     21 static int process_file(struct selabel_handle *rec, const char *filename)
     22 {
     23 	unsigned int line_num;
     24 	int rc;
     25 	char *line_buf = NULL;
     26 	size_t line_len = 0;
     27 	FILE *context_file;
     28 	const char *prefix = NULL;
     29 
     30 	context_file = fopen(filename, "r");
     31 	if (!context_file) {
     32 		fprintf(stderr, "Error opening %s: %s\n",
     33 			    filename, strerror(errno));
     34 		return -1;
     35 	}
     36 
     37 	line_num = 0;
     38 	rc = 0;
     39 	while (getline(&line_buf, &line_len, context_file) > 0) {
     40 		rc = process_line(rec, filename, prefix, line_buf, ++line_num);
     41 		if (rc)
     42 			goto out;
     43 	}
     44 out:
     45 	free(line_buf);
     46 	fclose(context_file);
     47 	return rc;
     48 }
     49 
     50 /*
     51  * File Format
     52  *
     53  * u32 - magic number
     54  * u32 - version
     55  * u32 - length of pcre version EXCLUDING nul
     56  * char - pcre version string EXCLUDING nul
     57  * u32 - number of stems
     58  * ** Stems
     59  *	u32  - length of stem EXCLUDING nul
     60  *	char - stem char array INCLUDING nul
     61  * u32 - number of regexs
     62  * ** Regexes
     63  *	u32  - length of upcoming context INCLUDING nul
     64  *	char - char array of the raw context
     65  *	u32  - length of the upcoming regex_str
     66  *	char - char array of the original regex string including the stem.
     67  *	u32  - mode bits for >= SELINUX_COMPILED_FCONTEXT_MODE
     68  *	       mode_t for <= SELINUX_COMPILED_FCONTEXT_PCRE_VERS
     69  *	s32  - stemid associated with the regex
     70  *	u32  - spec has meta characters
     71  *	u32  - The specs prefix_len if >= SELINUX_COMPILED_FCONTEXT_PREFIX_LEN
     72  *	u32  - data length of the pcre regex
     73  *	char - a bufer holding the raw pcre regex info
     74  *	u32  - data length of the pcre regex study daya
     75  *	char - a buffer holding the raw pcre regex study data
     76  */
     77 static int write_binary_file(struct saved_data *data, int fd)
     78 {
     79 	struct spec *specs = data->spec_arr;
     80 	FILE *bin_file;
     81 	size_t len;
     82 	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
     83 	uint32_t section_len;
     84 	uint32_t i;
     85 	int rc;
     86 
     87 	bin_file = fdopen(fd, "w");
     88 	if (!bin_file) {
     89 		perror("fopen output_file");
     90 		exit(EXIT_FAILURE);
     91 	}
     92 
     93 	/* write some magic number */
     94 	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
     95 	if (len != 1)
     96 		goto err;
     97 
     98 	/* write the version */
     99 	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
    100 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    101 	if (len != 1)
    102 		goto err;
    103 
    104 	/* write the pcre version */
    105 	section_len = strlen(pcre_version());
    106 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    107 	if (len != 1)
    108 		goto err;
    109 	len = fwrite(pcre_version(), sizeof(char), section_len, bin_file);
    110 	if (len != section_len)
    111 		goto err;
    112 
    113 	/* write the number of stems coming */
    114 	section_len = data->num_stems;
    115 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    116 	if (len != 1)
    117 		goto err;
    118 
    119 	for (i = 0; i < section_len; i++) {
    120 		char *stem = data->stem_arr[i].buf;
    121 		uint32_t stem_len = data->stem_arr[i].len;
    122 
    123 		/* write the strlen (aka no nul) */
    124 		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
    125 		if (len != 1)
    126 			goto err;
    127 
    128 		/* include the nul in the file */
    129 		stem_len += 1;
    130 		len = fwrite(stem, sizeof(char), stem_len, bin_file);
    131 		if (len != stem_len)
    132 			goto err;
    133 	}
    134 
    135 	/* write the number of regexes coming */
    136 	section_len = data->nspec;
    137 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    138 	if (len != 1)
    139 		goto err;
    140 
    141 	for (i = 0; i < section_len; i++) {
    142 		char *context = specs[i].lr.ctx_raw;
    143 		char *regex_str = specs[i].regex_str;
    144 		mode_t mode = specs[i].mode;
    145 		size_t prefix_len = specs[i].prefix_len;
    146 		int32_t stem_id = specs[i].stem_id;
    147 		pcre *re = specs[i].regex;
    148 		pcre_extra *sd = get_pcre_extra(&specs[i]);
    149 		uint32_t to_write;
    150 		size_t size;
    151 
    152 		/* length of the context string (including nul) */
    153 		to_write = strlen(context) + 1;
    154 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    155 		if (len != 1)
    156 			goto err;
    157 
    158 		/* original context strin (including nul) */
    159 		len = fwrite(context, sizeof(char), to_write, bin_file);
    160 		if (len != to_write)
    161 			goto err;
    162 
    163 		/* length of the original regex string (including nul) */
    164 		to_write = strlen(regex_str) + 1;
    165 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    166 		if (len != 1)
    167 			goto err;
    168 
    169 		/* original regex string */
    170 		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
    171 		if (len != to_write)
    172 			goto err;
    173 
    174 		/* binary F_MODE bits */
    175 		to_write = mode;
    176 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    177 		if (len != 1)
    178 			goto err;
    179 
    180 		/* stem for this regex (could be -1) */
    181 		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
    182 		if (len != 1)
    183 			goto err;
    184 
    185 		/* does this spec have a metaChar? */
    186 		to_write = specs[i].hasMetaChars;
    187 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
    188 		if (len != 1)
    189 			goto err;
    190 
    191 		/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
    192 		to_write = prefix_len;
    193 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
    194 		if (len != 1)
    195 			goto err;
    196 
    197 		/* determine the size of the pcre data in bytes */
    198 		rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &size);
    199 		if (rc < 0)
    200 			goto err;
    201 
    202 		/* write the number of bytes in the pcre data */
    203 		to_write = size;
    204 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    205 		if (len != 1)
    206 			goto err;
    207 
    208 		/* write the actual pcre data as a char array */
    209 		len = fwrite(re, 1, to_write, bin_file);
    210 		if (len != to_write)
    211 			goto err;
    212 
    213 		/* determine the size of the pcre study info */
    214 		rc = pcre_fullinfo(re, sd, PCRE_INFO_STUDYSIZE, &size);
    215 		if (rc < 0)
    216 			goto err;
    217 
    218 		/* write the number of bytes in the pcre study data */
    219 		to_write = size;
    220 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    221 		if (len != 1)
    222 			goto err;
    223 
    224 		/* write the actual pcre study data as a char array */
    225 		len = fwrite(sd->study_data, 1, to_write, bin_file);
    226 		if (len != to_write)
    227 			goto err;
    228 	}
    229 
    230 	rc = 0;
    231 out:
    232 	fclose(bin_file);
    233 	return rc;
    234 err:
    235 	rc = -1;
    236 	goto out;
    237 }
    238 
    239 static void free_specs(struct saved_data *data)
    240 {
    241 	struct spec *specs = data->spec_arr;
    242 	unsigned int num_entries = data->nspec;
    243 	unsigned int i;
    244 
    245 	for (i = 0; i < num_entries; i++) {
    246 		free(specs[i].lr.ctx_raw);
    247 		free(specs[i].lr.ctx_trans);
    248 		free(specs[i].regex_str);
    249 		free(specs[i].type_str);
    250 		pcre_free(specs[i].regex);
    251 		pcre_free_study(specs[i].sd);
    252 	}
    253 	free(specs);
    254 
    255 	num_entries = data->num_stems;
    256 	for (i = 0; i < num_entries; i++)
    257 		free(data->stem_arr[i].buf);
    258 	free(data->stem_arr);
    259 
    260 	memset(data, 0, sizeof(*data));
    261 }
    262 
    263 static void usage(const char *progname)
    264 {
    265 	fprintf(stderr,
    266 		"usage: %s [-o out_file] fc_file\n"
    267 		"Where:\n\t"
    268 		"-o      Optional file name of the PCRE formatted binary\n\t"
    269 		"        file to be output. If not specified the default\n\t"
    270 		"        will be fc_file with the .bin suffix appended.\n\t"
    271 		"fc_file The text based file contexts file to be processed.\n",
    272 		progname);
    273 		exit(EXIT_FAILURE);
    274 }
    275 
    276 int main(int argc, char *argv[])
    277 {
    278 	const char *path = NULL;
    279 	const char *out_file = NULL;
    280 	char stack_path[PATH_MAX + 1];
    281 	char *tmp = NULL;
    282 	int fd, rc, opt;
    283 	struct stat buf;
    284 	struct selabel_handle *rec = NULL;
    285 	struct saved_data *data = NULL;
    286 
    287 	if (argc < 2)
    288 		usage(argv[0]);
    289 
    290 	while ((opt = getopt(argc, argv, "o:")) > 0) {
    291 		switch (opt) {
    292 		case 'o':
    293 			out_file = optarg;
    294 			break;
    295 		default:
    296 			usage(argv[0]);
    297 		}
    298 	}
    299 
    300 	if (optind >= argc)
    301 		usage(argv[0]);
    302 
    303 	path = argv[optind];
    304 	if (stat(path, &buf) < 0) {
    305 		fprintf(stderr, "Can not stat: %s: %m\n", path);
    306 		exit(EXIT_FAILURE);
    307 	}
    308 
    309 	/* Generate dummy handle for process_line() function */
    310 	rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
    311 	if (!rec) {
    312 		fprintf(stderr, "Failed to calloc handle\n");
    313 		exit(EXIT_FAILURE);
    314 	}
    315 	rec->backend = SELABEL_CTX_FILE;
    316 
    317 	/* Need to set validation on to get the bin file generated by the
    318 	 * process_line function, however as the bin file being generated
    319 	 * may not be related to the currently loaded policy (that it
    320 	 * would be validated against), then set callback to ignore any
    321 	 * validation. */
    322 	rec->validating = 1;
    323 	selinux_set_callback(SELINUX_CB_VALIDATE,
    324 			    (union selinux_callback)&validate_context);
    325 
    326 	data = (struct saved_data *)calloc(1, sizeof(*data));
    327 	if (!data) {
    328 		fprintf(stderr, "Failed to calloc saved_data\n");
    329 		free(rec);
    330 		exit(EXIT_FAILURE);
    331 	}
    332 
    333 	rec->data = data;
    334 
    335 	rc = process_file(rec, path);
    336 	if (rc < 0)
    337 		goto err;
    338 
    339 	rc = sort_specs(data);
    340 	if (rc)
    341 		goto err;
    342 
    343 	if (out_file)
    344 		rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
    345 	else
    346 		rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
    347 
    348 	if (rc < 0 || rc >= (int)sizeof(stack_path))
    349 		goto err;
    350 
    351 	tmp = malloc(strlen(stack_path) + 7);
    352 	if (!tmp)
    353 		goto err;
    354 
    355 	rc = sprintf(tmp, "%sXXXXXX", stack_path);
    356 	if (rc < 0)
    357 		goto err;
    358 
    359 	fd  = mkstemp(tmp);
    360 	if (fd < 0)
    361 		goto err;
    362 
    363 	rc = fchmod(fd, buf.st_mode);
    364 	if (rc < 0) {
    365 		perror("fchmod failed to set permission on compiled regexs");
    366 		goto err_unlink;
    367 	}
    368 
    369 	rc = write_binary_file(data, fd);
    370 	if (rc < 0)
    371 		goto err_unlink;
    372 
    373 	rc = rename(tmp, stack_path);
    374 	if (rc < 0)
    375 		goto err_unlink;
    376 
    377 	rc = 0;
    378 out:
    379 	free_specs(data);
    380 	free(rec);
    381 	free(data);
    382 	free(tmp);
    383 	return rc;
    384 
    385 err_unlink:
    386 	unlink(tmp);
    387 err:
    388 	rc = -1;
    389 	goto out;
    390 }
    391