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