1 #include <sys/stat.h> 2 #include <string.h> 3 #include <errno.h> 4 #include <stdio.h> 5 #include "selinux_internal.h" 6 #include "label_internal.h" 7 #include "callbacks.h" 8 #include <limits.h> 9 10 static int (*myinvalidcon) (const char *p, unsigned l, char *c) = NULL; 11 static int (*mycanoncon) (const char *p, unsigned l, char **c) = NULL; 12 13 static void 14 #ifdef __GNUC__ 15 __attribute__ ((format(printf, 1, 2))) 16 #endif 17 default_printf(const char *fmt, ...) 18 { 19 va_list ap; 20 va_start(ap, fmt); 21 vfprintf(stderr, fmt, ap); 22 va_end(ap); 23 } 24 25 void 26 #ifdef __GNUC__ 27 __attribute__ ((format(printf, 1, 2))) 28 #endif 29 (*myprintf) (const char *fmt,...) = &default_printf; 30 int myprintf_compat = 0; 31 32 void set_matchpathcon_printf(void (*f) (const char *fmt, ...)) 33 { 34 myprintf = f ? f : &default_printf; 35 myprintf_compat = 1; 36 } 37 38 int compat_validate(struct selabel_handle *rec, 39 struct selabel_lookup_rec *contexts, 40 const char *path, unsigned lineno) 41 { 42 int rc; 43 char **ctx = &contexts->ctx_raw; 44 45 if (myinvalidcon) 46 rc = myinvalidcon(path, lineno, *ctx); 47 else if (mycanoncon) 48 rc = mycanoncon(path, lineno, ctx); 49 else { 50 rc = selabel_validate(rec, contexts); 51 if (rc < 0) { 52 if (lineno) { 53 COMPAT_LOG(SELINUX_WARNING, 54 "%s: line %u has invalid context %s\n", 55 path, lineno, *ctx); 56 } else { 57 COMPAT_LOG(SELINUX_WARNING, 58 "%s: has invalid context %s\n", path, *ctx); 59 } 60 } 61 } 62 63 return rc ? -1 : 0; 64 } 65 66 #ifndef BUILD_HOST 67 68 static __thread struct selabel_handle *hnd; 69 70 /* 71 * An array for mapping integers to contexts 72 */ 73 static __thread char **con_array; 74 static __thread int con_array_size; 75 static __thread int con_array_used; 76 77 static pthread_once_t once = PTHREAD_ONCE_INIT; 78 static pthread_key_t destructor_key; 79 static int destructor_key_initialized = 0; 80 81 static int add_array_elt(char *con) 82 { 83 if (con_array_size) { 84 while (con_array_used >= con_array_size) { 85 con_array_size *= 2; 86 con_array = (char **)realloc(con_array, sizeof(char*) * 87 con_array_size); 88 if (!con_array) { 89 con_array_size = con_array_used = 0; 90 return -1; 91 } 92 } 93 } else { 94 con_array_size = 1000; 95 con_array = (char **)malloc(sizeof(char*) * con_array_size); 96 if (!con_array) { 97 con_array_size = con_array_used = 0; 98 return -1; 99 } 100 } 101 102 con_array[con_array_used] = strdup(con); 103 if (!con_array[con_array_used]) 104 return -1; 105 return con_array_used++; 106 } 107 108 static void free_array_elts(void) 109 { 110 con_array_size = con_array_used = 0; 111 free(con_array); 112 con_array = NULL; 113 } 114 115 void set_matchpathcon_invalidcon(int (*f) (const char *p, unsigned l, char *c)) 116 { 117 myinvalidcon = f; 118 } 119 120 static int default_canoncon(const char *path, unsigned lineno, char **context) 121 { 122 char *tmpcon; 123 if (security_canonicalize_context_raw(*context, &tmpcon) < 0) { 124 if (errno == ENOENT) 125 return 0; 126 if (lineno) 127 myprintf("%s: line %u has invalid context %s\n", path, 128 lineno, *context); 129 else 130 myprintf("%s: invalid context %s\n", path, *context); 131 return 1; 132 } 133 free(*context); 134 *context = tmpcon; 135 return 0; 136 } 137 138 void set_matchpathcon_canoncon(int (*f) (const char *p, unsigned l, char **c)) 139 { 140 if (f) 141 mycanoncon = f; 142 else 143 mycanoncon = &default_canoncon; 144 } 145 146 static __thread struct selinux_opt options[SELABEL_NOPT]; 147 static __thread int notrans; 148 149 void set_matchpathcon_flags(unsigned int flags) 150 { 151 int i; 152 memset(options, 0, sizeof(options)); 153 i = SELABEL_OPT_BASEONLY; 154 options[i].type = i; 155 options[i].value = (flags & MATCHPATHCON_BASEONLY) ? (char*)1 : NULL; 156 i = SELABEL_OPT_VALIDATE; 157 options[i].type = i; 158 options[i].value = (flags & MATCHPATHCON_VALIDATE) ? (char*)1 : NULL; 159 notrans = flags & MATCHPATHCON_NOTRANS; 160 } 161 162 /* 163 * An association between an inode and a 164 * specification. 165 */ 166 typedef struct file_spec { 167 ino_t ino; /* inode number */ 168 int specind; /* index of specification in spec */ 169 char *file; /* full pathname for diagnostic messages about conflicts */ 170 struct file_spec *next; /* next association in hash bucket chain */ 171 } file_spec_t; 172 173 /* 174 * The hash table of associations, hashed by inode number. 175 * Chaining is used for collisions, with elements ordered 176 * by inode number in each bucket. Each hash bucket has a dummy 177 * header. 178 */ 179 #define HASH_BITS 16 180 #define HASH_BUCKETS (1 << HASH_BITS) 181 #define HASH_MASK (HASH_BUCKETS-1) 182 static file_spec_t *fl_head; 183 184 /* 185 * Try to add an association between an inode and 186 * a specification. If there is already an association 187 * for the inode and it conflicts with this specification, 188 * then use the specification that occurs later in the 189 * specification array. 190 */ 191 int matchpathcon_filespec_add(ino_t ino, int specind, const char *file) 192 { 193 file_spec_t *prevfl, *fl; 194 int h, ret; 195 struct stat sb; 196 197 if (!fl_head) { 198 fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); 199 if (!fl_head) 200 goto oom; 201 memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS); 202 } 203 204 h = (ino + (ino >> HASH_BITS)) & HASH_MASK; 205 for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; 206 prevfl = fl, fl = fl->next) { 207 if (ino == fl->ino) { 208 ret = lstat(fl->file, &sb); 209 if (ret < 0 || sb.st_ino != ino) { 210 fl->specind = specind; 211 free(fl->file); 212 fl->file = malloc(strlen(file) + 1); 213 if (!fl->file) 214 goto oom; 215 strcpy(fl->file, file); 216 return fl->specind; 217 218 } 219 220 if (!strcmp(con_array[fl->specind], 221 con_array[specind])) 222 return fl->specind; 223 224 myprintf 225 ("%s: conflicting specifications for %s and %s, using %s.\n", 226 __FUNCTION__, file, fl->file, 227 con_array[fl->specind]); 228 free(fl->file); 229 fl->file = malloc(strlen(file) + 1); 230 if (!fl->file) 231 goto oom; 232 strcpy(fl->file, file); 233 return fl->specind; 234 } 235 236 if (ino > fl->ino) 237 break; 238 } 239 240 fl = malloc(sizeof(file_spec_t)); 241 if (!fl) 242 goto oom; 243 fl->ino = ino; 244 fl->specind = specind; 245 fl->file = malloc(strlen(file) + 1); 246 if (!fl->file) 247 goto oom_freefl; 248 strcpy(fl->file, file); 249 fl->next = prevfl->next; 250 prevfl->next = fl; 251 return fl->specind; 252 oom_freefl: 253 free(fl); 254 oom: 255 myprintf("%s: insufficient memory for file label entry for %s\n", 256 __FUNCTION__, file); 257 return -1; 258 } 259 260 /* 261 * Evaluate the association hash table distribution. 262 */ 263 void matchpathcon_filespec_eval(void) 264 { 265 file_spec_t *fl; 266 int h, used, nel, len, longest; 267 268 if (!fl_head) 269 return; 270 271 used = 0; 272 longest = 0; 273 nel = 0; 274 for (h = 0; h < HASH_BUCKETS; h++) { 275 len = 0; 276 for (fl = fl_head[h].next; fl; fl = fl->next) { 277 len++; 278 } 279 if (len) 280 used++; 281 if (len > longest) 282 longest = len; 283 nel += len; 284 } 285 286 myprintf 287 ("%s: hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n", 288 __FUNCTION__, nel, used, HASH_BUCKETS, longest); 289 } 290 291 /* 292 * Destroy the association hash table. 293 */ 294 void matchpathcon_filespec_destroy(void) 295 { 296 file_spec_t *fl, *tmp; 297 int h; 298 299 free_array_elts(); 300 301 if (!fl_head) 302 return; 303 304 for (h = 0; h < HASH_BUCKETS; h++) { 305 fl = fl_head[h].next; 306 while (fl) { 307 tmp = fl; 308 fl = fl->next; 309 free(tmp->file); 310 free(tmp); 311 } 312 fl_head[h].next = NULL; 313 } 314 free(fl_head); 315 fl_head = NULL; 316 } 317 318 static void matchpathcon_thread_destructor(void __attribute__((unused)) *ptr) 319 { 320 matchpathcon_fini(); 321 } 322 323 void __attribute__((destructor)) matchpathcon_lib_destructor(void); 324 325 void hidden __attribute__((destructor)) matchpathcon_lib_destructor(void) 326 { 327 if (destructor_key_initialized) 328 __selinux_key_delete(destructor_key); 329 } 330 331 static void matchpathcon_init_once(void) 332 { 333 if (__selinux_key_create(&destructor_key, matchpathcon_thread_destructor) == 0) 334 destructor_key_initialized = 1; 335 } 336 337 int matchpathcon_init_prefix(const char *path, const char *subset) 338 { 339 if (!mycanoncon) 340 mycanoncon = default_canoncon; 341 342 __selinux_once(once, matchpathcon_init_once); 343 __selinux_setspecific(destructor_key, (void *)1); 344 345 options[SELABEL_OPT_SUBSET].type = SELABEL_OPT_SUBSET; 346 options[SELABEL_OPT_SUBSET].value = subset; 347 options[SELABEL_OPT_PATH].type = SELABEL_OPT_PATH; 348 options[SELABEL_OPT_PATH].value = path; 349 350 hnd = selabel_open(SELABEL_CTX_FILE, options, SELABEL_NOPT); 351 return hnd ? 0 : -1; 352 } 353 354 hidden_def(matchpathcon_init_prefix) 355 356 int matchpathcon_init(const char *path) 357 { 358 return matchpathcon_init_prefix(path, NULL); 359 } 360 361 void matchpathcon_fini(void) 362 { 363 free_array_elts(); 364 365 if (hnd) { 366 selabel_close(hnd); 367 hnd = NULL; 368 } 369 } 370 371 /* 372 * We do not want to resolve a symlink to a real path if it is the final 373 * component of the name. Thus we split the pathname on the last "/" and 374 * determine a real path component of the first portion. We then have to 375 * copy the last part back on to get the final real path. Wheww. 376 */ 377 int realpath_not_final(const char *name, char *resolved_path) 378 { 379 char *last_component; 380 char *tmp_path, *p; 381 size_t len = 0; 382 int rc = 0; 383 384 tmp_path = strdup(name); 385 if (!tmp_path) { 386 myprintf("symlink_realpath(%s) strdup() failed: %s\n", 387 name, strerror(errno)); 388 rc = -1; 389 goto out; 390 } 391 392 last_component = strrchr(tmp_path, '/'); 393 394 if (last_component == tmp_path) { 395 last_component++; 396 p = strcpy(resolved_path, ""); 397 } else if (last_component) { 398 *last_component = '\0'; 399 last_component++; 400 p = realpath(tmp_path, resolved_path); 401 } else { 402 last_component = tmp_path; 403 p = realpath("./", resolved_path); 404 } 405 406 if (!p) { 407 myprintf("symlink_realpath(%s) realpath() failed: %s\n", 408 name, strerror(errno)); 409 rc = -1; 410 goto out; 411 } 412 413 len = strlen(p); 414 if (len + strlen(last_component) + 2 > PATH_MAX) { 415 myprintf("symlink_realpath(%s) failed: Filename too long \n", 416 name); 417 errno=ENAMETOOLONG; 418 rc = -1; 419 goto out; 420 } 421 422 resolved_path += len; 423 strcpy(resolved_path, "/"); 424 resolved_path += 1; 425 strcpy(resolved_path, last_component); 426 out: 427 free(tmp_path); 428 return rc; 429 } 430 431 int matchpathcon(const char *path, mode_t mode, char ** con) 432 { 433 char stackpath[PATH_MAX + 1]; 434 char *p = NULL; 435 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0)) 436 return -1; 437 438 if (S_ISLNK(mode)) { 439 if (!realpath_not_final(path, stackpath)) 440 path = stackpath; 441 } else { 442 p = realpath(path, stackpath); 443 if (p) 444 path = p; 445 } 446 447 return notrans ? 448 selabel_lookup_raw(hnd, con, path, mode) : 449 selabel_lookup(hnd, con, path, mode); 450 } 451 452 int matchpathcon_index(const char *name, mode_t mode, char ** con) 453 { 454 int i = matchpathcon(name, mode, con); 455 456 if (i < 0) 457 return -1; 458 459 return add_array_elt(*con); 460 } 461 462 void matchpathcon_checkmatches(char *str __attribute__((unused))) 463 { 464 selabel_stats(hnd); 465 } 466 467 /* Compare two contexts to see if their differences are "significant", 468 * or whether the only difference is in the user. */ 469 int selinux_file_context_cmp(const char * a, 470 const char * b) 471 { 472 char *rest_a, *rest_b; /* Rest of the context after the user */ 473 if (!a && !b) 474 return 0; 475 if (!a) 476 return -1; 477 if (!b) 478 return 1; 479 rest_a = strchr((char *)a, ':'); 480 rest_b = strchr((char *)b, ':'); 481 if (!rest_a && !rest_b) 482 return 0; 483 if (!rest_a) 484 return -1; 485 if (!rest_b) 486 return 1; 487 return strcmp(rest_a, rest_b); 488 } 489 490 int selinux_file_context_verify(const char *path, mode_t mode) 491 { 492 char * con = NULL; 493 char * fcontext = NULL; 494 int rc = 0; 495 char stackpath[PATH_MAX + 1]; 496 char *p = NULL; 497 498 if (S_ISLNK(mode)) { 499 if (!realpath_not_final(path, stackpath)) 500 path = stackpath; 501 } else { 502 p = realpath(path, stackpath); 503 if (p) 504 path = p; 505 } 506 507 rc = lgetfilecon_raw(path, &con); 508 if (rc == -1) { 509 if (errno != ENOTSUP) 510 return -1; 511 else 512 return 0; 513 } 514 515 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0)) 516 return -1; 517 518 if (selabel_lookup_raw(hnd, &fcontext, path, mode) != 0) { 519 if (errno != ENOENT) 520 rc = -1; 521 else 522 rc = 0; 523 } else { 524 /* 525 * Need to set errno to 0 as it can be set to ENOENT if the 526 * file_contexts.subs file does not exist (see selabel_open in 527 * label.c), thus causing confusion if errno is checked on return. 528 */ 529 errno = 0; 530 rc = (selinux_file_context_cmp(fcontext, con) == 0); 531 } 532 533 freecon(con); 534 freecon(fcontext); 535 return rc; 536 } 537 538 int selinux_lsetfilecon_default(const char *path) 539 { 540 struct stat st; 541 int rc = -1; 542 char * scontext = NULL; 543 if (lstat(path, &st) != 0) 544 return rc; 545 546 if (!hnd && (matchpathcon_init_prefix(NULL, NULL) < 0)) 547 return -1; 548 549 /* If there's an error determining the context, or it has none, 550 return to allow default context */ 551 if (selabel_lookup_raw(hnd, &scontext, path, st.st_mode)) { 552 if (errno == ENOENT) 553 rc = 0; 554 } else { 555 rc = lsetfilecon_raw(path, scontext); 556 freecon(scontext); 557 } 558 return rc; 559 } 560 561 #endif 562