1 #ifndef _SELABEL_FILE_H_ 2 #define _SELABEL_FILE_H_ 3 4 #include <sys/stat.h> 5 6 #include "callbacks.h" 7 #include "label_internal.h" 8 9 #define SELINUX_MAGIC_COMPILED_FCONTEXT 0xf97cff8a 10 11 /* Version specific changes */ 12 #define SELINUX_COMPILED_FCONTEXT_NOPCRE_VERS 1 13 #define SELINUX_COMPILED_FCONTEXT_PCRE_VERS 2 14 #define SELINUX_COMPILED_FCONTEXT_MODE 3 15 #define SELINUX_COMPILED_FCONTEXT_PREFIX_LEN 4 16 17 #define SELINUX_COMPILED_FCONTEXT_MAX_VERS SELINUX_COMPILED_FCONTEXT_PREFIX_LEN 18 19 /* Prior to version 8.20, libpcre did not have pcre_free_study() */ 20 #if (PCRE_MAJOR < 8 || (PCRE_MAJOR == 8 && PCRE_MINOR < 20)) 21 #define pcre_free_study pcre_free 22 #endif 23 24 /* A file security context specification. */ 25 struct spec { 26 struct selabel_lookup_rec lr; /* holds contexts for lookup result */ 27 char *regex_str; /* regular expession string for diagnostics */ 28 char *type_str; /* type string for diagnostic messages */ 29 pcre *regex; /* compiled regular expression */ 30 union { 31 pcre_extra *sd; /* pointer to extra compiled stuff */ 32 pcre_extra lsd; /* used to hold the mmap'd version */ 33 }; 34 mode_t mode; /* mode format value */ 35 int matches; /* number of matching pathnames */ 36 int stem_id; /* indicates which stem-compression item */ 37 char hasMetaChars; /* regular expression has meta-chars */ 38 char regcomp; /* regex_str has been compiled to regex */ 39 char from_mmap; /* this spec is from an mmap of the data */ 40 size_t prefix_len; /* length of fixed path prefix */ 41 }; 42 43 /* A regular expression stem */ 44 struct stem { 45 char *buf; 46 int len; 47 char from_mmap; 48 }; 49 50 /* Where we map the file in during selabel_open() */ 51 struct mmap_area { 52 void *addr; /* Start addr + len used to release memory at close */ 53 size_t len; 54 void *next_addr; /* Incremented by next_entry() */ 55 size_t next_len; /* Decremented by next_entry() */ 56 struct mmap_area *next; 57 }; 58 59 /* Our stored configuration */ 60 struct saved_data { 61 /* 62 * The array of specifications, initially in the same order as in 63 * the specification file. Sorting occurs based on hasMetaChars. 64 */ 65 struct spec *spec_arr; 66 unsigned int nspec; 67 unsigned int alloc_specs; 68 69 /* 70 * The array of regular expression stems. 71 */ 72 struct stem *stem_arr; 73 int num_stems; 74 int alloc_stems; 75 struct mmap_area *mmap_areas; 76 }; 77 78 static inline pcre_extra *get_pcre_extra(struct spec *spec) 79 { 80 if (spec->from_mmap) 81 return &spec->lsd; 82 else 83 return spec->sd; 84 } 85 86 static inline mode_t string_to_mode(char *mode) 87 { 88 size_t len; 89 90 if (!mode) 91 return 0; 92 len = strlen(mode); 93 if (mode[0] != '-' || len != 2) 94 return -1; 95 switch (mode[1]) { 96 case 'b': 97 return S_IFBLK; 98 case 'c': 99 return S_IFCHR; 100 case 'd': 101 return S_IFDIR; 102 case 'p': 103 return S_IFIFO; 104 case 'l': 105 return S_IFLNK; 106 case 's': 107 return S_IFSOCK; 108 case '-': 109 return S_IFREG; 110 default: 111 return -1; 112 } 113 /* impossible to get here */ 114 return 0; 115 } 116 117 static inline int grow_specs(struct saved_data *data) 118 { 119 struct spec *specs; 120 size_t new_specs, total_specs; 121 122 if (data->nspec < data->alloc_specs) 123 return 0; 124 125 new_specs = data->nspec + 16; 126 total_specs = data->nspec + new_specs; 127 128 specs = realloc(data->spec_arr, total_specs * sizeof(*specs)); 129 if (!specs) { 130 perror("realloc"); 131 return -1; 132 } 133 134 /* blank the new entries */ 135 memset(&specs[data->nspec], 0, new_specs * sizeof(*specs)); 136 137 data->spec_arr = specs; 138 data->alloc_specs = total_specs; 139 return 0; 140 } 141 142 /* Determine if the regular expression specification has any meta characters. */ 143 static inline void spec_hasMetaChars(struct spec *spec) 144 { 145 char *c; 146 int len; 147 char *end; 148 149 c = spec->regex_str; 150 len = strlen(spec->regex_str); 151 end = c + len; 152 153 spec->hasMetaChars = 0; 154 spec->prefix_len = len; 155 156 /* Look at each character in the RE specification string for a 157 * meta character. Return when any meta character reached. */ 158 while (c < end) { 159 switch (*c) { 160 case '.': 161 case '^': 162 case '$': 163 case '?': 164 case '*': 165 case '+': 166 case '|': 167 case '[': 168 case '(': 169 case '{': 170 spec->hasMetaChars = 1; 171 spec->prefix_len = c - spec->regex_str; 172 return; 173 case '\\': /* skip the next character */ 174 c++; 175 break; 176 default: 177 break; 178 179 } 180 c++; 181 } 182 } 183 184 /* Move exact pathname specifications to the end. */ 185 static inline int sort_specs(struct saved_data *data) 186 { 187 struct spec *spec_copy; 188 struct spec spec; 189 unsigned int i; 190 int front, back; 191 size_t len = sizeof(*spec_copy); 192 193 spec_copy = malloc(len * data->nspec); 194 if (!spec_copy) 195 return -1; 196 197 /* first move the exact pathnames to the back */ 198 front = 0; 199 back = data->nspec - 1; 200 for (i = 0; i < data->nspec; i++) { 201 if (data->spec_arr[i].hasMetaChars) 202 memcpy(&spec_copy[front++], &data->spec_arr[i], len); 203 else 204 memcpy(&spec_copy[back--], &data->spec_arr[i], len); 205 } 206 207 /* 208 * now the exact pathnames are at the end, but they are in the reverse 209 * order. Since 'front' is now the first of the 'exact' we can run 210 * that part of the array switching the front and back element. 211 */ 212 back = data->nspec - 1; 213 while (front < back) { 214 /* save the front */ 215 memcpy(&spec, &spec_copy[front], len); 216 /* move the back to the front */ 217 memcpy(&spec_copy[front], &spec_copy[back], len); 218 /* put the old front in the back */ 219 memcpy(&spec_copy[back], &spec, len); 220 front++; 221 back--; 222 } 223 224 free(data->spec_arr); 225 data->spec_arr = spec_copy; 226 227 return 0; 228 } 229 230 /* Return the length of the text that can be considered the stem, returns 0 231 * if there is no identifiable stem */ 232 static inline int get_stem_from_spec(const char *const buf) 233 { 234 const char *tmp = strchr(buf + 1, '/'); 235 const char *ind; 236 237 if (!tmp) 238 return 0; 239 240 for (ind = buf; ind < tmp; ind++) { 241 if (strchr(".^$?*+|[({", (int)*ind)) 242 return 0; 243 } 244 return tmp - buf; 245 } 246 247 /* 248 * return the stemid given a string and a length 249 */ 250 static inline int find_stem(struct saved_data *data, const char *buf, 251 int stem_len) 252 { 253 int i; 254 255 for (i = 0; i < data->num_stems; i++) { 256 if (stem_len == data->stem_arr[i].len && 257 !strncmp(buf, data->stem_arr[i].buf, stem_len)) 258 return i; 259 } 260 261 return -1; 262 } 263 264 /* returns the index of the new stored object */ 265 static inline int store_stem(struct saved_data *data, char *buf, int stem_len) 266 { 267 int num = data->num_stems; 268 269 if (data->alloc_stems == num) { 270 struct stem *tmp_arr; 271 272 data->alloc_stems = data->alloc_stems * 2 + 16; 273 tmp_arr = realloc(data->stem_arr, 274 sizeof(*tmp_arr) * data->alloc_stems); 275 if (!tmp_arr) 276 return -1; 277 data->stem_arr = tmp_arr; 278 } 279 data->stem_arr[num].len = stem_len; 280 data->stem_arr[num].buf = buf; 281 data->stem_arr[num].from_mmap = 0; 282 data->num_stems++; 283 284 return num; 285 } 286 287 /* find the stem of a file spec, returns the index into stem_arr for a new 288 * or existing stem, (or -1 if there is no possible stem - IE for a file in 289 * the root directory or a regex that is too complex for us). */ 290 static inline int find_stem_from_spec(struct saved_data *data, const char *buf) 291 { 292 int stem_len = get_stem_from_spec(buf); 293 int stemid; 294 char *stem; 295 296 if (!stem_len) 297 return -1; 298 299 stemid = find_stem(data, buf, stem_len); 300 if (stemid >= 0) 301 return stemid; 302 303 /* not found, allocate a new one */ 304 stem = strndup(buf, stem_len); 305 if (!stem) 306 return -1; 307 308 return store_stem(data, stem, stem_len); 309 } 310 311 /* This will always check for buffer over-runs and either read the next entry 312 * if buf != NULL or skip over the entry (as these areas are mapped in the 313 * current buffer). */ 314 static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes) 315 { 316 if (bytes > fp->next_len) 317 return -1; 318 319 if (buf) 320 memcpy(buf, fp->next_addr, bytes); 321 322 fp->next_addr = (char *)fp->next_addr + bytes; 323 fp->next_len -= bytes; 324 return 0; 325 } 326 327 static inline int compile_regex(struct saved_data *data, struct spec *spec, 328 const char **errbuf) 329 { 330 const char *tmperrbuf; 331 char *reg_buf, *anchored_regex, *cp; 332 struct stem *stem_arr = data->stem_arr; 333 size_t len; 334 int erroff; 335 336 if (spec->regcomp) 337 return 0; /* already done */ 338 339 /* Skip the fixed stem. */ 340 reg_buf = spec->regex_str; 341 if (spec->stem_id >= 0) 342 reg_buf += stem_arr[spec->stem_id].len; 343 344 /* Anchor the regular expression. */ 345 len = strlen(reg_buf); 346 cp = anchored_regex = malloc(len + 3); 347 if (!anchored_regex) 348 return -1; 349 350 /* Create ^...$ regexp. */ 351 *cp++ = '^'; 352 memcpy(cp, reg_buf, len); 353 cp += len; 354 *cp++ = '$'; 355 *cp = '\0'; 356 357 /* Compile the regular expression. */ 358 spec->regex = pcre_compile(anchored_regex, PCRE_DOTALL, &tmperrbuf, 359 &erroff, NULL); 360 free(anchored_regex); 361 if (!spec->regex) { 362 if (errbuf) 363 *errbuf = tmperrbuf; 364 return -1; 365 } 366 367 spec->sd = pcre_study(spec->regex, 0, &tmperrbuf); 368 if (!spec->sd && tmperrbuf) { 369 if (errbuf) 370 *errbuf = tmperrbuf; 371 return -1; 372 } 373 374 /* Done. */ 375 spec->regcomp = 1; 376 377 return 0; 378 } 379 380 /* This service is used by label_file.c process_file() and 381 * utils/sefcontext_compile.c */ 382 static inline int process_line(struct selabel_handle *rec, 383 const char *path, const char *prefix, 384 char *line_buf, unsigned lineno) 385 { 386 int items, len, rc; 387 char *regex = NULL, *type = NULL, *context = NULL; 388 struct saved_data *data = (struct saved_data *)rec->data; 389 struct spec *spec_arr; 390 unsigned int nspec = data->nspec; 391 const char *errbuf = NULL; 392 393 items = read_spec_entries(line_buf, 3, ®ex, &type, &context); 394 if (items <= 0) 395 return items; 396 397 if (items < 2) { 398 COMPAT_LOG(SELINUX_ERROR, 399 "%s: line %u is missing fields\n", path, 400 lineno); 401 if (items == 1) 402 free(regex); 403 errno = EINVAL; 404 return -1; 405 } else if (items == 2) { 406 /* The type field is optional. */ 407 context = type; 408 type = 0; 409 } 410 411 len = get_stem_from_spec(regex); 412 if (len && prefix && strncmp(prefix, regex, len)) { 413 /* Stem of regex does not match requested prefix, discard. */ 414 free(regex); 415 free(type); 416 free(context); 417 return 0; 418 } 419 420 rc = grow_specs(data); 421 if (rc) 422 return rc; 423 424 spec_arr = data->spec_arr; 425 426 /* process and store the specification in spec. */ 427 spec_arr[nspec].stem_id = find_stem_from_spec(data, regex); 428 spec_arr[nspec].regex_str = regex; 429 430 spec_arr[nspec].type_str = type; 431 spec_arr[nspec].mode = 0; 432 433 spec_arr[nspec].lr.ctx_raw = context; 434 435 /* 436 * bump data->nspecs to cause closef() to cover it in its free 437 * but do not bump nspec since it's used below. 438 */ 439 data->nspec++; 440 441 if (rec->validating && 442 compile_regex(data, &spec_arr[nspec], &errbuf)) { 443 COMPAT_LOG(SELINUX_ERROR, 444 "%s: line %u has invalid regex %s: %s\n", 445 path, lineno, regex, 446 (errbuf ? errbuf : "out of memory")); 447 errno = EINVAL; 448 return -1; 449 } 450 451 if (type) { 452 mode_t mode = string_to_mode(type); 453 454 if (mode == (mode_t)-1) { 455 COMPAT_LOG(SELINUX_ERROR, 456 "%s: line %u has invalid file type %s\n", 457 path, lineno, type); 458 errno = EINVAL; 459 return -1; 460 } 461 spec_arr[nspec].mode = mode; 462 } 463 464 /* Determine if specification has 465 * any meta characters in the RE */ 466 spec_hasMetaChars(&spec_arr[nspec]); 467 468 if (strcmp(context, "<<none>>") && rec->validating) 469 compat_validate(rec, &spec_arr[nspec].lr, path, lineno); 470 471 return 0; 472 } 473 474 #endif /* _SELABEL_FILE_H_ */ 475