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(§ion_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(§ion_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(§ion_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(§ion_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(§ion_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