Home | History | Annotate | Download | only in utils
      1 #include <ctype.h>
      2 #include <errno.h>
      3 #include <stdint.h>
      4 #include <stdio.h>
      5 #include <string.h>
      6 #include <unistd.h>
      7 #include <sys/types.h>
      8 #include <sys/stat.h>
      9 #include <getopt.h>
     10 #include <limits.h>
     11 #include <selinux/selinux.h>
     12 #include <sepol/sepol.h>
     13 
     14 #include "../src/label_file.h"
     15 #include "../src/regex.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 			     int do_write_precompregex)
     97 {
     98 	struct spec *specs = data->spec_arr;
     99 	FILE *bin_file;
    100 	size_t len;
    101 	uint32_t magic = SELINUX_MAGIC_COMPILED_FCONTEXT;
    102 	uint32_t section_len;
    103 	uint32_t i;
    104 	int rc;
    105 	const char *reg_version;
    106 	const char *reg_arch;
    107 
    108 	bin_file = fdopen(fd, "w");
    109 	if (!bin_file) {
    110 		perror("fopen output_file");
    111 		exit(EXIT_FAILURE);
    112 	}
    113 
    114 	/* write some magic number */
    115 	len = fwrite(&magic, sizeof(uint32_t), 1, bin_file);
    116 	if (len != 1)
    117 		goto err;
    118 
    119 	/* write the version */
    120 	section_len = SELINUX_COMPILED_FCONTEXT_MAX_VERS;
    121 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    122 	if (len != 1)
    123 		goto err;
    124 
    125 	/* write version of the regex back-end */
    126 	reg_version = regex_version();
    127 	if (!reg_version)
    128 		goto err;
    129 	section_len = strlen(reg_version);
    130 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    131 	if (len != 1)
    132 		goto err;
    133 	len = fwrite(reg_version, sizeof(char), section_len, bin_file);
    134 	if (len != section_len)
    135 		goto err;
    136 
    137 	/* write regex arch string */
    138 	reg_arch = regex_arch_string();
    139 	if (!reg_arch)
    140 		goto err;
    141 	section_len = strlen(reg_arch);
    142 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    143 	if (len != 1)
    144 		goto err;
    145 	len = fwrite(reg_arch, sizeof(char), section_len, bin_file);
    146 	if (len != section_len)
    147 		goto err;
    148 
    149 	/* write the number of stems coming */
    150 	section_len = data->num_stems;
    151 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    152 	if (len != 1)
    153 		goto err;
    154 
    155 	for (i = 0; i < section_len; i++) {
    156 		char *stem = data->stem_arr[i].buf;
    157 		uint32_t stem_len = data->stem_arr[i].len;
    158 
    159 		/* write the strlen (aka no nul) */
    160 		len = fwrite(&stem_len, sizeof(uint32_t), 1, bin_file);
    161 		if (len != 1)
    162 			goto err;
    163 
    164 		/* include the nul in the file */
    165 		stem_len += 1;
    166 		len = fwrite(stem, sizeof(char), stem_len, bin_file);
    167 		if (len != stem_len)
    168 			goto err;
    169 	}
    170 
    171 	/* write the number of regexes coming */
    172 	section_len = data->nspec;
    173 	len = fwrite(&section_len, sizeof(uint32_t), 1, bin_file);
    174 	if (len != 1)
    175 		goto err;
    176 
    177 	for (i = 0; i < section_len; i++) {
    178 		char *context = specs[i].lr.ctx_raw;
    179 		char *regex_str = specs[i].regex_str;
    180 		mode_t mode = specs[i].mode;
    181 		size_t prefix_len = specs[i].prefix_len;
    182 		int32_t stem_id = specs[i].stem_id;
    183 		struct regex_data *re = specs[i].regex;
    184 		uint32_t to_write;
    185 
    186 		/* length of the context string (including nul) */
    187 		to_write = strlen(context) + 1;
    188 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    189 		if (len != 1)
    190 			goto err;
    191 
    192 		/* original context strin (including nul) */
    193 		len = fwrite(context, sizeof(char), to_write, bin_file);
    194 		if (len != to_write)
    195 			goto err;
    196 
    197 		/* length of the original regex string (including nul) */
    198 		to_write = strlen(regex_str) + 1;
    199 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    200 		if (len != 1)
    201 			goto err;
    202 
    203 		/* original regex string */
    204 		len = fwrite(regex_str, sizeof(char), to_write, bin_file);
    205 		if (len != to_write)
    206 			goto err;
    207 
    208 		/* binary F_MODE bits */
    209 		to_write = mode;
    210 		len = fwrite(&to_write, sizeof(uint32_t), 1, bin_file);
    211 		if (len != 1)
    212 			goto err;
    213 
    214 		/* stem for this regex (could be -1) */
    215 		len = fwrite(&stem_id, sizeof(stem_id), 1, bin_file);
    216 		if (len != 1)
    217 			goto err;
    218 
    219 		/* does this spec have a metaChar? */
    220 		to_write = specs[i].hasMetaChars;
    221 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
    222 		if (len != 1)
    223 			goto err;
    224 
    225 		/* For SELINUX_COMPILED_FCONTEXT_PREFIX_LEN */
    226 		to_write = prefix_len;
    227 		len = fwrite(&to_write, sizeof(to_write), 1, bin_file);
    228 		if (len != 1)
    229 			goto err;
    230 
    231 		/* Write regex related data */
    232 		rc = regex_writef(re, bin_file, do_write_precompregex);
    233 		if (rc < 0)
    234 			goto err;
    235 	}
    236 
    237 	rc = 0;
    238 out:
    239 	fclose(bin_file);
    240 	return rc;
    241 err:
    242 	rc = -1;
    243 	goto out;
    244 }
    245 
    246 static void free_specs(struct saved_data *data)
    247 {
    248 	struct spec *specs = data->spec_arr;
    249 	unsigned int num_entries = data->nspec;
    250 	unsigned int i;
    251 
    252 	for (i = 0; i < num_entries; i++) {
    253 		free(specs[i].lr.ctx_raw);
    254 		free(specs[i].lr.ctx_trans);
    255 		free(specs[i].regex_str);
    256 		free(specs[i].type_str);
    257 		regex_data_free(specs[i].regex);
    258 	}
    259 	free(specs);
    260 
    261 	num_entries = data->num_stems;
    262 	for (i = 0; i < num_entries; i++)
    263 		free(data->stem_arr[i].buf);
    264 	free(data->stem_arr);
    265 
    266 	memset(data, 0, sizeof(*data));
    267 }
    268 
    269 static __attribute__ ((__noreturn__)) void usage(const char *progname)
    270 {
    271 	fprintf(stderr,
    272 	    "usage: %s [-o out_file] [-p policy_file] fc_file\n"
    273 	    "Where:\n\t"
    274 	    "-o       Optional file name of the PCRE formatted binary\n\t"
    275 	    "         file to be output. If not specified the default\n\t"
    276 	    "         will be fc_file with the .bin suffix appended.\n\t"
    277 	    "-p       Optional binary policy file that will be used to\n\t"
    278 	    "         validate contexts defined in the fc_file.\n\t"
    279 	    "-r       Omit precompiled regular expressions from the output.\n\t"
    280 	    "         (PCRE2 only. Compiled PCRE2 regular expressions are\n\t"
    281 	    "         not portable across architectures. Use this flag\n\t"
    282 	    "         if you know that you build for an incompatible\n\t"
    283 	    "         architecture to save space. When linked against\n\t"
    284 	    "         PCRE1 this flag is ignored.)\n\t"
    285 	    "-i       Print regular expression info end exit. That is, back\n\t"
    286 	    "         end version and architecture identifier.\n\t"
    287 	    "         Arch identifier format (PCRE2):\n\t"
    288 	    "         <pointer width>-<size type width>-<endianness>, e.g.,\n\t"
    289 	    "         \"8-8-el\" for x86_64.\n\t"
    290 	    "fc_file  The text based file contexts file to be processed.\n",
    291 	    progname);
    292 		exit(EXIT_FAILURE);
    293 }
    294 
    295 int main(int argc, char *argv[])
    296 {
    297 	const char *path = NULL;
    298 	const char *out_file = NULL;
    299 	int do_write_precompregex = 1;
    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, "io:p:r")) > 0) {
    312 		switch (opt) {
    313 		case 'o':
    314 			out_file = optarg;
    315 			break;
    316 		case 'p':
    317 			policy_file = optarg;
    318 			break;
    319 		case 'r':
    320 			do_write_precompregex = 0;
    321 			break;
    322 		case 'i':
    323 			printf("%s (%s)\n", regex_version(),
    324 					regex_arch_string());
    325 			return 0;
    326 		default:
    327 			usage(argv[0]);
    328 		}
    329 	}
    330 
    331 	if (optind >= argc)
    332 		usage(argv[0]);
    333 
    334 	path = argv[optind];
    335 	if (stat(path, &buf) < 0) {
    336 		fprintf(stderr, "%s: could not stat: %s: %s\n", argv[0], path, strerror(errno));
    337 		exit(EXIT_FAILURE);
    338 	}
    339 
    340 	/* Open binary policy if supplied. */
    341 	if (policy_file) {
    342 		policy_fp = fopen(policy_file, "r");
    343 
    344 		if (!policy_fp) {
    345 			fprintf(stderr, "%s: failed to open %s: %s\n",
    346 				argv[0], policy_file, strerror(errno));
    347 			exit(EXIT_FAILURE);
    348 		}
    349 
    350 		if (sepol_set_policydb_from_file(policy_fp) < 0) {
    351 			fprintf(stderr, "%s: failed to load policy from %s\n",
    352 				argv[0], policy_file);
    353 			fclose(policy_fp);
    354 			exit(EXIT_FAILURE);
    355 		}
    356 	}
    357 
    358 	/* Generate dummy handle for process_line() function */
    359 	rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
    360 	if (!rec) {
    361 		fprintf(stderr, "%s: calloc failed: %s\n", argv[0], strerror(errno));
    362 		if (policy_fp)
    363 			fclose(policy_fp);
    364 		exit(EXIT_FAILURE);
    365 	}
    366 	rec->backend = SELABEL_CTX_FILE;
    367 
    368 	/* Need to set validation on to get the bin file generated by the
    369 	 * process_line function, however as the bin file being generated
    370 	 * may not be related to the currently loaded policy (that it
    371 	 * would be validated against), then set callback to ignore any
    372 	 * validation - unless the -p option is used in which case if an
    373 	 * error is detected, the process will be aborted. */
    374 	rec->validating = 1;
    375 	selinux_set_callback(SELINUX_CB_VALIDATE,
    376 			    (union selinux_callback)&validate_context);
    377 
    378 	data = (struct saved_data *)calloc(1, sizeof(*data));
    379 	if (!data) {
    380 		fprintf(stderr, "%s: calloc failed: %s\n", argv[0], strerror(errno));
    381 		free(rec);
    382 		if (policy_fp)
    383 			fclose(policy_fp);
    384 		exit(EXIT_FAILURE);
    385 	}
    386 
    387 	rec->data = data;
    388 
    389 	rc = process_file(rec, path);
    390 	if (rc < 0) {
    391 		fprintf(stderr, "%s: process_file failed\n", argv[0]);
    392 		goto err;
    393 	}
    394 
    395 	rc = sort_specs(data);
    396 	if (rc) {
    397 		fprintf(stderr, "%s: sort_specs failed\n", argv[0]);
    398 		goto err;
    399 	}
    400 
    401 	if (out_file)
    402 		rc = snprintf(stack_path, sizeof(stack_path), "%s", out_file);
    403 	else
    404 		rc = snprintf(stack_path, sizeof(stack_path), "%s.bin", path);
    405 
    406 	if (rc < 0 || rc >= (int)sizeof(stack_path)) {
    407 		fprintf(stderr, "%s: snprintf failed\n", argv[0]);
    408 		goto err;
    409 	}
    410 
    411 	tmp = malloc(strlen(stack_path) + 7);
    412 	if (!tmp) {
    413 		fprintf(stderr, "%s: malloc failed: %s\n", argv[0], strerror(errno));
    414 		goto err;
    415 	}
    416 
    417 	rc = sprintf(tmp, "%sXXXXXX", stack_path);
    418 	if (rc < 0) {
    419 		fprintf(stderr, "%s: sprintf failed\n", argv[0]);
    420 		goto err;
    421 	}
    422 
    423 	fd  = mkstemp(tmp);
    424 	if (fd < 0) {
    425 		fprintf(stderr, "%s: mkstemp %s failed: %s\n", argv[0], tmp, strerror(errno));
    426 		goto err;
    427 	}
    428 
    429 	rc = fchmod(fd, buf.st_mode);
    430 	if (rc < 0) {
    431 		fprintf(stderr, "%s: fchmod %s failed: %s\n", argv[0], tmp, strerror(errno));
    432 		goto err_unlink;
    433 	}
    434 
    435 	rc = write_binary_file(data, fd, do_write_precompregex);
    436 	if (rc < 0) {
    437 		fprintf(stderr, "%s: write_binary_file %s failed\n", argv[0], tmp);
    438 		goto err_unlink;
    439 	}
    440 
    441 	rc = rename(tmp, stack_path);
    442 	if (rc < 0) {
    443 		fprintf(stderr, "%s: rename %s -> %s failed: %s\n", argv[0], tmp, stack_path, strerror(errno));
    444 		goto err_unlink;
    445 	}
    446 
    447 	rc = 0;
    448 out:
    449 	if (policy_fp)
    450 		fclose(policy_fp);
    451 
    452 	free_specs(data);
    453 	free(rec);
    454 	free(data);
    455 	free(tmp);
    456 	return rc;
    457 
    458 err_unlink:
    459 	unlink(tmp);
    460 err:
    461 	rc = -1;
    462 	goto out;
    463 }
    464