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