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