1/* 2 * The majority of this code is from Android's 3 * external/libselinux/src/android.c and upstream 4 * selinux/policycoreutils/setfiles/restore.c 5 * 6 * See selinux_restorecon(3) for details. 7 */ 8 9#include <unistd.h> 10#include <string.h> 11#include <stdio.h> 12#include <stdlib.h> 13#include <stdbool.h> 14#include <ctype.h> 15#include <errno.h> 16#include <fcntl.h> 17#include <include/fts.h> 18#include <inttypes.h> 19#include <limits.h> 20#include <stdint.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <sys/xattr.h> 24#include <sys/vfs.h> 25#include <sys/statvfs.h> 26#include <sys/utsname.h> 27#include <linux/magic.h> 28#include <libgen.h> 29#include <syslog.h> 30#include <assert.h> 31 32#include <selinux/selinux.h> 33#include <selinux/context.h> 34#include <selinux/label.h> 35#include <selinux/restorecon.h> 36#include <selinux/skip_elx_constants.h> 37 38#include "ignore_path.h" 39#include "callbacks.h" 40#include "selinux_internal.h" 41#include "label_file.h" 42#include "sha1.h" 43 44#define STAR_COUNT 1024 45 46static struct selabel_handle *fc_sehandle = NULL; 47static bool selabel_no_digest; 48static char *rootpath = NULL; 49static size_t rootpathlen; 50 51/* Information on excluded fs and directories. */ 52struct edir { 53 char *directory; 54 size_t size; 55 /* True if excluded by selinux_restorecon_set_exclude_list(3). */ 56 bool caller_excluded; 57}; 58#define CALLER_EXCLUDED true 59static bool ignore_mounts; 60static uint64_t exclude_non_seclabel_mounts(void); 61static int exclude_count = 0; 62static struct edir *exclude_lst = NULL; 63static uint64_t fc_count = 0; /* Number of files processed so far */ 64static uint64_t efile_count; /* Estimated total number of files */ 65static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; 66 67/* Store information on directories with xattr's. */ 68static struct dir_xattr *dir_xattr_list; 69static struct dir_xattr *dir_xattr_last; 70 71/* Number of errors ignored during the file tree walk. */ 72static long unsigned skipped_errors; 73 74/* restorecon_flags for passing to restorecon_sb() */ 75struct rest_flags { 76 bool nochange; 77 bool verbose; 78 bool progress; 79 bool mass_relabel; 80 bool set_specctx; 81 bool add_assoc; 82 bool recurse; 83 bool userealpath; 84 bool set_xdev; 85 bool abort_on_error; 86 bool syslog_changes; 87 bool log_matches; 88 bool ignore_noent; 89 bool warnonnomatch; 90 bool conflicterror; 91 bool count_errors; 92 bool skipelx; 93}; 94 95static void restorecon_init(void) 96{ 97 struct selabel_handle *sehandle = NULL; 98 99 if (!fc_sehandle) { 100 sehandle = selinux_restorecon_default_handle(); 101 selinux_restorecon_set_sehandle(sehandle); 102 } 103 104 efile_count = 0; 105 if (!ignore_mounts) 106 efile_count = exclude_non_seclabel_mounts(); 107} 108 109static pthread_once_t fc_once = PTHREAD_ONCE_INIT; 110 111/* 112 * Manage excluded directories: 113 * remove_exclude() - This removes any conflicting entries as there could be 114 * a case where a non-seclabel fs is mounted on /foo and 115 * then a seclabel fs is mounted on top of it. 116 * However if an entry has been added via 117 * selinux_restorecon_set_exclude_list(3) do not remove. 118 * 119 * add_exclude() - Add a directory/fs to be excluded from labeling. If it 120 * has already been added, then ignore. 121 * 122 * check_excluded() - Check if directory/fs is to be excluded when relabeling. 123 * 124 * file_system_count() - Calculates the number of files to be processed. 125 * The count is only used if SELINUX_RESTORECON_PROGRESS 126 * is set and a mass relabel is requested. 127 * 128 * exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what 129 * non-seclabel mounts to exclude from 130 * relabeling. restorecon_init() will not 131 * call this function if the 132 * SELINUX_RESTORECON_IGNORE_MOUNTS 133 * flag is set. 134 * Setting SELINUX_RESTORECON_IGNORE_MOUNTS 135 * is useful where there is a non-seclabel fs 136 * mounted on /foo and then a seclabel fs is 137 * mounted on a directory below this. 138 */ 139static void remove_exclude(const char *directory) 140{ 141 int i; 142 143 for (i = 0; i < exclude_count; i++) { 144 if (strcmp(directory, exclude_lst[i].directory) == 0 && 145 !exclude_lst[i].caller_excluded) { 146 free(exclude_lst[i].directory); 147 if (i != exclude_count - 1) 148 exclude_lst[i] = exclude_lst[exclude_count - 1]; 149 exclude_count--; 150 return; 151 } 152 } 153} 154 155static int add_exclude(const char *directory, bool who) 156{ 157 struct edir *tmp_list, *current; 158 size_t len = 0; 159 int i; 160 161 /* Check if already present. */ 162 for (i = 0; i < exclude_count; i++) { 163 if (strcmp(directory, exclude_lst[i].directory) == 0) 164 return 0; 165 } 166 167 if (directory == NULL || directory[0] != '/') { 168 selinux_log(SELINUX_ERROR, 169 "Full path required for exclude: %s.\n", 170 directory); 171 errno = EINVAL; 172 return -1; 173 } 174 175 if (exclude_count >= INT_MAX - 1) { 176 selinux_log(SELINUX_ERROR, "Too many directory excludes: %d.\n", exclude_count); 177 errno = EOVERFLOW; 178 return -1; 179 } 180 181 tmp_list = realloc(exclude_lst, 182 sizeof(struct edir) * (exclude_count + 1)); 183 if (!tmp_list) 184 goto oom; 185 186 exclude_lst = tmp_list; 187 188 len = strlen(directory); 189 while (len > 1 && directory[len - 1] == '/') 190 len--; 191 192 current = (exclude_lst + exclude_count); 193 194 current->directory = strndup(directory, len); 195 if (!current->directory) 196 goto oom; 197 198 current->size = len; 199 current->caller_excluded = who; 200 exclude_count++; 201 return 0; 202 203oom: 204 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 205 return -1; 206} 207 208static int check_excluded(const char *file) 209{ 210 int i; 211 212 for (i = 0; i < exclude_count; i++) { 213 if (strncmp(file, exclude_lst[i].directory, 214 exclude_lst[i].size) == 0) { 215 if (file[exclude_lst[i].size] == 0 || 216 file[exclude_lst[i].size] == '/') 217 return 1; 218 } 219 } 220 return 0; 221} 222 223static uint64_t file_system_count(const char *name) 224{ 225 struct statvfs statvfs_buf; 226 uint64_t nfile = 0; 227 228 memset(&statvfs_buf, 0, sizeof(statvfs_buf)); 229 if (!statvfs(name, &statvfs_buf)) 230 nfile = statvfs_buf.f_files - statvfs_buf.f_ffree; 231 232 return nfile; 233} 234 235/* 236 * This is called once when selinux_restorecon() is first called. 237 * Searches /proc/mounts for all file systems that do not support extended 238 * attributes and adds them to the exclude directory table. File systems 239 * that support security labels have the seclabel option, return 240 * approximate total file count. 241 */ 242static uint64_t exclude_non_seclabel_mounts(void) 243{ 244 struct utsname uts; 245 FILE *fp; 246 size_t len; 247 int index = 0, found = 0; 248 uint64_t nfile = 0; 249 char *mount_info[4]; 250 char *buf = NULL, *item; 251 252 /* Check to see if the kernel supports seclabel */ 253 if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) 254 return 0; 255 if (is_selinux_enabled() <= 0) 256 return 0; 257 258 fp = fopen("/proc/mounts", "re"); 259 if (!fp) 260 return 0; 261 262 while (getline(&buf, &len, fp) != -1) { 263 found = 0; 264 index = 0; 265 item = strtok(buf, " "); 266 while (item != NULL) { 267 mount_info[index] = item; 268 index++; 269 if (index == 4) 270 break; 271 item = strtok(NULL, " "); 272 } 273 if (index < 4) { 274 selinux_log(SELINUX_ERROR, 275 "/proc/mounts record \"%s\" has incorrect format.\n", 276 buf); 277 continue; 278 } 279 280 /* Remove pre-existing entry */ 281 remove_exclude(mount_info[1]); 282 283 item = strtok(mount_info[3], ","); 284 while (item != NULL) { 285 if (strcmp(item, "seclabel") == 0) { 286 found = 1; 287 nfile += file_system_count(mount_info[1]); 288 break; 289 } 290 item = strtok(NULL, ","); 291 } 292 293 /* Exclude mount points without the seclabel option */ 294 if (!found) { 295 if (add_exclude(mount_info[1], !CALLER_EXCLUDED) && 296 errno == ENOMEM) 297 assert(0); 298 } 299 } 300 301 free(buf); 302 fclose(fp); 303 /* return estimated #Files + 5% for directories and hard links */ 304 return nfile * 1.05; 305} 306 307/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */ 308static int add_xattr_entry(const char *directory, bool delete_nonmatch, 309 bool delete_all) 310{ 311 char *sha1_buf = NULL; 312 size_t i, digest_len = 0; 313 int rc; 314 enum digest_result digest_result; 315 bool match; 316 struct dir_xattr *new_entry; 317 uint8_t *xattr_digest = NULL; 318 uint8_t *calculated_digest = NULL; 319 320 if (!directory) { 321 errno = EINVAL; 322 return -1; 323 } 324 325 match = selabel_get_digests_all_partial_matches(fc_sehandle, directory, 326 &calculated_digest, &xattr_digest, 327 &digest_len); 328 329 if (!xattr_digest || !digest_len) { 330 free(calculated_digest); 331 return 1; 332 } 333 334 /* Convert entry to a hex encoded string. */ 335 sha1_buf = malloc(digest_len * 2 + 1); 336 if (!sha1_buf) { 337 free(xattr_digest); 338 free(calculated_digest); 339 goto oom; 340 } 341 342 for (i = 0; i < digest_len; i++) 343 sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]); 344 345 digest_result = match ? MATCH : NOMATCH; 346 347 if ((delete_nonmatch && !match) || delete_all) { 348 digest_result = match ? DELETED_MATCH : DELETED_NOMATCH; 349 rc = removexattr(directory, RESTORECON_PARTIAL_MATCH_DIGEST); 350 if (rc) { 351 selinux_log(SELINUX_ERROR, 352 "Error: %m removing xattr \"%s\" from: %s\n", 353 RESTORECON_PARTIAL_MATCH_DIGEST, directory); 354 digest_result = ERROR; 355 } 356 } 357 free(xattr_digest); 358 free(calculated_digest); 359 360 /* Now add entries to link list. */ 361 new_entry = malloc(sizeof(struct dir_xattr)); 362 if (!new_entry) { 363 free(sha1_buf); 364 goto oom; 365 } 366 new_entry->next = NULL; 367 368 new_entry->directory = strdup(directory); 369 if (!new_entry->directory) { 370 free(new_entry); 371 free(sha1_buf); 372 goto oom; 373 } 374 375 new_entry->digest = strdup(sha1_buf); 376 if (!new_entry->digest) { 377 free(new_entry->directory); 378 free(new_entry); 379 free(sha1_buf); 380 goto oom; 381 } 382 383 new_entry->result = digest_result; 384 385 if (!dir_xattr_list) { 386 dir_xattr_list = new_entry; 387 dir_xattr_last = new_entry; 388 } else { 389 dir_xattr_last->next = new_entry; 390 dir_xattr_last = new_entry; 391 } 392 393 free(sha1_buf); 394 return 0; 395 396oom: 397 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 398 return -1; 399} 400 401/* 402 * Support filespec services filespec_add(), filespec_eval() and 403 * filespec_destroy(). 404 * 405 * selinux_restorecon(3) uses filespec services when the 406 * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between 407 * an inode and a specification. 408 */ 409 410/* 411 * The hash table of associations, hashed by inode number. Chaining is used 412 * for collisions, with elements ordered by inode number in each bucket. 413 * Each hash bucket has a dummy header. 414 */ 415#define HASH_BITS 16 416#define HASH_BUCKETS (1 << HASH_BITS) 417#define HASH_MASK (HASH_BUCKETS-1) 418 419/* 420 * An association between an inode and a context. 421 */ 422typedef struct file_spec { 423 ino_t ino; /* inode number */ 424 char *con; /* matched context */ 425 char *file; /* full pathname */ 426 struct file_spec *next; /* next association in hash bucket chain */ 427} file_spec_t; 428 429static file_spec_t *fl_head; 430static pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER; 431 432/* 433 * Try to add an association between an inode and a context. If there is a 434 * different context that matched the inode, then use the first context 435 * that matched. 436 */ 437static int filespec_add(ino_t ino, const char *con, const char *file, 438 const struct rest_flags *flags) 439{ 440 file_spec_t *prevfl, *fl; 441 uint32_t h; 442 int ret; 443 struct stat64 sb; 444 445 __pthread_mutex_lock(&fl_mutex); 446 447 if (!fl_head) { 448 fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t)); 449 if (!fl_head) 450 goto oom; 451 } 452 453 h = (ino + (ino >> HASH_BITS)) & HASH_MASK; 454 for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; 455 prevfl = fl, fl = fl->next) { 456 if (ino == fl->ino) { 457 ret = lstat64(fl->file, &sb); 458 if (ret < 0 || sb.st_ino != ino) { 459 freecon(fl->con); 460 free(fl->file); 461 fl->file = strdup(file); 462 if (!fl->file) 463 goto oom; 464 fl->con = strdup(con); 465 if (!fl->con) 466 goto oom; 467 goto unlock_1; 468 } 469 470 if (strcmp(fl->con, con) == 0) 471 goto unlock_1; 472 473 selinux_log(SELINUX_ERROR, 474 "conflicting specifications for %s and %s, using %s.\n", 475 file, fl->file, fl->con); 476 free(fl->file); 477 fl->file = strdup(file); 478 if (!fl->file) 479 goto oom; 480 481 __pthread_mutex_unlock(&fl_mutex); 482 483 if (flags->conflicterror) { 484 selinux_log(SELINUX_ERROR, 485 "treating conflicting specifications as an error.\n"); 486 return -1; 487 } 488 return 1; 489 } 490 491 if (ino > fl->ino) 492 break; 493 } 494 495 fl = malloc(sizeof(file_spec_t)); 496 if (!fl) 497 goto oom; 498 fl->ino = ino; 499 fl->con = strdup(con); 500 if (!fl->con) 501 goto oom_freefl; 502 fl->file = strdup(file); 503 if (!fl->file) 504 goto oom_freeflcon; 505 fl->next = prevfl->next; 506 prevfl->next = fl; 507 508 __pthread_mutex_unlock(&fl_mutex); 509 return 0; 510 511oom_freeflcon: 512 free(fl->con); 513oom_freefl: 514 free(fl); 515oom: 516 __pthread_mutex_unlock(&fl_mutex); 517 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 518 return -1; 519unlock_1: 520 __pthread_mutex_unlock(&fl_mutex); 521 return 1; 522} 523 524/* 525 * Evaluate the association hash table distribution. 526 */ 527#ifdef DEBUG 528static void filespec_eval(void) 529{ 530 file_spec_t *fl; 531 uint32_t h; 532 size_t used, nel, len, longest; 533 534 if (!fl_head) 535 return; 536 537 used = 0; 538 longest = 0; 539 nel = 0; 540 for (h = 0; h < HASH_BUCKETS; h++) { 541 len = 0; 542 for (fl = fl_head[h].next; fl; fl = fl->next) 543 len++; 544 if (len) 545 used++; 546 if (len > longest) 547 longest = len; 548 nel += len; 549 } 550 551 selinux_log(SELINUX_INFO, 552 "filespec hash table stats: %zu elements, %zu/%zu buckets used, longest chain length %zu\n", 553 nel, used, HASH_BUCKETS, longest); 554} 555#else 556static void filespec_eval(void) 557{ 558} 559#endif 560 561/* 562 * Destroy the association hash table. 563 */ 564static void filespec_destroy(void) 565{ 566 file_spec_t *fl, *tmp; 567 uint32_t h; 568 569 if (!fl_head) 570 return; 571 572 for (h = 0; h < HASH_BUCKETS; h++) { 573 fl = fl_head[h].next; 574 while (fl) { 575 tmp = fl; 576 fl = fl->next; 577 freecon(tmp->con); 578 free(tmp->file); 579 free(tmp); 580 } 581 fl_head[h].next = NULL; 582 } 583 free(fl_head); 584 fl_head = NULL; 585} 586 587/* 588 * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if 589 * the type components differ, updating newtypecon if so. 590 */ 591static int compare_types(const char *curcon, const char *newcon, char **newtypecon) 592{ 593 int types_differ = 0; 594 context_t cona; 595 context_t conb; 596 int rc = 0; 597 598 cona = context_new(curcon); 599 if (!cona) { 600 rc = -1; 601 goto out; 602 } 603 conb = context_new(newcon); 604 if (!conb) { 605 context_free(cona); 606 rc = -1; 607 goto out; 608 } 609 610 types_differ = strcmp(context_type_get(cona), context_type_get(conb)); 611 if (types_differ) { 612 rc |= context_user_set(conb, context_user_get(cona)); 613 rc |= context_role_set(conb, context_role_get(cona)); 614 rc |= context_range_set(conb, context_range_get(cona)); 615 if (!rc) { 616 *newtypecon = strdup(context_str(conb)); 617 if (!*newtypecon) { 618 rc = -1; 619 goto err; 620 } 621 } 622 } 623 624err: 625 context_free(cona); 626 context_free(conb); 627out: 628 return rc; 629} 630 631#define DATA_APP_EL1 "/data/app/el1/" 632#define DATA_APP_EL2 "/data/app/el2/" 633#define DATA_APP_EL3 "/data/app/el3/" 634#define DATA_APP_EL4 "/data/app/el4/" 635#define DATA_ACCOUNTS_ACCOUNT_0 "/data/accounts/account_0/" 636#define HNP_ROOT_PATH "/data/app/el1/bundle/" 637#define HNP_PUBLIC_DIR "/hnppublic" 638#define HNP_ROOT_PATH_LEN 21 639#define HNP_PUBLIC_DIR_LEN 10 640 641// Allow the hnp process to refresh the labels of files in the HNP_ROOT_PATH directory 642static bool is_hnp_path(const char *path) 643{ 644 size_t pathLen = strlen(path); 645 if ((pathLen < HNP_ROOT_PATH_LEN + 1 + HNP_PUBLIC_DIR_LEN + 1) || 646 (strstr(path, HNP_PUBLIC_DIR) == NULL)) { 647 return false; 648 } 649 650 if (strncmp(path, HNP_ROOT_PATH, HNP_ROOT_PATH_LEN) != 0) { 651 return false; 652 } 653 return true; 654} 655 656static bool check_path_allow_restorecon(const char *pathname) 657{ 658 if ((!strncmp(pathname, DATA_APP_EL1, sizeof(DATA_APP_EL1) - 1) && (!is_hnp_path(pathname))) || 659 !strncmp(pathname, DATA_APP_EL2, sizeof(DATA_APP_EL2) - 1) || 660 !strncmp(pathname, DATA_APP_EL3, sizeof(DATA_APP_EL3) - 1) || 661 !strncmp(pathname, DATA_APP_EL4, sizeof(DATA_APP_EL4) - 1) || 662 !strncmp(pathname, DATA_ACCOUNTS_ACCOUNT_0, sizeof(DATA_ACCOUNTS_ACCOUNT_0) - 1)) { 663 return false; 664 } 665 return true; 666} 667 668 669static int restorecon_sb(const char *pathname, const struct stat *sb, 670 const struct rest_flags *flags, bool first) 671{ 672 char *newcon = NULL; 673 char *curcon = NULL; 674 char *newtypecon = NULL; 675 int rc; 676 const char *lookup_path = pathname; 677 678 if (!check_path_allow_restorecon(pathname)) { 679 goto out; 680 } 681 682 if (rootpath) { 683 if (strncmp(rootpath, lookup_path, rootpathlen) != 0) { 684 selinux_log(SELINUX_ERROR, 685 "%s is not located in alt_rootpath %s\n", 686 lookup_path, rootpath); 687 return -1; 688 } 689 lookup_path += rootpathlen; 690 } 691 692 if (rootpath != NULL && lookup_path[0] == '\0') 693 /* this is actually the root dir of the alt root. */ 694 rc = selabel_lookup_raw(fc_sehandle, &newcon, "/", 695 sb->st_mode & S_IFMT); 696 else 697 rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path, 698 sb->st_mode & S_IFMT); 699 700 if (rc < 0) { 701 if (errno == ENOENT) { 702 if (flags->warnonnomatch && first) 703 selinux_log(SELINUX_INFO, 704 "Warning no default label for %s\n", 705 lookup_path); 706 707 return 0; /* no match, but not an error */ 708 } 709 710 return -1; 711 } 712 713 if (flags->progress) { 714 __pthread_mutex_lock(&progress_mutex); 715 fc_count++; 716 if (fc_count % STAR_COUNT == 0) { 717 if (flags->mass_relabel && efile_count > 0) { 718 float pc = (fc_count < efile_count) ? (100.0 * 719 fc_count / efile_count) : 100; 720 fprintf(stdout, "\r%-.1f%%", (double)pc); 721 } else { 722 fprintf(stdout, "\r%" PRIu64 "k", fc_count / STAR_COUNT); 723 } 724 fflush(stdout); 725 } 726 __pthread_mutex_unlock(&progress_mutex); 727 } 728 729 if (flags->add_assoc) { 730 rc = filespec_add(sb->st_ino, newcon, pathname, flags); 731 732 if (rc < 0) { 733 selinux_log(SELINUX_ERROR, 734 "filespec_add error: %s\n", pathname); 735 freecon(newcon); 736 return -1; 737 } 738 739 if (rc > 0) { 740 /* Already an association and it took precedence. */ 741 freecon(newcon); 742 return 0; 743 } 744 } 745 746 if (flags->log_matches) 747 selinux_log(SELINUX_INFO, "%s matched by %s\n", 748 pathname, newcon); 749 750 if (lgetfilecon_raw(pathname, &curcon) < 0) { 751 if (errno != ENODATA) 752 goto err; 753 754 curcon = NULL; 755 } 756 757 if (curcon == NULL || strcmp(curcon, newcon) != 0) { 758 bool updated = false; 759 760 if (!flags->set_specctx && curcon && 761 (is_context_customizable(curcon) > 0)) { 762 if (flags->verbose) { 763 selinux_log(SELINUX_INFO, 764 "%s not reset as customized by admin to %s\n", 765 pathname, curcon); 766 } 767 goto out; 768 } 769 770 if (!flags->set_specctx && curcon) { 771 /* If types different then update newcon. */ 772 rc = compare_types(curcon, newcon, &newtypecon); 773 if (rc) 774 goto err; 775 776 if (newtypecon) { 777 freecon(newcon); 778 newcon = newtypecon; 779 } else { 780 goto out; 781 } 782 } 783 784 if (!flags->nochange) { 785 if (lsetfilecon(pathname, newcon) < 0) 786 goto err; 787 updated = true; 788 } 789 790 if (flags->verbose) 791 selinux_log(SELINUX_INFO, 792 "%s %s from %s to %s\n", 793 updated ? "Relabeled" : "Would relabel", 794 pathname, 795 curcon ? curcon : "<no context>", 796 newcon); 797 798 if (flags->syslog_changes && !flags->nochange) { 799 if (curcon) 800 syslog(LOG_INFO, 801 "relabeling %s from %s to %s\n", 802 pathname, curcon, newcon); 803 else 804 syslog(LOG_INFO, "labeling %s to %s\n", 805 pathname, newcon); 806 } 807 } 808 809out: 810 rc = 0; 811out1: 812 freecon(curcon); 813 freecon(newcon); 814 return rc; 815err: 816 selinux_log(SELINUX_ERROR, 817 "Could not set context for %s: %m\n", 818 pathname); 819 rc = -1; 820 goto out1; 821} 822 823struct dir_hash_node { 824 char *path; 825 uint8_t digest[SHA1_HASH_SIZE]; 826 struct dir_hash_node *next; 827}; 828/* 829 * Returns true if the digest of all partial matched contexts is the same as 830 * the one saved by setxattr. Otherwise returns false and constructs a 831 * dir_hash_node with the newly calculated digest. 832 */ 833static bool check_context_match_for_dir(const char *pathname, 834 struct dir_hash_node **new_node, 835 int error) 836{ 837 bool status; 838 size_t digest_len = 0; 839 uint8_t *read_digest = NULL; 840 uint8_t *calculated_digest = NULL; 841 842 if (!new_node) 843 return false; 844 845 *new_node = NULL; 846 847 /* status = true if digests match, false otherwise. */ 848 status = selabel_get_digests_all_partial_matches(fc_sehandle, pathname, 849 &calculated_digest, 850 &read_digest, 851 &digest_len); 852 853 if (status) 854 goto free; 855 856 /* Save digest of all matched contexts for the current directory. */ 857 if (!error && calculated_digest) { 858 *new_node = calloc(1, sizeof(struct dir_hash_node)); 859 860 if (!*new_node) 861 goto oom; 862 863 (*new_node)->path = strdup(pathname); 864 865 if (!(*new_node)->path) { 866 free(*new_node); 867 *new_node = NULL; 868 goto oom; 869 } 870 memcpy((*new_node)->digest, calculated_digest, digest_len); 871 (*new_node)->next = NULL; 872 } 873 874free: 875 free(calculated_digest); 876 free(read_digest); 877 return status; 878 879oom: 880 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 881 goto free; 882} 883 884static bool is_in_skip_elx(const char *path) { 885 if (sizeof(skip_elx_path) == 0) { 886 return false; 887 } 888 size_t len = sizeof(skip_elx_path) / sizeof(skip_elx_path[0]); 889 for (size_t i = 0; i < len; i++) { 890 if (strncmp(path, skip_elx_path[i], strlen(skip_elx_path[i])) == 0) { 891 return true; 892 } 893 } 894 return false; 895} 896 897struct rest_state { 898 struct rest_flags flags; 899 dev_t dev_num; 900 struct statfs sfsb; 901 bool ignore_digest; 902 bool setrestorecondigest; 903 bool parallel; 904 905 FTS *fts; 906 FTSENT *ftsent_first; 907 struct dir_hash_node *head, *current; 908 bool abort; 909 int error; 910 long unsigned skipped_errors; 911 int saved_errno; 912 pthread_mutex_t mutex; 913}; 914 915static void *selinux_restorecon_thread(void *arg) 916{ 917 struct rest_state *state = arg; 918 FTS *fts = state->fts; 919 FTSENT *ftsent; 920 int error; 921 char ent_path[PATH_MAX]; 922 struct stat ent_st; 923 bool first = false; 924 925 if (state->parallel) 926 pthread_mutex_lock(&state->mutex); 927 928 if (state->ftsent_first) { 929 ftsent = state->ftsent_first; 930 state->ftsent_first = NULL; 931 first = true; 932 goto loop_body; 933 } 934 935 while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) { 936loop_body: 937 /* If the FTS_XDEV flag is set and the device is different */ 938 if (state->flags.set_xdev && 939 ftsent->fts_statp->st_dev != state->dev_num) 940 continue; 941 942 switch (ftsent->fts_info) { 943 case FTS_DC: 944 selinux_log(SELINUX_ERROR, 945 "Directory cycle on %s.\n", 946 ftsent->fts_path); 947 errno = ELOOP; 948 state->error = -1; 949 state->abort = true; 950 goto finish; 951 case FTS_DP: 952 continue; 953 case FTS_DNR: 954 error = errno; 955 errno = ftsent->fts_errno; 956 selinux_log(SELINUX_ERROR, 957 "Could not read %s: %m.\n", 958 ftsent->fts_path); 959 errno = error; 960 fts_set(fts, ftsent, FTS_SKIP); 961 continue; 962 case FTS_NS: 963 error = errno; 964 errno = ftsent->fts_errno; 965 selinux_log(SELINUX_ERROR, 966 "Could not stat %s: %m.\n", 967 ftsent->fts_path); 968 errno = error; 969 fts_set(fts, ftsent, FTS_SKIP); 970 continue; 971 case FTS_ERR: 972 error = errno; 973 errno = ftsent->fts_errno; 974 selinux_log(SELINUX_ERROR, 975 "Error on %s: %m.\n", 976 ftsent->fts_path); 977 errno = error; 978 fts_set(fts, ftsent, FTS_SKIP); 979 continue; 980 case FTS_D: 981 if (state->sfsb.f_type == SYSFS_MAGIC && 982 !selabel_partial_match(fc_sehandle, 983 ftsent->fts_path)) { 984 fts_set(fts, ftsent, FTS_SKIP); 985 continue; 986 } 987 988 if (check_excluded(ftsent->fts_path)) { 989 fts_set(fts, ftsent, FTS_SKIP); 990 continue; 991 } 992 993 if (state->setrestorecondigest) { 994 struct dir_hash_node *new_node = NULL; 995 996 if (check_context_match_for_dir(ftsent->fts_path, 997 &new_node, 998 state->error) && 999 !state->ignore_digest) { 1000 selinux_log(SELINUX_INFO, 1001 "Skipping restorecon on directory(%s)\n", 1002 ftsent->fts_path); 1003 fts_set(fts, ftsent, FTS_SKIP); 1004 continue; 1005 } 1006 1007 if (new_node && !state->error) { 1008 if (!state->current) { 1009 state->current = new_node; 1010 state->head = state->current; 1011 } else { 1012 state->current->next = new_node; 1013 state->current = new_node; 1014 } 1015 } 1016 } 1017 1018 if (state->flags.skipelx && is_in_skip_elx(ftsent->fts_path)) { 1019 fts_set(fts, ftsent, FTS_SKIP); 1020 continue; 1021 } 1022 1023 enum skip_type skip_ignore_flag = skip_ignore_relabel(ftsent->fts_path); 1024 selinux_log(SELINUX_INFO, 1025 "ignore cfg parsing result %d \n", 1026 skip_ignore_flag); 1027 switch (skip_ignore_flag) { 1028 case SKIP_SELF_SUB_DIR: 1029 selinux_log(SELINUX_INFO, 1030 "Skipping restorecon on directory(%s), cause ignroe_cfg\n", 1031 ftsent->fts_path); 1032 fts_set(fts, ftsent, FTS_SKIP); 1033 continue; 1034 case SKIP_SUB_DIR: 1035 selinux_log(SELINUX_INFO, 1036 "Skipping restorecon on directory(%s) sub directory, cause ignroe_cfg\n", 1037 ftsent->fts_path); 1038 fts_set(fts, ftsent, FTS_SKIP); 1039 default: 1040 break; 1041 } 1042 1043 /* fall through */ 1044 default: 1045 if (strlcpy(ent_path, ftsent->fts_path, sizeof(ent_path)) >= sizeof(ent_path)) { 1046 selinux_log(SELINUX_ERROR, 1047 "Path name too long on %s.\n", 1048 ftsent->fts_path); 1049 errno = ENAMETOOLONG; 1050 state->error = -1; 1051 state->abort = true; 1052 goto finish; 1053 } 1054 1055 ent_st = *ftsent->fts_statp; 1056 if (state->parallel) 1057 pthread_mutex_unlock(&state->mutex); 1058 1059 error = restorecon_sb(ent_path, &ent_st, &state->flags, 1060 first); 1061 1062 if (state->parallel) { 1063 pthread_mutex_lock(&state->mutex); 1064 if (state->abort) 1065 goto unlock; 1066 } 1067 1068 first = false; 1069 if (error) { 1070 if (state->flags.abort_on_error) { 1071 state->error = error; 1072 state->abort = true; 1073 goto finish; 1074 } 1075 if (state->flags.count_errors) 1076 state->skipped_errors++; 1077 else 1078 state->error = error; 1079 } 1080 break; 1081 } 1082 } 1083 1084finish: 1085 if (!state->saved_errno) 1086 state->saved_errno = errno; 1087unlock: 1088 if (state->parallel) 1089 pthread_mutex_unlock(&state->mutex); 1090 return NULL; 1091} 1092 1093static int selinux_restorecon_common(const char *pathname_orig, 1094 unsigned int restorecon_flags, 1095 size_t nthreads) 1096{ 1097 struct rest_state state; 1098 1099 state.flags.nochange = (restorecon_flags & 1100 SELINUX_RESTORECON_NOCHANGE) ? true : false; 1101 state.flags.verbose = (restorecon_flags & 1102 SELINUX_RESTORECON_VERBOSE) ? true : false; 1103 state.flags.progress = (restorecon_flags & 1104 SELINUX_RESTORECON_PROGRESS) ? true : false; 1105 state.flags.mass_relabel = (restorecon_flags & 1106 SELINUX_RESTORECON_MASS_RELABEL) ? true : false; 1107 state.flags.recurse = (restorecon_flags & 1108 SELINUX_RESTORECON_RECURSE) ? true : false; 1109 state.flags.set_specctx = (restorecon_flags & 1110 SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false; 1111 state.flags.userealpath = (restorecon_flags & 1112 SELINUX_RESTORECON_REALPATH) ? true : false; 1113 state.flags.set_xdev = (restorecon_flags & 1114 SELINUX_RESTORECON_XDEV) ? true : false; 1115 state.flags.add_assoc = (restorecon_flags & 1116 SELINUX_RESTORECON_ADD_ASSOC) ? true : false; 1117 state.flags.abort_on_error = (restorecon_flags & 1118 SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false; 1119 state.flags.syslog_changes = (restorecon_flags & 1120 SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false; 1121 state.flags.log_matches = (restorecon_flags & 1122 SELINUX_RESTORECON_LOG_MATCHES) ? true : false; 1123 state.flags.ignore_noent = (restorecon_flags & 1124 SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false; 1125 state.flags.warnonnomatch = true; 1126 state.flags.conflicterror = (restorecon_flags & 1127 SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false; 1128 ignore_mounts = (restorecon_flags & 1129 SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false; 1130 state.ignore_digest = (restorecon_flags & 1131 SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false; 1132 state.flags.count_errors = (restorecon_flags & 1133 SELINUX_RESTORECON_COUNT_ERRORS) ? true : false; 1134 state.flags.skipelx = (restorecon_flags & 1135 SELINUX_RESTORECON_SKIPELX) ? true : false; 1136 state.setrestorecondigest = true; 1137 1138 state.head = NULL; 1139 state.current = NULL; 1140 state.abort = false; 1141 state.error = 0; 1142 state.skipped_errors = 0; 1143 state.saved_errno = 0; 1144 1145 struct stat sb; 1146 char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname; 1147 char *paths[2] = { NULL, NULL }; 1148 int fts_flags, error; 1149 struct dir_hash_node *current = NULL; 1150 1151 if (state.flags.verbose && state.flags.progress) 1152 state.flags.verbose = false; 1153 1154 __selinux_once(fc_once, restorecon_init); 1155 1156 if (!fc_sehandle) 1157 return -1; 1158 1159 /* 1160 * If selabel_no_digest = true then no digest has been requested by 1161 * an external selabel_open(3) call. 1162 */ 1163 if (selabel_no_digest || 1164 (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST)) 1165 state.setrestorecondigest = false; 1166 1167 if (!__pthread_supported) { 1168 if (nthreads != 1) { 1169 nthreads = 1; 1170 selinux_log(SELINUX_WARNING, 1171 "Threading functionality not available, falling back to 1 thread."); 1172 } 1173 } else if (nthreads == 0) { 1174 long nproc = sysconf(_SC_NPROCESSORS_ONLN); 1175 1176 if (nproc > 0) { 1177 nthreads = nproc; 1178 } else { 1179 nthreads = 1; 1180 selinux_log(SELINUX_WARNING, 1181 "Unable to detect CPU count, falling back to 1 thread."); 1182 } 1183 } 1184 1185 /* 1186 * Convert passed-in pathname to canonical pathname by resolving 1187 * realpath of containing dir, then appending last component name. 1188 */ 1189 if (state.flags.userealpath) { 1190 char *basename_cpy = strdup(pathname_orig); 1191 if (!basename_cpy) 1192 goto realpatherr; 1193 pathbname = basename(basename_cpy); 1194 if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || 1195 !strcmp(pathbname, "..")) { 1196 pathname = realpath(pathname_orig, NULL); 1197 if (!pathname) { 1198 free(basename_cpy); 1199 /* missing parent directory */ 1200 if (state.flags.ignore_noent && errno == ENOENT) { 1201 return 0; 1202 } 1203 goto realpatherr; 1204 } 1205 } else { 1206 char *dirname_cpy = strdup(pathname_orig); 1207 if (!dirname_cpy) { 1208 free(basename_cpy); 1209 goto realpatherr; 1210 } 1211 pathdname = dirname(dirname_cpy); 1212 pathdnamer = realpath(pathdname, NULL); 1213 free(dirname_cpy); 1214 if (!pathdnamer) { 1215 free(basename_cpy); 1216 if (state.flags.ignore_noent && errno == ENOENT) { 1217 return 0; 1218 } 1219 goto realpatherr; 1220 } 1221 if (!strcmp(pathdnamer, "/")) 1222 error = asprintf(&pathname, "/%s", pathbname); 1223 else 1224 error = asprintf(&pathname, "%s/%s", 1225 pathdnamer, pathbname); 1226 if (error < 0) { 1227 free(basename_cpy); 1228 goto oom; 1229 } 1230 } 1231 free(basename_cpy); 1232 } else { 1233 pathname = strdup(pathname_orig); 1234 if (!pathname) 1235 goto oom; 1236 } 1237 1238 paths[0] = pathname; 1239 1240 if (lstat(pathname, &sb) < 0) { 1241 if (state.flags.ignore_noent && errno == ENOENT) { 1242 free(pathdnamer); 1243 free(pathname); 1244 return 0; 1245 } else { 1246 selinux_log(SELINUX_ERROR, 1247 "lstat(%s) failed: %m\n", 1248 pathname); 1249 error = -1; 1250 goto cleanup; 1251 } 1252 } 1253 1254 /* Skip digest if not a directory */ 1255 if (!S_ISDIR(sb.st_mode)) 1256 state.setrestorecondigest = false; 1257 1258 if (!state.flags.recurse) { 1259 if (check_excluded(pathname)) { 1260 error = 0; 1261 goto cleanup; 1262 } 1263 1264 error = restorecon_sb(pathname, &sb, &state.flags, true); 1265 goto cleanup; 1266 } 1267 1268 /* Obtain fs type */ 1269 memset(&state.sfsb, 0, sizeof(state.sfsb)); 1270 if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) { 1271 selinux_log(SELINUX_ERROR, 1272 "statfs(%s) failed: %m\n", 1273 pathname); 1274 error = -1; 1275 goto cleanup; 1276 } 1277 1278 /* Skip digest on in-memory filesystems and /sys */ 1279 if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC || 1280 state.sfsb.f_type == SYSFS_MAGIC) 1281 state.setrestorecondigest = false; 1282 1283 if (state.flags.set_xdev) 1284 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV; 1285 else 1286 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 1287 1288 state.fts = fts_open(paths, fts_flags, NULL); 1289 if (!state.fts) 1290 goto fts_err; 1291 1292 state.ftsent_first = fts_read(state.fts); 1293 if (!state.ftsent_first) 1294 goto fts_err; 1295 1296 /* 1297 * Keep the inode of the first device. This is because the FTS_XDEV 1298 * flag tells fts not to descend into directories with different 1299 * device numbers, but fts will still give back the actual directory. 1300 * By saving the device number of the directory that was passed to 1301 * selinux_restorecon() and then skipping all actions on any 1302 * directories with a different device number when the FTS_XDEV flag 1303 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2). 1304 */ 1305 state.dev_num = state.ftsent_first->fts_statp->st_dev; 1306 1307 bool load_ignore_path = load_ignore_cfg(); 1308 if (!load_ignore_path) { 1309 selinux_log(SELINUX_ERROR, "Failed to load ignore cfg!\n"); 1310 } 1311 1312 if (nthreads == 1) { 1313 state.parallel = false; 1314 selinux_restorecon_thread(&state); 1315 } else { 1316 size_t i; 1317 pthread_t self = pthread_self(); 1318 pthread_t *threads = NULL; 1319 1320 pthread_mutex_init(&state.mutex, NULL); 1321 1322 threads = calloc(nthreads - 1, sizeof(*threads)); 1323 if (!threads) 1324 goto oom; 1325 1326 state.parallel = true; 1327 /* 1328 * Start (nthreads - 1) threads - the main thread is going to 1329 * take part, too. 1330 */ 1331 for (i = 0; i < nthreads - 1; i++) { 1332 if (pthread_create(&threads[i], NULL, 1333 selinux_restorecon_thread, &state)) { 1334 /* 1335 * If any thread fails to be created, just mark 1336 * it as such and let the successfully created 1337 * threads do the job. In the worst case the 1338 * main thread will do everything, but that's 1339 * still better than to give up. 1340 */ 1341 threads[i] = self; 1342 } 1343 } 1344 1345 /* Let's join in on the fun! */ 1346 selinux_restorecon_thread(&state); 1347 1348 /* Now wait for all threads to finish. */ 1349 for (i = 0; i < nthreads - 1; i++) { 1350 /* Skip threads that failed to be created. */ 1351 if (pthread_equal(threads[i], self)) 1352 continue; 1353 pthread_join(threads[i], NULL); 1354 } 1355 free(threads); 1356 1357 pthread_mutex_destroy(&state.mutex); 1358 } 1359 1360 error = state.error; 1361 if (state.saved_errno) 1362 goto out; 1363 1364 /* 1365 * Labeling successful. Write partial match digests for subdirectories. 1366 * TODO: Write digest upon FTS_DP if no error occurs in its descents. 1367 * Note: we can't ignore errors here that we've masked due to 1368 * SELINUX_RESTORECON_COUNT_ERRORS. 1369 */ 1370 if (state.setrestorecondigest && !state.flags.nochange && !error && 1371 state.skipped_errors == 0) { 1372 current = state.head; 1373 while (current != NULL) { 1374 if (setxattr(current->path, 1375 RESTORECON_PARTIAL_MATCH_DIGEST, 1376 current->digest, 1377 SHA1_HASH_SIZE, 0) < 0) { 1378 selinux_log(SELINUX_ERROR, 1379 "setxattr failed: %s: %m\n", 1380 current->path); 1381 } 1382 current = current->next; 1383 } 1384 } 1385 1386 skipped_errors = state.skipped_errors; 1387 1388out: 1389 if (state.flags.progress && state.flags.mass_relabel) 1390 fprintf(stdout, "\r%s 100.0%%\n", pathname); 1391 1392 (void) fts_close(state.fts); 1393 errno = state.saved_errno; 1394cleanup: 1395 if (state.flags.add_assoc) { 1396 if (state.flags.verbose) 1397 filespec_eval(); 1398 filespec_destroy(); 1399 } 1400 free(pathdnamer); 1401 free(pathname); 1402 1403 current = state.head; 1404 while (current != NULL) { 1405 struct dir_hash_node *next = current->next; 1406 1407 free(current->path); 1408 free(current); 1409 current = next; 1410 } 1411 return error; 1412 1413oom: 1414 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 1415 error = -1; 1416 goto cleanup; 1417 1418realpatherr: 1419 selinux_log(SELINUX_ERROR, 1420 "SELinux: Could not get canonical path for %s restorecon: %m.\n", 1421 pathname_orig); 1422 error = -1; 1423 goto cleanup; 1424 1425fts_err: 1426 selinux_log(SELINUX_ERROR, 1427 "fts error while labeling %s: %m\n", 1428 paths[0]); 1429 error = -1; 1430 goto cleanup; 1431} 1432 1433 1434/* 1435 * Public API 1436 */ 1437 1438/* selinux_restorecon(3) - Main function that is responsible for labeling */ 1439int selinux_restorecon(const char *pathname_orig, 1440 unsigned int restorecon_flags) 1441{ 1442 return selinux_restorecon_common(pathname_orig, restorecon_flags, 1); 1443} 1444 1445/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */ 1446int selinux_restorecon_parallel(const char *pathname_orig, 1447 unsigned int restorecon_flags, 1448 size_t nthreads) 1449{ 1450 return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads); 1451} 1452 1453/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */ 1454void selinux_restorecon_set_sehandle(struct selabel_handle *hndl) 1455{ 1456 char **specfiles; 1457 unsigned char *fc_digest; 1458 size_t num_specfiles, fc_digest_len; 1459 1460 fc_sehandle = hndl; 1461 if (!fc_sehandle) 1462 return; 1463 1464 /* Check if digest requested in selabel_open(3), if so use it. */ 1465 if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len, 1466 &specfiles, &num_specfiles) < 0) 1467 selabel_no_digest = true; 1468 else 1469 selabel_no_digest = false; 1470} 1471 1472 1473/* 1474 * selinux_restorecon_default_handle(3) is called to set the global restorecon 1475 * handle by a process if the default params are required. 1476 */ 1477struct selabel_handle *selinux_restorecon_default_handle(void) 1478{ 1479 struct selabel_handle *sehandle; 1480 1481 struct selinux_opt fc_opts[] = { 1482 { SELABEL_OPT_DIGEST, (char *)1 } 1483 }; 1484 1485 sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1); 1486 1487 if (!sehandle) { 1488 selinux_log(SELINUX_ERROR, 1489 "Error obtaining file context handle: %m\n"); 1490 return NULL; 1491 } 1492 1493 selabel_no_digest = false; 1494 return sehandle; 1495} 1496 1497/* 1498 * selinux_restorecon_set_exclude_list(3) is called to add additional entries 1499 * to be excluded from labeling checks. 1500 */ 1501void selinux_restorecon_set_exclude_list(const char **exclude_list) 1502{ 1503 int i; 1504 struct stat sb; 1505 1506 for (i = 0; exclude_list[i]; i++) { 1507 if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) { 1508 selinux_log(SELINUX_ERROR, 1509 "lstat error on exclude path \"%s\", %m - ignoring.\n", 1510 exclude_list[i]); 1511 break; 1512 } 1513 if (add_exclude(exclude_list[i], CALLER_EXCLUDED) && 1514 errno == ENOMEM) 1515 assert(0); 1516 } 1517} 1518 1519/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */ 1520int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath) 1521{ 1522 size_t len; 1523 1524 /* This should be NULL on first use */ 1525 if (rootpath) 1526 free(rootpath); 1527 1528 rootpath = strdup(alt_rootpath); 1529 if (!rootpath) { 1530 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 1531 return -1; 1532 } 1533 1534 /* trim trailing /, if present */ 1535 len = strlen(rootpath); 1536 while (len && (rootpath[len - 1] == '/')) 1537 rootpath[--len] = '\0'; 1538 rootpathlen = len; 1539 1540 return 0; 1541} 1542 1543/* selinux_restorecon_xattr(3) 1544 * Find RESTORECON_PARTIAL_MATCH_DIGEST entries. 1545 */ 1546int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags, 1547 struct dir_xattr ***xattr_list) 1548{ 1549 bool recurse = (xattr_flags & 1550 SELINUX_RESTORECON_XATTR_RECURSE) ? true : false; 1551 bool delete_nonmatch = (xattr_flags & 1552 SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false; 1553 bool delete_all = (xattr_flags & 1554 SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false; 1555 ignore_mounts = (xattr_flags & 1556 SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false; 1557 1558 int rc, fts_flags; 1559 struct stat sb; 1560 struct statfs sfsb; 1561 struct dir_xattr *current, *next; 1562 FTS *fts; 1563 FTSENT *ftsent; 1564 char *paths[2] = { NULL, NULL }; 1565 1566 __selinux_once(fc_once, restorecon_init); 1567 1568 if (!fc_sehandle) 1569 return -1; 1570 1571 if (lstat(pathname, &sb) < 0) { 1572 if (errno == ENOENT) 1573 return 0; 1574 1575 selinux_log(SELINUX_ERROR, 1576 "lstat(%s) failed: %m\n", 1577 pathname); 1578 return -1; 1579 } 1580 1581 if (!recurse) { 1582 if (statfs(pathname, &sfsb) == 0) { 1583 if (sfsb.f_type == RAMFS_MAGIC || 1584 sfsb.f_type == TMPFS_MAGIC) 1585 return 0; 1586 } 1587 1588 if (check_excluded(pathname)) 1589 return 0; 1590 1591 rc = add_xattr_entry(pathname, delete_nonmatch, delete_all); 1592 1593 if (!rc && dir_xattr_list) 1594 *xattr_list = &dir_xattr_list; 1595 else if (rc == -1) 1596 return rc; 1597 1598 return 0; 1599 } 1600 1601 paths[0] = (char *)pathname; 1602 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 1603 1604 fts = fts_open(paths, fts_flags, NULL); 1605 if (!fts) { 1606 selinux_log(SELINUX_ERROR, 1607 "fts error on %s: %m\n", 1608 paths[0]); 1609 return -1; 1610 } 1611 1612 while ((ftsent = fts_read(fts)) != NULL) { 1613 switch (ftsent->fts_info) { 1614 case FTS_DP: 1615 continue; 1616 case FTS_D: 1617 if (statfs(ftsent->fts_path, &sfsb) == 0) { 1618 if (sfsb.f_type == RAMFS_MAGIC || 1619 sfsb.f_type == TMPFS_MAGIC) 1620 continue; 1621 } 1622 if (check_excluded(ftsent->fts_path)) { 1623 fts_set(fts, ftsent, FTS_SKIP); 1624 continue; 1625 } 1626 1627 rc = add_xattr_entry(ftsent->fts_path, 1628 delete_nonmatch, delete_all); 1629 if (rc == 1) 1630 continue; 1631 else if (rc == -1) 1632 goto cleanup; 1633 break; 1634 default: 1635 break; 1636 } 1637 } 1638 1639 if (dir_xattr_list) 1640 *xattr_list = &dir_xattr_list; 1641 1642 (void) fts_close(fts); 1643 return 0; 1644 1645cleanup: 1646 rc = errno; 1647 (void) fts_close(fts); 1648 errno = rc; 1649 1650 if (dir_xattr_list) { 1651 /* Free any used memory */ 1652 current = dir_xattr_list; 1653 while (current) { 1654 next = current->next; 1655 free(current->directory); 1656 free(current->digest); 1657 free(current); 1658 current = next; 1659 } 1660 } 1661 return -1; 1662} 1663 1664long unsigned selinux_restorecon_get_skipped_errors(void) 1665{ 1666 return skipped_errors; 1667} 1668