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 17const char *policy_file; 18static int ctx_err; 19 20static 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 32static 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 } 62out: 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 */ 95static 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(§ion_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(§ion_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(§ion_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(§ion_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; 249out: 250 fclose(bin_file); 251 return rc; 252err: 253 rc = -1; 254 goto out; 255} 256 257static 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 281static 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 296int 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; 425out: 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 435err_unlink: 436 unlink(tmp); 437err: 438 rc = -1; 439 goto out; 440} 441