Joshua Brindle | 13cd4c8 | 2008-08-19 15:30:36 -0400 | [diff] [blame] | 1 | /* |
| 2 | * File contexts backend for labeling system |
| 3 | * |
| 4 | * Author : Eamon Walsh <ewalsh@tycho.nsa.gov> |
| 5 | * Author : Stephen Smalley <sds@tycho.nsa.gov> |
| 6 | * |
| 7 | * This library derived in part from setfiles and the setfiles.pl script |
| 8 | * developed by Secure Computing Corporation. |
| 9 | */ |
| 10 | |
| 11 | #include <fcntl.h> |
| 12 | #include <stdarg.h> |
| 13 | #include <string.h> |
| 14 | #include <stdio.h> |
| 15 | #include <stdio_ext.h> |
| 16 | #include <ctype.h> |
| 17 | #include <errno.h> |
| 18 | #include <limits.h> |
| 19 | #include <regex.h> |
| 20 | #include "callbacks.h" |
| 21 | #include "label_internal.h" |
| 22 | |
| 23 | /* |
| 24 | * Internals, mostly moved over from matchpathcon.c |
| 25 | */ |
| 26 | |
| 27 | /* A file security context specification. */ |
| 28 | typedef struct spec { |
| 29 | struct selabel_lookup_rec lr; /* holds contexts for lookup result */ |
| 30 | char *regex_str; /* regular expession string for diagnostics */ |
| 31 | char *type_str; /* type string for diagnostic messages */ |
| 32 | regex_t regex; /* compiled regular expression */ |
| 33 | char regcomp; /* regex_str has been compiled to regex */ |
| 34 | mode_t mode; /* mode format value */ |
| 35 | int matches; /* number of matching pathnames */ |
| 36 | int hasMetaChars; /* regular expression has meta-chars */ |
| 37 | int stem_id; /* indicates which stem-compression item */ |
| 38 | } spec_t; |
| 39 | |
| 40 | /* A regular expression stem */ |
| 41 | typedef struct stem { |
| 42 | char *buf; |
| 43 | int len; |
| 44 | } stem_t; |
| 45 | |
| 46 | /* Our stored configuration */ |
| 47 | struct saved_data { |
| 48 | /* |
| 49 | * The array of specifications, initially in the same order as in |
| 50 | * the specification file. Sorting occurs based on hasMetaChars. |
| 51 | */ |
| 52 | spec_t *spec_arr; |
| 53 | unsigned int nspec; |
| 54 | unsigned int ncomp; |
| 55 | |
| 56 | /* |
| 57 | * The array of regular expression stems. |
| 58 | */ |
| 59 | stem_t *stem_arr; |
| 60 | int num_stems; |
| 61 | int alloc_stems; |
| 62 | }; |
| 63 | |
| 64 | /* Return the length of the text that can be considered the stem, returns 0 |
| 65 | * if there is no identifiable stem */ |
| 66 | static int get_stem_from_spec(const char *const buf) |
| 67 | { |
| 68 | const char *tmp = strchr(buf + 1, '/'); |
| 69 | const char *ind; |
| 70 | |
| 71 | if (!tmp) |
| 72 | return 0; |
| 73 | |
| 74 | for (ind = buf; ind < tmp; ind++) { |
| 75 | if (strchr(".^$?*+|[({", (int)*ind)) |
| 76 | return 0; |
| 77 | } |
| 78 | return tmp - buf; |
| 79 | } |
| 80 | |
| 81 | /* return the length of the text that is the stem of a file name */ |
| 82 | static int get_stem_from_file_name(const char *const buf) |
| 83 | { |
| 84 | const char *tmp = strchr(buf + 1, '/'); |
| 85 | |
| 86 | if (!tmp) |
| 87 | return 0; |
| 88 | return tmp - buf; |
| 89 | } |
| 90 | |
| 91 | /* find the stem of a file spec, returns the index into stem_arr for a new |
| 92 | * or existing stem, (or -1 if there is no possible stem - IE for a file in |
| 93 | * the root directory or a regex that is too complex for us). */ |
| 94 | static int find_stem_from_spec(struct saved_data *data, const char *buf) |
| 95 | { |
| 96 | int i, num = data->num_stems; |
| 97 | int stem_len = get_stem_from_spec(buf); |
| 98 | |
| 99 | if (!stem_len) |
| 100 | return -1; |
| 101 | for (i = 0; i < num; i++) { |
| 102 | if (stem_len == data->stem_arr[i].len |
| 103 | && !strncmp(buf, data->stem_arr[i].buf, stem_len)) |
| 104 | return i; |
| 105 | } |
| 106 | if (data->alloc_stems == num) { |
| 107 | stem_t *tmp_arr; |
| 108 | data->alloc_stems = data->alloc_stems * 2 + 16; |
| 109 | tmp_arr = realloc(data->stem_arr, |
| 110 | sizeof(stem_t) * data->alloc_stems); |
| 111 | if (!tmp_arr) |
| 112 | return -1; |
| 113 | data->stem_arr = tmp_arr; |
| 114 | } |
| 115 | data->stem_arr[num].len = stem_len; |
| 116 | data->stem_arr[num].buf = malloc(stem_len + 1); |
| 117 | if (!data->stem_arr[num].buf) |
| 118 | return -1; |
| 119 | memcpy(data->stem_arr[num].buf, buf, stem_len); |
| 120 | data->stem_arr[num].buf[stem_len] = '\0'; |
| 121 | data->num_stems++; |
| 122 | buf += stem_len; |
| 123 | return num; |
| 124 | } |
| 125 | |
| 126 | /* find the stem of a file name, returns the index into stem_arr (or -1 if |
| 127 | * there is no match - IE for a file in the root directory or a regex that is |
| 128 | * too complex for us). Makes buf point to the text AFTER the stem. */ |
| 129 | static int find_stem_from_file(struct saved_data *data, const char **buf) |
| 130 | { |
| 131 | int i; |
| 132 | int stem_len = get_stem_from_file_name(*buf); |
| 133 | |
| 134 | if (!stem_len) |
| 135 | return -1; |
| 136 | for (i = 0; i < data->num_stems; i++) { |
| 137 | if (stem_len == data->stem_arr[i].len |
| 138 | && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) { |
| 139 | *buf += stem_len; |
| 140 | return i; |
| 141 | } |
| 142 | } |
| 143 | return -1; |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | * Warn about duplicate specifications. |
| 148 | */ |
| 149 | static int nodups_specs(struct saved_data *data, const char *path) |
| 150 | { |
| 151 | int rc = 0; |
| 152 | unsigned int ii, jj; |
| 153 | struct spec *curr_spec, *spec_arr = data->spec_arr; |
| 154 | |
| 155 | for (ii = 0; ii < data->nspec; ii++) { |
| 156 | curr_spec = &spec_arr[ii]; |
| 157 | for (jj = ii + 1; jj < data->nspec; jj++) { |
| 158 | if ((!strcmp |
| 159 | (spec_arr[jj].regex_str, curr_spec->regex_str)) |
| 160 | && (!spec_arr[jj].mode || !curr_spec->mode |
| 161 | || spec_arr[jj].mode == curr_spec->mode)) { |
| 162 | rc = -1; |
| 163 | errno = EINVAL; |
| 164 | if (strcmp |
| 165 | (spec_arr[jj].lr.ctx_raw, |
| 166 | curr_spec->lr.ctx_raw)) { |
| 167 | COMPAT_LOG |
| 168 | (SELINUX_ERROR, |
| 169 | "%s: Multiple different specifications for %s (%s and %s).\n", |
| 170 | path, curr_spec->regex_str, |
| 171 | spec_arr[jj].lr.ctx_raw, |
| 172 | curr_spec->lr.ctx_raw); |
| 173 | } else { |
| 174 | COMPAT_LOG |
| 175 | (SELINUX_ERROR, |
| 176 | "%s: Multiple same specifications for %s.\n", |
| 177 | path, curr_spec->regex_str); |
| 178 | } |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | return rc; |
| 183 | } |
| 184 | |
| 185 | /* Determine if the regular expression specification has any meta characters. */ |
| 186 | static void spec_hasMetaChars(struct spec *spec) |
| 187 | { |
| 188 | char *c; |
| 189 | int len; |
| 190 | char *end; |
| 191 | |
| 192 | c = spec->regex_str; |
| 193 | len = strlen(spec->regex_str); |
| 194 | end = c + len; |
| 195 | |
| 196 | spec->hasMetaChars = 0; |
| 197 | |
| 198 | /* Look at each character in the RE specification string for a |
| 199 | * meta character. Return when any meta character reached. */ |
| 200 | while (c != end) { |
| 201 | switch (*c) { |
| 202 | case '.': |
| 203 | case '^': |
| 204 | case '$': |
| 205 | case '?': |
| 206 | case '*': |
| 207 | case '+': |
| 208 | case '|': |
| 209 | case '[': |
| 210 | case '(': |
| 211 | case '{': |
| 212 | spec->hasMetaChars = 1; |
| 213 | return; |
| 214 | case '\\': /* skip the next character */ |
| 215 | c++; |
| 216 | break; |
| 217 | default: |
| 218 | break; |
| 219 | |
| 220 | } |
| 221 | c++; |
| 222 | } |
| 223 | return; |
| 224 | } |
| 225 | |
| 226 | static int compile_regex(struct saved_data *data, spec_t *spec, char **errbuf) |
| 227 | { |
| 228 | char *reg_buf, *anchored_regex, *cp; |
| 229 | stem_t *stem_arr = data->stem_arr; |
| 230 | size_t len; |
| 231 | int regerr; |
| 232 | |
| 233 | if (spec->regcomp) |
| 234 | return 0; /* already done */ |
| 235 | |
| 236 | data->ncomp++; /* how many compiled regexes required */ |
| 237 | |
| 238 | /* Skip the fixed stem. */ |
| 239 | reg_buf = spec->regex_str; |
| 240 | if (spec->stem_id >= 0) |
| 241 | reg_buf += stem_arr[spec->stem_id].len; |
| 242 | |
| 243 | /* Anchor the regular expression. */ |
| 244 | len = strlen(reg_buf); |
| 245 | cp = anchored_regex = malloc(len + 3); |
| 246 | if (!anchored_regex) |
| 247 | return -1; |
| 248 | /* Create ^...$ regexp. */ |
| 249 | *cp++ = '^'; |
| 250 | cp = mempcpy(cp, reg_buf, len); |
| 251 | *cp++ = '$'; |
| 252 | *cp = '\0'; |
| 253 | |
| 254 | /* Compile the regular expression. */ |
| 255 | regerr = regcomp(&spec->regex, anchored_regex, |
| 256 | REG_EXTENDED | REG_NOSUB); |
| 257 | if (regerr != 0) { |
| 258 | size_t errsz = 0; |
| 259 | errsz = regerror(regerr, &spec->regex, NULL, 0); |
| 260 | if (errsz && errbuf) |
| 261 | *errbuf = malloc(errsz); |
| 262 | if (errbuf && *errbuf) |
| 263 | (void)regerror(regerr, &spec->regex, |
| 264 | *errbuf, errsz); |
| 265 | |
| 266 | free(anchored_regex); |
| 267 | return -1; |
| 268 | } |
| 269 | free(anchored_regex); |
| 270 | |
| 271 | /* Done. */ |
| 272 | spec->regcomp = 1; |
| 273 | |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | |
| 278 | static int process_line(struct selabel_handle *rec, |
| 279 | const char *path, const char *prefix, |
| 280 | char *line_buf, int pass, unsigned lineno) |
| 281 | { |
| 282 | int items, len; |
| 283 | char *buf_p, *regex, *type, *context; |
| 284 | struct saved_data *data = (struct saved_data *)rec->data; |
| 285 | spec_t *spec_arr = data->spec_arr; |
| 286 | unsigned int nspec = data->nspec; |
| 287 | |
| 288 | len = strlen(line_buf); |
| 289 | if (line_buf[len - 1] == '\n') |
| 290 | line_buf[len - 1] = 0; |
| 291 | buf_p = line_buf; |
| 292 | while (isspace(*buf_p)) |
| 293 | buf_p++; |
| 294 | /* Skip comment lines and empty lines. */ |
| 295 | if (*buf_p == '#' || *buf_p == 0) |
| 296 | return 0; |
| 297 | items = sscanf(line_buf, "%as %as %as", ®ex, &type, &context); |
| 298 | if (items < 2) { |
| 299 | COMPAT_LOG(SELINUX_WARNING, |
| 300 | "%s: line %d is missing fields, skipping\n", path, |
| 301 | lineno); |
Hiroshi Shinji | a4af847 | 2009-04-11 14:41:51 -0400 | [diff] [blame^] | 302 | if (items == 1) |
| 303 | free(regex); |
Joshua Brindle | 13cd4c8 | 2008-08-19 15:30:36 -0400 | [diff] [blame] | 304 | return 0; |
| 305 | } else if (items == 2) { |
| 306 | /* The type field is optional. */ |
| 307 | free(context); |
| 308 | context = type; |
| 309 | type = 0; |
| 310 | } |
| 311 | |
| 312 | len = get_stem_from_spec(regex); |
| 313 | if (len && prefix && strncmp(prefix, regex, len)) { |
| 314 | /* Stem of regex does not match requested prefix, discard. */ |
| 315 | free(regex); |
| 316 | free(type); |
| 317 | free(context); |
| 318 | return 0; |
| 319 | } |
| 320 | |
| 321 | if (pass == 1) { |
| 322 | /* On the second pass, process and store the specification in spec. */ |
| 323 | char *errbuf = NULL; |
| 324 | spec_arr[nspec].stem_id = find_stem_from_spec(data, regex); |
| 325 | spec_arr[nspec].regex_str = regex; |
| 326 | if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) { |
| 327 | COMPAT_LOG(SELINUX_WARNING, |
| 328 | "%s: line %d has invalid regex %s: %s\n", |
| 329 | path, lineno, regex, |
| 330 | (errbuf ? errbuf : "out of memory")); |
| 331 | } |
| 332 | |
| 333 | /* Convert the type string to a mode format */ |
| 334 | spec_arr[nspec].type_str = type; |
| 335 | spec_arr[nspec].mode = 0; |
| 336 | if (!type) |
| 337 | goto skip_type; |
| 338 | len = strlen(type); |
| 339 | if (type[0] != '-' || len != 2) { |
| 340 | COMPAT_LOG(SELINUX_WARNING, |
| 341 | "%s: line %d has invalid file type %s\n", |
| 342 | path, lineno, type); |
| 343 | return 0; |
| 344 | } |
| 345 | switch (type[1]) { |
| 346 | case 'b': |
| 347 | spec_arr[nspec].mode = S_IFBLK; |
| 348 | break; |
| 349 | case 'c': |
| 350 | spec_arr[nspec].mode = S_IFCHR; |
| 351 | break; |
| 352 | case 'd': |
| 353 | spec_arr[nspec].mode = S_IFDIR; |
| 354 | break; |
| 355 | case 'p': |
| 356 | spec_arr[nspec].mode = S_IFIFO; |
| 357 | break; |
| 358 | case 'l': |
| 359 | spec_arr[nspec].mode = S_IFLNK; |
| 360 | break; |
| 361 | case 's': |
| 362 | spec_arr[nspec].mode = S_IFSOCK; |
| 363 | break; |
| 364 | case '-': |
| 365 | spec_arr[nspec].mode = S_IFREG; |
| 366 | break; |
| 367 | default: |
| 368 | COMPAT_LOG(SELINUX_WARNING, |
| 369 | "%s: line %d has invalid file type %s\n", |
| 370 | path, lineno, type); |
| 371 | return 0; |
| 372 | } |
| 373 | |
| 374 | skip_type: |
| 375 | spec_arr[nspec].lr.ctx_raw = context; |
| 376 | |
| 377 | /* Determine if specification has |
| 378 | * any meta characters in the RE */ |
| 379 | spec_hasMetaChars(&spec_arr[nspec]); |
| 380 | |
| 381 | if (strcmp(context, "<<none>>") && rec->validating) |
| 382 | compat_validate(rec, &spec_arr[nspec].lr, path, lineno); |
| 383 | } |
| 384 | |
| 385 | data->nspec = ++nspec; |
| 386 | if (pass == 0) { |
| 387 | free(regex); |
| 388 | if (type) |
| 389 | free(type); |
| 390 | free(context); |
| 391 | } |
| 392 | return 0; |
| 393 | } |
| 394 | |
| 395 | static int init(struct selabel_handle *rec, struct selinux_opt *opts, |
| 396 | unsigned n) |
| 397 | { |
| 398 | struct saved_data *data = (struct saved_data *)rec->data; |
| 399 | const char *path = NULL; |
| 400 | const char *prefix = NULL; |
| 401 | FILE *fp; |
| 402 | FILE *localfp = NULL; |
| 403 | FILE *homedirfp = NULL; |
| 404 | char local_path[PATH_MAX + 1]; |
| 405 | char homedir_path[PATH_MAX + 1]; |
| 406 | char *line_buf = NULL; |
| 407 | size_t line_len = 0; |
| 408 | unsigned int lineno, pass, i, j, maxnspec; |
| 409 | spec_t *spec_copy = NULL; |
| 410 | int status = -1, baseonly = 0; |
| 411 | struct stat sb; |
| 412 | |
| 413 | /* Process arguments */ |
| 414 | while (n--) |
| 415 | switch(opts[n].type) { |
| 416 | case SELABEL_OPT_PATH: |
| 417 | path = opts[n].value; |
| 418 | break; |
| 419 | case SELABEL_OPT_SUBSET: |
| 420 | prefix = opts[n].value; |
| 421 | break; |
| 422 | case SELABEL_OPT_BASEONLY: |
| 423 | baseonly = !!opts[n].value; |
| 424 | break; |
| 425 | } |
| 426 | |
| 427 | /* Open the specification file. */ |
| 428 | if (!path) |
| 429 | path = selinux_file_context_path(); |
| 430 | if ((fp = fopen(path, "r")) == NULL) |
| 431 | return -1; |
| 432 | __fsetlocking(fp, FSETLOCKING_BYCALLER); |
| 433 | |
| 434 | if (fstat(fileno(fp), &sb) < 0) |
| 435 | return -1; |
| 436 | if (!S_ISREG(sb.st_mode)) { |
| 437 | errno = EINVAL; |
| 438 | return -1; |
| 439 | } |
| 440 | |
| 441 | if (!baseonly) { |
| 442 | snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs", |
| 443 | path); |
| 444 | homedirfp = fopen(homedir_path, "r"); |
| 445 | if (homedirfp != NULL) |
| 446 | __fsetlocking(homedirfp, FSETLOCKING_BYCALLER); |
| 447 | |
| 448 | snprintf(local_path, sizeof(local_path), "%s.local", path); |
| 449 | localfp = fopen(local_path, "r"); |
| 450 | if (localfp != NULL) |
| 451 | __fsetlocking(localfp, FSETLOCKING_BYCALLER); |
| 452 | } |
| 453 | |
| 454 | /* |
| 455 | * Perform two passes over the specification file. |
| 456 | * The first pass counts the number of specifications and |
| 457 | * performs simple validation of the input. At the end |
| 458 | * of the first pass, the spec array is allocated. |
| 459 | * The second pass performs detailed validation of the input |
| 460 | * and fills in the spec array. |
| 461 | */ |
| 462 | maxnspec = UINT_MAX / sizeof(spec_t); |
| 463 | for (pass = 0; pass < 2; pass++) { |
| 464 | lineno = 0; |
| 465 | data->nspec = 0; |
| 466 | data->ncomp = 0; |
| 467 | while (getline(&line_buf, &line_len, fp) > 0 |
| 468 | && data->nspec < maxnspec) { |
| 469 | if (process_line(rec, path, prefix, line_buf, |
| 470 | pass, ++lineno) != 0) |
| 471 | goto finish; |
| 472 | } |
| 473 | if (pass == 1) { |
| 474 | status = nodups_specs(data, path); |
| 475 | if (status) |
| 476 | goto finish; |
| 477 | } |
| 478 | lineno = 0; |
| 479 | if (homedirfp) |
| 480 | while (getline(&line_buf, &line_len, homedirfp) > 0 |
| 481 | && data->nspec < maxnspec) { |
| 482 | if (process_line |
| 483 | (rec, homedir_path, prefix, |
| 484 | line_buf, pass, ++lineno) != 0) |
| 485 | goto finish; |
| 486 | } |
| 487 | |
| 488 | lineno = 0; |
| 489 | if (localfp) |
| 490 | while (getline(&line_buf, &line_len, localfp) > 0 |
| 491 | && data->nspec < maxnspec) { |
| 492 | if (process_line |
| 493 | (rec, local_path, prefix, line_buf, |
| 494 | pass, ++lineno) != 0) |
| 495 | goto finish; |
| 496 | } |
| 497 | |
| 498 | if (pass == 0) { |
| 499 | if (data->nspec == 0) { |
| 500 | status = 0; |
| 501 | goto finish; |
| 502 | } |
| 503 | if (NULL == (data->spec_arr = |
| 504 | malloc(sizeof(spec_t) * data->nspec))) |
| 505 | goto finish; |
| 506 | memset(data->spec_arr, 0, sizeof(spec_t)*data->nspec); |
| 507 | maxnspec = data->nspec; |
| 508 | rewind(fp); |
| 509 | if (homedirfp) |
| 510 | rewind(homedirfp); |
| 511 | if (localfp) |
| 512 | rewind(localfp); |
| 513 | } |
| 514 | } |
| 515 | free(line_buf); |
| 516 | |
| 517 | /* Move exact pathname specifications to the end. */ |
| 518 | spec_copy = malloc(sizeof(spec_t) * data->nspec); |
| 519 | if (!spec_copy) |
| 520 | goto finish; |
| 521 | j = 0; |
| 522 | for (i = 0; i < data->nspec; i++) |
| 523 | if (data->spec_arr[i].hasMetaChars) |
| 524 | memcpy(&spec_copy[j++], |
| 525 | &data->spec_arr[i], sizeof(spec_t)); |
| 526 | for (i = 0; i < data->nspec; i++) |
| 527 | if (!data->spec_arr[i].hasMetaChars) |
| 528 | memcpy(&spec_copy[j++], |
| 529 | &data->spec_arr[i], sizeof(spec_t)); |
| 530 | free(data->spec_arr); |
| 531 | data->spec_arr = spec_copy; |
| 532 | |
| 533 | status = 0; |
| 534 | finish: |
| 535 | fclose(fp); |
| 536 | if (data->spec_arr != spec_copy) |
| 537 | free(data->spec_arr); |
| 538 | if (homedirfp) |
| 539 | fclose(homedirfp); |
| 540 | if (localfp) |
| 541 | fclose(localfp); |
| 542 | return status; |
| 543 | } |
| 544 | |
| 545 | /* |
| 546 | * Backend interface routines |
| 547 | */ |
| 548 | static void close(struct selabel_handle *rec) |
| 549 | { |
| 550 | struct saved_data *data = (struct saved_data *)rec->data; |
| 551 | struct spec *spec; |
| 552 | struct stem *stem; |
| 553 | unsigned int i; |
| 554 | |
| 555 | for (i = 0; i < data->nspec; i++) { |
| 556 | spec = &data->spec_arr[i]; |
| 557 | free(spec->regex_str); |
| 558 | free(spec->type_str); |
| 559 | free(spec->lr.ctx_raw); |
| 560 | free(spec->lr.ctx_trans); |
| 561 | regfree(&spec->regex); |
| 562 | } |
| 563 | |
| 564 | for (i = 0; i < (unsigned int)data->num_stems; i++) { |
| 565 | stem = &data->stem_arr[i]; |
| 566 | free(stem->buf); |
| 567 | } |
| 568 | |
| 569 | if (data->spec_arr) |
| 570 | free(data->spec_arr); |
| 571 | if (data->stem_arr) |
| 572 | free(data->stem_arr); |
| 573 | |
| 574 | free(data); |
| 575 | } |
| 576 | |
| 577 | static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, |
| 578 | const char *key, int type) |
| 579 | { |
| 580 | struct saved_data *data = (struct saved_data *)rec->data; |
| 581 | spec_t *spec_arr = data->spec_arr; |
| 582 | int i, rc, file_stem; |
| 583 | mode_t mode = (mode_t)type; |
| 584 | const char *buf = key; |
| 585 | |
| 586 | if (!data->nspec) { |
| 587 | errno = ENOENT; |
| 588 | return NULL; |
| 589 | } |
| 590 | |
| 591 | file_stem = find_stem_from_file(data, &buf); |
| 592 | mode &= S_IFMT; |
| 593 | |
| 594 | /* |
| 595 | * Check for matching specifications in reverse order, so that |
| 596 | * the last matching specification is used. |
| 597 | */ |
| 598 | for (i = data->nspec - 1; i >= 0; i--) { |
| 599 | /* if the spec in question matches no stem or has the same |
| 600 | * stem as the file AND if the spec in question has no mode |
| 601 | * specified or if the mode matches the file mode then we do |
| 602 | * a regex check */ |
| 603 | if ((spec_arr[i].stem_id == -1 |
| 604 | || spec_arr[i].stem_id == file_stem) |
| 605 | && (!mode || !spec_arr[i].mode |
| 606 | || mode == spec_arr[i].mode)) { |
| 607 | if (compile_regex(data, &spec_arr[i], NULL) < 0) |
| 608 | return NULL; |
| 609 | if (spec_arr[i].stem_id == -1) |
| 610 | rc = regexec(&spec_arr[i].regex, key, 0, 0, 0); |
| 611 | else |
| 612 | rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0); |
| 613 | |
| 614 | if (rc == 0) { |
| 615 | spec_arr[i].matches++; |
| 616 | break; |
| 617 | } |
| 618 | if (rc == REG_NOMATCH) |
| 619 | continue; |
| 620 | /* else it's an error */ |
| 621 | return NULL; |
| 622 | } |
| 623 | } |
| 624 | |
| 625 | if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) { |
| 626 | /* No matching specification. */ |
| 627 | errno = ENOENT; |
| 628 | return NULL; |
| 629 | } |
| 630 | |
| 631 | return &spec_arr[i].lr; |
| 632 | } |
| 633 | |
| 634 | static void stats(struct selabel_handle *rec) |
| 635 | { |
| 636 | struct saved_data *data = (struct saved_data *)rec->data; |
| 637 | unsigned int i, nspec = data->nspec; |
| 638 | spec_t *spec_arr = data->spec_arr; |
| 639 | |
| 640 | for (i = 0; i < nspec; i++) { |
| 641 | if (spec_arr[i].matches == 0) { |
| 642 | if (spec_arr[i].type_str) { |
| 643 | COMPAT_LOG(SELINUX_WARNING, |
| 644 | "Warning! No matches for (%s, %s, %s)\n", |
| 645 | spec_arr[i].regex_str, |
| 646 | spec_arr[i].type_str, |
| 647 | spec_arr[i].lr.ctx_raw); |
| 648 | } else { |
| 649 | COMPAT_LOG(SELINUX_WARNING, |
| 650 | "Warning! No matches for (%s, %s)\n", |
| 651 | spec_arr[i].regex_str, |
| 652 | spec_arr[i].lr.ctx_raw); |
| 653 | } |
| 654 | } |
| 655 | } |
| 656 | } |
| 657 | |
| 658 | int selabel_file_init(struct selabel_handle *rec, struct selinux_opt *opts, |
| 659 | unsigned nopts) |
| 660 | { |
| 661 | struct saved_data *data; |
| 662 | |
| 663 | data = (struct saved_data *)malloc(sizeof(*data)); |
| 664 | if (!data) |
| 665 | return -1; |
| 666 | memset(data, 0, sizeof(*data)); |
| 667 | |
| 668 | rec->data = data; |
| 669 | rec->func_close = &close; |
| 670 | rec->func_stats = &stats; |
| 671 | rec->func_lookup = &lookup; |
| 672 | |
| 673 | return init(rec, opts, nopts); |
| 674 | } |