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 <fts.h> 18#include <limits.h> 19#include <stdint.h> 20#include <sys/types.h> 21#include <sys/stat.h> 22#include <sys/xattr.h> 23#include <sys/vfs.h> 24#include <sys/statvfs.h> 25#include <sys/utsname.h> 26#include <linux/magic.h> 27#include <libgen.h> 28#include <syslog.h> 29#include <assert.h> 30 31#include <selinux/selinux.h> 32#include <selinux/context.h> 33#include <selinux/label.h> 34#include <selinux/restorecon.h> 35 36#include "callbacks.h" 37#include "selinux_internal.h" 38 39#define RESTORECON_LAST "security.restorecon_last" 40 41#define SYS_PATH "/sys" 42#define SYS_PREFIX SYS_PATH "/" 43 44#define STAR_COUNT 1024 45 46static struct selabel_handle *fc_sehandle = NULL; 47static unsigned char *fc_digest = NULL; 48static size_t fc_digest_len = 0; 49static char *rootpath = NULL; 50static int rootpathlen; 51 52/* Information on excluded fs and directories. */ 53struct edir { 54 char *directory; 55 size_t size; 56 /* True if excluded by selinux_restorecon_set_exclude_list(3). */ 57 bool caller_excluded; 58}; 59#define CALLER_EXCLUDED true 60static bool ignore_mounts; 61static int exclude_non_seclabel_mounts(void); 62static int exclude_count = 0; 63static struct edir *exclude_lst = NULL; 64static uint64_t fc_count = 0; /* Number of files processed so far */ 65static uint64_t efile_count; /* Estimated total number of files */ 66 67/* Store information on directories with xattr's. */ 68struct dir_xattr *dir_xattr_list; 69static struct dir_xattr *dir_xattr_last; 70 71/* restorecon_flags for passing to restorecon_sb() */ 72struct rest_flags { 73 bool nochange; 74 bool verbose; 75 bool progress; 76 bool mass_relabel; 77 bool set_specctx; 78 bool add_assoc; 79 bool ignore_digest; 80 bool recurse; 81 bool userealpath; 82 bool set_xdev; 83 bool abort_on_error; 84 bool syslog_changes; 85 bool log_matches; 86 bool ignore_noent; 87 bool warnonnomatch; 88}; 89 90static void restorecon_init(void) 91{ 92 struct selabel_handle *sehandle = NULL; 93 94 if (!fc_sehandle) { 95 sehandle = selinux_restorecon_default_handle(); 96 selinux_restorecon_set_sehandle(sehandle); 97 } 98 99 efile_count = 0; 100 if (!ignore_mounts) 101 efile_count = exclude_non_seclabel_mounts(); 102} 103 104static pthread_once_t fc_once = PTHREAD_ONCE_INIT; 105 106/* 107 * Manage excluded directories: 108 * remove_exclude() - This removes any conflicting entries as there could be 109 * a case where a non-seclabel fs is mounted on /foo and 110 * then a seclabel fs is mounted on top of it. 111 * However if an entry has been added via 112 * selinux_restorecon_set_exclude_list(3) do not remove. 113 * 114 * add_exclude() - Add a directory/fs to be excluded from labeling. If it 115 * has already been added, then ignore. 116 * 117 * check_excluded() - Check if directory/fs is to be excluded when relabeling. 118 * 119 * file_system_count() - Calculates the the number of files to be processed. 120 * The count is only used if SELINUX_RESTORECON_PROGRESS 121 * is set and a mass relabel is requested. 122 * 123 * exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what 124 * non-seclabel mounts to exclude from 125 * relabeling. restorecon_init() will not 126 * call this function if the 127 * SELINUX_RESTORECON_IGNORE_MOUNTS 128 * flag is set. 129 * Setting SELINUX_RESTORECON_IGNORE_MOUNTS 130 * is useful where there is a non-seclabel fs 131 * mounted on /foo and then a seclabel fs is 132 * mounted on a directory below this. 133 */ 134static void remove_exclude(const char *directory) 135{ 136 int i; 137 138 for (i = 0; i < exclude_count; i++) { 139 if (strcmp(directory, exclude_lst[i].directory) == 0 && 140 !exclude_lst[i].caller_excluded) { 141 free(exclude_lst[i].directory); 142 if (i != exclude_count - 1) 143 exclude_lst[i] = exclude_lst[exclude_count - 1]; 144 exclude_count--; 145 return; 146 } 147 } 148} 149 150static int add_exclude(const char *directory, bool who) 151{ 152 struct edir *tmp_list, *current; 153 size_t len = 0; 154 int i; 155 156 /* Check if already present. */ 157 for (i = 0; i < exclude_count; i++) { 158 if (strcmp(directory, exclude_lst[i].directory) == 0) 159 return 0; 160 } 161 162 if (directory == NULL || directory[0] != '/') { 163 selinux_log(SELINUX_ERROR, 164 "Full path required for exclude: %s.\n", 165 directory); 166 errno = EINVAL; 167 return -1; 168 } 169 170 tmp_list = realloc(exclude_lst, 171 sizeof(struct edir) * (exclude_count + 1)); 172 if (!tmp_list) 173 goto oom; 174 175 exclude_lst = tmp_list; 176 177 len = strlen(directory); 178 while (len > 1 && directory[len - 1] == '/') 179 len--; 180 181 current = (exclude_lst + exclude_count); 182 183 current->directory = strndup(directory, len); 184 if (!current->directory) 185 goto oom; 186 187 current->size = len; 188 current->caller_excluded = who; 189 exclude_count++; 190 return 0; 191 192oom: 193 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 194 return -1; 195} 196 197static int check_excluded(const char *file) 198{ 199 int i; 200 201 for (i = 0; i < exclude_count; i++) { 202 if (strncmp(file, exclude_lst[i].directory, 203 exclude_lst[i].size) == 0) { 204 if (file[exclude_lst[i].size] == 0 || 205 file[exclude_lst[i].size] == '/') 206 return 1; 207 } 208 } 209 return 0; 210} 211 212static int file_system_count(char *name) 213{ 214 struct statvfs statvfs_buf; 215 int nfile = 0; 216 217 memset(&statvfs_buf, 0, sizeof(statvfs_buf)); 218 if (!statvfs(name, &statvfs_buf)) 219 nfile = statvfs_buf.f_files - statvfs_buf.f_ffree; 220 221 return nfile; 222} 223 224/* 225 * This is called once when selinux_restorecon() is first called. 226 * Searches /proc/mounts for all file systems that do not support extended 227 * attributes and adds them to the exclude directory table. File systems 228 * that support security labels have the seclabel option, return 229 * approximate total file count. 230 */ 231static int exclude_non_seclabel_mounts(void) 232{ 233 struct utsname uts; 234 FILE *fp; 235 size_t len; 236 ssize_t num; 237 int index = 0, found = 0, nfile = 0; 238 char *mount_info[4]; 239 char *buf = NULL, *item; 240 241 /* Check to see if the kernel supports seclabel */ 242 if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) 243 return 0; 244 245 fp = fopen("/proc/mounts", "r"); 246 if (!fp) 247 return 0; 248 249 while ((num = getline(&buf, &len, fp)) != -1) { 250 found = 0; 251 index = 0; 252 item = strtok(buf, " "); 253 while (item != NULL) { 254 mount_info[index] = item; 255 if (index == 3) 256 break; 257 index++; 258 item = strtok(NULL, " "); 259 } 260 if (index < 3) { 261 selinux_log(SELINUX_ERROR, 262 "/proc/mounts record \"%s\" has incorrect format.\n", 263 buf); 264 continue; 265 } 266 267 /* Remove pre-existing entry */ 268 remove_exclude(mount_info[1]); 269 270 item = strtok(mount_info[3], ","); 271 while (item != NULL) { 272 if (strcmp(item, "seclabel") == 0) { 273 found = 1; 274 nfile += file_system_count(mount_info[1]); 275 break; 276 } 277 item = strtok(NULL, ","); 278 } 279 280 /* Exclude mount points without the seclabel option */ 281 if (!found) { 282 if (add_exclude(mount_info[1], !CALLER_EXCLUDED) && 283 errno == ENOMEM) 284 assert(0); 285 } 286 } 287 288 free(buf); 289 fclose(fp); 290 /* return estimated #Files + 5% for directories and hard links */ 291 return nfile * 1.05; 292} 293 294/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */ 295static int add_xattr_entry(const char *directory, bool delete_nonmatch, 296 bool delete_all) 297{ 298 char *sha1_buf = NULL; 299 unsigned char *xattr_value = NULL; 300 ssize_t xattr_size; 301 size_t i; 302 int rc, digest_result; 303 struct dir_xattr *new_entry; 304 305 if (!directory) { 306 errno = EINVAL; 307 return -1; 308 } 309 310 xattr_value = malloc(fc_digest_len); 311 if (!xattr_value) 312 goto oom; 313 314 xattr_size = getxattr(directory, RESTORECON_LAST, xattr_value, 315 fc_digest_len); 316 if (xattr_size < 0) { 317 free(xattr_value); 318 return 1; 319 } 320 321 /* Convert entry to a hex encoded string. */ 322 sha1_buf = malloc(xattr_size * 2 + 1); 323 if (!sha1_buf) { 324 free(xattr_value); 325 goto oom; 326 } 327 328 for (i = 0; i < (size_t)xattr_size; i++) 329 sprintf((&sha1_buf[i * 2]), "%02x", xattr_value[i]); 330 331 rc = memcmp(fc_digest, xattr_value, fc_digest_len); 332 digest_result = rc ? NOMATCH : MATCH; 333 334 if ((delete_nonmatch && rc != 0) || delete_all) { 335 digest_result = rc ? DELETED_NOMATCH : DELETED_MATCH; 336 rc = removexattr(directory, RESTORECON_LAST); 337 if (rc) { 338 selinux_log(SELINUX_ERROR, 339 "Error: %s removing xattr \"%s\" from: %s\n", 340 strerror(errno), RESTORECON_LAST, directory); 341 digest_result = ERROR; 342 } 343 } 344 free(xattr_value); 345 346 /* Now add entries to link list. */ 347 new_entry = malloc(sizeof(struct dir_xattr)); 348 if (!new_entry) 349 goto oom; 350 new_entry->next = NULL; 351 352 new_entry->directory = strdup(directory); 353 if (!new_entry->directory) 354 goto oom; 355 356 new_entry->digest = strdup(sha1_buf); 357 if (!new_entry->digest) 358 goto oom; 359 360 new_entry->result = digest_result; 361 362 if (!dir_xattr_list) { 363 dir_xattr_list = new_entry; 364 dir_xattr_last = new_entry; 365 } else { 366 dir_xattr_last->next = new_entry; 367 dir_xattr_last = new_entry; 368 } 369 370 free(sha1_buf); 371 return 0; 372 373oom: 374 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 375 return -1; 376} 377 378/* 379 * Support filespec services filespec_add(), filespec_eval() and 380 * filespec_destroy(). 381 * 382 * selinux_restorecon(3) uses filespec services when the 383 * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between 384 * an inode and a specification. 385 */ 386 387/* 388 * The hash table of associations, hashed by inode number. Chaining is used 389 * for collisions, with elements ordered by inode number in each bucket. 390 * Each hash bucket has a dummy header. 391 */ 392#define HASH_BITS 16 393#define HASH_BUCKETS (1 << HASH_BITS) 394#define HASH_MASK (HASH_BUCKETS-1) 395 396/* 397 * An association between an inode and a context. 398 */ 399typedef struct file_spec { 400 ino_t ino; /* inode number */ 401 char *con; /* matched context */ 402 char *file; /* full pathname */ 403 struct file_spec *next; /* next association in hash bucket chain */ 404} file_spec_t; 405 406static file_spec_t *fl_head; 407 408/* 409 * Try to add an association between an inode and a context. If there is a 410 * different context that matched the inode, then use the first context 411 * that matched. 412 */ 413static int filespec_add(ino_t ino, const char *con, const char *file) 414{ 415 file_spec_t *prevfl, *fl; 416 int h, ret; 417 struct stat64 sb; 418 419 if (!fl_head) { 420 fl_head = malloc(sizeof(file_spec_t) * HASH_BUCKETS); 421 if (!fl_head) 422 goto oom; 423 memset(fl_head, 0, sizeof(file_spec_t) * HASH_BUCKETS); 424 } 425 426 h = (ino + (ino >> HASH_BITS)) & HASH_MASK; 427 for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; 428 prevfl = fl, fl = fl->next) { 429 if (ino == fl->ino) { 430 ret = lstat64(fl->file, &sb); 431 if (ret < 0 || sb.st_ino != ino) { 432 freecon(fl->con); 433 free(fl->file); 434 fl->file = strdup(file); 435 if (!fl->file) 436 goto oom; 437 fl->con = strdup(con); 438 if (!fl->con) 439 goto oom; 440 return 1; 441 } 442 443 if (strcmp(fl->con, con) == 0) 444 return 1; 445 446 selinux_log(SELINUX_ERROR, 447 "conflicting specifications for %s and %s, using %s.\n", 448 file, fl->file, fl->con); 449 free(fl->file); 450 fl->file = strdup(file); 451 if (!fl->file) 452 goto oom; 453 return 1; 454 } 455 456 if (ino > fl->ino) 457 break; 458 } 459 460 fl = malloc(sizeof(file_spec_t)); 461 if (!fl) 462 goto oom; 463 fl->ino = ino; 464 fl->con = strdup(con); 465 if (!fl->con) 466 goto oom_freefl; 467 fl->file = strdup(file); 468 if (!fl->file) 469 goto oom_freefl; 470 fl->next = prevfl->next; 471 prevfl->next = fl; 472 return 0; 473 474oom_freefl: 475 free(fl); 476oom: 477 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 478 return -1; 479} 480 481/* 482 * Evaluate the association hash table distribution. 483 */ 484#ifdef DEBUG 485static void filespec_eval(void) 486{ 487 file_spec_t *fl; 488 int h, used, nel, len, longest; 489 490 if (!fl_head) 491 return; 492 493 used = 0; 494 longest = 0; 495 nel = 0; 496 for (h = 0; h < HASH_BUCKETS; h++) { 497 len = 0; 498 for (fl = fl_head[h].next; fl; fl = fl->next) 499 len++; 500 if (len) 501 used++; 502 if (len > longest) 503 longest = len; 504 nel += len; 505 } 506 507 selinux_log(SELINUX_INFO, 508 "filespec hash table stats: %d elements, %d/%d buckets used, longest chain length %d\n", 509 nel, used, HASH_BUCKETS, longest); 510} 511#else 512static void filespec_eval(void) 513{ 514} 515#endif 516 517/* 518 * Destroy the association hash table. 519 */ 520static void filespec_destroy(void) 521{ 522 file_spec_t *fl, *tmp; 523 int h; 524 525 if (!fl_head) 526 return; 527 528 for (h = 0; h < HASH_BUCKETS; h++) { 529 fl = fl_head[h].next; 530 while (fl) { 531 tmp = fl; 532 fl = fl->next; 533 freecon(tmp->con); 534 free(tmp->file); 535 free(tmp); 536 } 537 fl_head[h].next = NULL; 538 } 539 free(fl_head); 540 fl_head = NULL; 541} 542 543/* 544 * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if 545 * the type components differ, updating newtypecon if so. 546 */ 547static int compare_types(char *curcon, char *newcon, char **newtypecon) 548{ 549 int types_differ = 0; 550 context_t cona; 551 context_t conb; 552 int rc = 0; 553 554 cona = context_new(curcon); 555 if (!cona) { 556 rc = -1; 557 goto out; 558 } 559 conb = context_new(newcon); 560 if (!conb) { 561 context_free(cona); 562 rc = -1; 563 goto out; 564 } 565 566 types_differ = strcmp(context_type_get(cona), context_type_get(conb)); 567 if (types_differ) { 568 rc |= context_user_set(conb, context_user_get(cona)); 569 rc |= context_role_set(conb, context_role_get(cona)); 570 rc |= context_range_set(conb, context_range_get(cona)); 571 if (!rc) { 572 *newtypecon = strdup(context_str(conb)); 573 if (!*newtypecon) { 574 rc = -1; 575 goto err; 576 } 577 } 578 } 579 580err: 581 context_free(cona); 582 context_free(conb); 583out: 584 return rc; 585} 586 587static int restorecon_sb(const char *pathname, const struct stat *sb, 588 struct rest_flags *flags) 589{ 590 char *newcon = NULL; 591 char *curcon = NULL; 592 char *newtypecon = NULL; 593 int rc; 594 bool updated = false; 595 const char *lookup_path = pathname; 596 float pc; 597 598 if (rootpath) { 599 if (strncmp(rootpath, lookup_path, rootpathlen) != 0) { 600 selinux_log(SELINUX_ERROR, 601 "%s is not located in alt_rootpath %s\n", 602 lookup_path, rootpath); 603 return -1; 604 } 605 lookup_path += rootpathlen; 606 } 607 608 if (rootpath != NULL && lookup_path[0] == '\0') 609 /* this is actually the root dir of the alt root. */ 610 rc = selabel_lookup_raw(fc_sehandle, &newcon, "/", 611 sb->st_mode); 612 else 613 rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path, 614 sb->st_mode); 615 616 if (rc < 0) { 617 if (errno == ENOENT && flags->warnonnomatch) 618 selinux_log(SELINUX_INFO, 619 "Warning no default label for %s\n", 620 lookup_path); 621 622 return 0; /* no match, but not an error */ 623 } 624 625 if (flags->progress) { 626 fc_count++; 627 if (fc_count % STAR_COUNT == 0) { 628 if (flags->mass_relabel && efile_count > 0) { 629 pc = (fc_count < efile_count) ? (100.0 * 630 fc_count / efile_count) : 100; 631 fprintf(stdout, "\r%-.1f%%", (double)pc); 632 } else { 633 fprintf(stdout, "\r%luk", fc_count / STAR_COUNT); 634 } 635 fflush(stdout); 636 } 637 } 638 639 if (flags->add_assoc) { 640 rc = filespec_add(sb->st_ino, newcon, pathname); 641 642 if (rc < 0) { 643 selinux_log(SELINUX_ERROR, 644 "filespec_add error: %s\n", pathname); 645 freecon(newcon); 646 return -1; 647 } 648 649 if (rc > 0) { 650 /* Already an association and it took precedence. */ 651 freecon(newcon); 652 return 0; 653 } 654 } 655 656 if (flags->log_matches) 657 selinux_log(SELINUX_INFO, "%s matched by %s\n", 658 pathname, newcon); 659 660 if (lgetfilecon_raw(pathname, &curcon) < 0) { 661 if (errno != ENODATA) 662 goto err; 663 664 curcon = NULL; 665 } 666 667 if (strcmp(curcon, newcon) != 0) { 668 if (!flags->set_specctx && curcon && 669 (is_context_customizable(curcon) > 0)) { 670 if (flags->verbose) { 671 selinux_log(SELINUX_INFO, 672 "%s not reset as customized by admin to %s\n", 673 pathname, curcon); 674 goto out; 675 } 676 } 677 678 if (!flags->set_specctx && curcon) { 679 /* If types different then update newcon. */ 680 rc = compare_types(curcon, newcon, &newtypecon); 681 if (rc) 682 goto err; 683 684 if (newtypecon) { 685 freecon(newcon); 686 newcon = newtypecon; 687 } else { 688 goto out; 689 } 690 } 691 692 if (!flags->nochange) { 693 if (lsetfilecon(pathname, newcon) < 0) 694 goto err; 695 updated = true; 696 } 697 698 if (flags->verbose) 699 selinux_log(SELINUX_INFO, 700 "%s %s from %s to %s\n", 701 updated ? "Relabeled" : "Would relabel", 702 pathname, curcon, newcon); 703 704 if (flags->syslog_changes && !flags->nochange) { 705 if (curcon) 706 syslog(LOG_INFO, 707 "relabeling %s from %s to %s\n", 708 pathname, curcon, newcon); 709 else 710 syslog(LOG_INFO, "labeling %s to %s\n", 711 pathname, newcon); 712 } 713 } 714 715out: 716 rc = 0; 717out1: 718 freecon(curcon); 719 freecon(newcon); 720 return rc; 721err: 722 selinux_log(SELINUX_ERROR, 723 "Could not set context for %s: %s\n", 724 pathname, strerror(errno)); 725 rc = -1; 726 goto out1; 727} 728 729/* 730 * Public API 731 */ 732 733/* selinux_restorecon(3) - Main function that is responsible for labeling */ 734int selinux_restorecon(const char *pathname_orig, 735 unsigned int restorecon_flags) 736{ 737 struct rest_flags flags; 738 739 flags.ignore_digest = (restorecon_flags & 740 SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false; 741 flags.nochange = (restorecon_flags & 742 SELINUX_RESTORECON_NOCHANGE) ? true : false; 743 flags.verbose = (restorecon_flags & 744 SELINUX_RESTORECON_VERBOSE) ? true : false; 745 flags.progress = (restorecon_flags & 746 SELINUX_RESTORECON_PROGRESS) ? true : false; 747 flags.mass_relabel = (restorecon_flags & 748 SELINUX_RESTORECON_MASS_RELABEL) ? true : false; 749 flags.recurse = (restorecon_flags & 750 SELINUX_RESTORECON_RECURSE) ? true : false; 751 flags.set_specctx = (restorecon_flags & 752 SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false; 753 flags.userealpath = (restorecon_flags & 754 SELINUX_RESTORECON_REALPATH) ? true : false; 755 flags.set_xdev = (restorecon_flags & 756 SELINUX_RESTORECON_XDEV) ? true : false; 757 flags.add_assoc = (restorecon_flags & 758 SELINUX_RESTORECON_ADD_ASSOC) ? true : false; 759 flags.abort_on_error = (restorecon_flags & 760 SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false; 761 flags.syslog_changes = (restorecon_flags & 762 SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false; 763 flags.log_matches = (restorecon_flags & 764 SELINUX_RESTORECON_LOG_MATCHES) ? true : false; 765 flags.ignore_noent = (restorecon_flags & 766 SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false; 767 flags.warnonnomatch = true; 768 ignore_mounts = (restorecon_flags & 769 SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false; 770 771 bool issys; 772 bool setrestoreconlast = true; /* TRUE = set xattr RESTORECON_LAST 773 * FALSE = don't use xattr */ 774 struct stat sb; 775 struct statfs sfsb; 776 FTS *fts; 777 FTSENT *ftsent; 778 char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname; 779 char *paths[2] = { NULL, NULL }; 780 int fts_flags, error, sverrno; 781 char *xattr_value = NULL; 782 ssize_t size; 783 dev_t dev_num = 0; 784 785 if (flags.verbose && flags.progress) 786 flags.verbose = false; 787 788 __selinux_once(fc_once, restorecon_init); 789 790 if (!fc_sehandle) 791 return -1; 792 793 if (fc_digest_len) { 794 xattr_value = malloc(fc_digest_len); 795 if (!xattr_value) 796 return -1; 797 } 798 799 /* 800 * Convert passed-in pathname to canonical pathname by resolving 801 * realpath of containing dir, then appending last component name. 802 */ 803 if (flags.userealpath) { 804 char *basename_cpy = strdup(pathname_orig); 805 if (!basename_cpy) 806 goto realpatherr; 807 pathbname = basename(basename_cpy); 808 if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || 809 !strcmp(pathbname, "..")) { 810 pathname = realpath(pathname_orig, NULL); 811 if (!pathname) { 812 free(basename_cpy); 813 goto realpatherr; 814 } 815 } else { 816 char *dirname_cpy = strdup(pathname_orig); 817 if (!dirname_cpy) { 818 free(basename_cpy); 819 goto realpatherr; 820 } 821 pathdname = dirname(dirname_cpy); 822 pathdnamer = realpath(pathdname, NULL); 823 free(dirname_cpy); 824 if (!pathdnamer) { 825 free(basename_cpy); 826 goto realpatherr; 827 } 828 if (!strcmp(pathdnamer, "/")) 829 error = asprintf(&pathname, "/%s", pathbname); 830 else 831 error = asprintf(&pathname, "%s/%s", 832 pathdnamer, pathbname); 833 if (error < 0) { 834 free(basename_cpy); 835 goto oom; 836 } 837 } 838 free(basename_cpy); 839 } else { 840 pathname = strdup(pathname_orig); 841 if (!pathname) 842 goto oom; 843 } 844 845 paths[0] = pathname; 846 issys = (!strcmp(pathname, SYS_PATH) || 847 !strncmp(pathname, SYS_PREFIX, 848 sizeof(SYS_PREFIX) - 1)) ? true : false; 849 850 if (lstat(pathname, &sb) < 0) { 851 if (flags.ignore_noent && errno == ENOENT) { 852 free(pathdnamer); 853 free(pathname); 854 return 0; 855 } else { 856 selinux_log(SELINUX_ERROR, 857 "lstat(%s) failed: %s\n", 858 pathname, strerror(errno)); 859 error = -1; 860 goto cleanup; 861 } 862 } 863 864 /* Ignore restoreconlast if not a directory */ 865 if ((sb.st_mode & S_IFDIR) != S_IFDIR) 866 setrestoreconlast = false; 867 868 if (!flags.recurse) { 869 if (check_excluded(pathname)) { 870 error = 0; 871 goto cleanup; 872 } 873 874 error = restorecon_sb(pathname, &sb, &flags); 875 goto cleanup; 876 } 877 878 /* Ignore restoreconlast on /sys */ 879 if (issys) 880 setrestoreconlast = false; 881 882 /* Ignore restoreconlast on in-memory filesystems */ 883 if (statfs(pathname, &sfsb) == 0) { 884 if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC) 885 setrestoreconlast = false; 886 } 887 888 if (setrestoreconlast) { 889 size = getxattr(pathname, RESTORECON_LAST, xattr_value, 890 fc_digest_len); 891 892 if (!flags.ignore_digest && (size_t)size == fc_digest_len && 893 memcmp(fc_digest, xattr_value, fc_digest_len) 894 == 0) { 895 selinux_log(SELINUX_INFO, 896 "Skipping restorecon as matching digest on: %s\n", 897 pathname); 898 error = 0; 899 goto cleanup; 900 } 901 } 902 903 if (flags.set_xdev) 904 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV; 905 else 906 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 907 908 fts = fts_open(paths, fts_flags, NULL); 909 if (!fts) 910 goto fts_err; 911 912 ftsent = fts_read(fts); 913 if (!ftsent) 914 goto fts_err; 915 916 /* 917 * Keep the inode of the first device. This is because the FTS_XDEV 918 * flag tells fts not to descend into directories with different 919 * device numbers, but fts will still give back the actual directory. 920 * By saving the device number of the directory that was passed to 921 * selinux_restorecon() and then skipping all actions on any 922 * directories with a different device number when the FTS_XDEV flag 923 * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2). 924 */ 925 dev_num = ftsent->fts_statp->st_dev; 926 927 error = 0; 928 do { 929 /* If the FTS_XDEV flag is set and the device is different */ 930 if (flags.set_xdev && ftsent->fts_statp->st_dev != dev_num) 931 continue; 932 933 switch (ftsent->fts_info) { 934 case FTS_DC: 935 selinux_log(SELINUX_ERROR, 936 "Directory cycle on %s.\n", 937 ftsent->fts_path); 938 errno = ELOOP; 939 error = -1; 940 goto out; 941 case FTS_DP: 942 continue; 943 case FTS_DNR: 944 selinux_log(SELINUX_ERROR, 945 "Could not read %s: %s.\n", 946 ftsent->fts_path, 947 strerror(ftsent->fts_errno)); 948 fts_set(fts, ftsent, FTS_SKIP); 949 continue; 950 case FTS_NS: 951 selinux_log(SELINUX_ERROR, 952 "Could not stat %s: %s.\n", 953 ftsent->fts_path, 954 strerror(ftsent->fts_errno)); 955 fts_set(fts, ftsent, FTS_SKIP); 956 continue; 957 case FTS_ERR: 958 selinux_log(SELINUX_ERROR, 959 "Error on %s: %s.\n", 960 ftsent->fts_path, 961 strerror(ftsent->fts_errno)); 962 fts_set(fts, ftsent, FTS_SKIP); 963 continue; 964 case FTS_D: 965 if (issys && !selabel_partial_match(fc_sehandle, 966 ftsent->fts_path)) { 967 fts_set(fts, ftsent, FTS_SKIP); 968 continue; 969 } 970 971 if (check_excluded(ftsent->fts_path)) { 972 fts_set(fts, ftsent, FTS_SKIP); 973 continue; 974 } 975 /* fall through */ 976 default: 977 error |= restorecon_sb(ftsent->fts_path, 978 ftsent->fts_statp, &flags); 979 if (flags.warnonnomatch) 980 flags.warnonnomatch = false; 981 if (error && flags.abort_on_error) 982 goto out; 983 break; 984 } 985 } while ((ftsent = fts_read(fts)) != NULL); 986 987 /* Labeling successful. Mark the top level directory as completed. */ 988 if (setrestoreconlast && !flags.nochange && !error && fc_digest) { 989 error = setxattr(pathname, RESTORECON_LAST, fc_digest, 990 fc_digest_len, 0); 991 if (!error && flags.verbose) 992 selinux_log(SELINUX_INFO, 993 "Updated digest for: %s\n", pathname); 994 } 995 996out: 997 if (flags.progress && flags.mass_relabel) 998 fprintf(stdout, "\r%s 100.0%%\n", pathname); 999 1000 sverrno = errno; 1001 (void) fts_close(fts); 1002 errno = sverrno; 1003cleanup: 1004 if (flags.add_assoc) { 1005 if (flags.verbose) 1006 filespec_eval(); 1007 filespec_destroy(); 1008 } 1009 free(pathdnamer); 1010 free(pathname); 1011 free(xattr_value); 1012 return error; 1013 1014oom: 1015 sverrno = errno; 1016 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 1017 errno = sverrno; 1018 error = -1; 1019 goto cleanup; 1020 1021realpatherr: 1022 sverrno = errno; 1023 selinux_log(SELINUX_ERROR, 1024 "SELinux: Could not get canonical path for %s restorecon: %s.\n", 1025 pathname_orig, strerror(errno)); 1026 errno = sverrno; 1027 error = -1; 1028 goto cleanup; 1029 1030fts_err: 1031 selinux_log(SELINUX_ERROR, 1032 "fts error while labeling %s: %s\n", 1033 paths[0], strerror(errno)); 1034 error = -1; 1035 goto cleanup; 1036} 1037 1038/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */ 1039void selinux_restorecon_set_sehandle(struct selabel_handle *hndl) 1040{ 1041 char **specfiles; 1042 size_t num_specfiles; 1043 1044 fc_sehandle = (struct selabel_handle *) hndl; 1045 1046 /* 1047 * Read digest if requested in selabel_open(3) and set global params. 1048 */ 1049 if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len, 1050 &specfiles, &num_specfiles) < 0) { 1051 fc_digest = NULL; 1052 fc_digest_len = 0; 1053 } 1054} 1055 1056/* 1057 * selinux_restorecon_default_handle(3) is called to set the global restorecon 1058 * handle by a process if the default params are required. 1059 */ 1060struct selabel_handle *selinux_restorecon_default_handle(void) 1061{ 1062 struct selabel_handle *sehandle; 1063 1064 struct selinux_opt fc_opts[] = { 1065 { SELABEL_OPT_DIGEST, (char *)1 } 1066 }; 1067 1068 sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1); 1069 1070 if (!sehandle) { 1071 selinux_log(SELINUX_ERROR, 1072 "Error obtaining file context handle: %s\n", 1073 strerror(errno)); 1074 return NULL; 1075 } 1076 1077 return sehandle; 1078} 1079 1080/* 1081 * selinux_restorecon_set_exclude_list(3) is called to add additional entries 1082 * to be excluded from labeling checks. 1083 */ 1084void selinux_restorecon_set_exclude_list(const char **exclude_list) 1085{ 1086 int i; 1087 struct stat sb; 1088 1089 for (i = 0; exclude_list[i]; i++) { 1090 if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) { 1091 selinux_log(SELINUX_ERROR, 1092 "lstat error on exclude path \"%s\", %s - ignoring.\n", 1093 exclude_list[i], strerror(errno)); 1094 break; 1095 } 1096 if (add_exclude(exclude_list[i], CALLER_EXCLUDED) && 1097 errno == ENOMEM) 1098 assert(0); 1099 } 1100} 1101 1102/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */ 1103int selinux_restorecon_set_alt_rootpath(const char *alt_rootpath) 1104{ 1105 int len; 1106 1107 /* This should be NULL on first use */ 1108 if (rootpath) 1109 free(rootpath); 1110 1111 rootpath = strdup(alt_rootpath); 1112 if (!rootpath) { 1113 selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 1114 return -1; 1115 } 1116 1117 /* trim trailing /, if present */ 1118 len = strlen(rootpath); 1119 while (len && (rootpath[len - 1] == '/')) 1120 rootpath[--len] = '\0'; 1121 rootpathlen = len; 1122 1123 return 0; 1124} 1125 1126/* selinux_restorecon_xattr(3) - Find RESTORECON_LAST entries. */ 1127int selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags, 1128 struct dir_xattr ***xattr_list) 1129{ 1130 bool recurse = (xattr_flags & 1131 SELINUX_RESTORECON_XATTR_RECURSE) ? true : false; 1132 bool delete_nonmatch = (xattr_flags & 1133 SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false; 1134 bool delete_all = (xattr_flags & 1135 SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false; 1136 ignore_mounts = (xattr_flags & 1137 SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false; 1138 1139 int rc, fts_flags; 1140 struct stat sb; 1141 struct statfs sfsb; 1142 struct dir_xattr *current, *next; 1143 FTS *fts; 1144 FTSENT *ftsent; 1145 char *paths[2] = { NULL, NULL }; 1146 1147 __selinux_once(fc_once, restorecon_init); 1148 1149 if (!fc_sehandle || !fc_digest_len) 1150 return -1; 1151 1152 if (lstat(pathname, &sb) < 0) { 1153 if (errno == ENOENT) 1154 return 0; 1155 1156 selinux_log(SELINUX_ERROR, 1157 "lstat(%s) failed: %s\n", 1158 pathname, strerror(errno)); 1159 return -1; 1160 } 1161 1162 if (!recurse) { 1163 if (statfs(pathname, &sfsb) == 0) { 1164 if (sfsb.f_type == RAMFS_MAGIC || 1165 sfsb.f_type == TMPFS_MAGIC) 1166 return 0; 1167 } 1168 1169 if (check_excluded(pathname)) 1170 return 0; 1171 1172 rc = add_xattr_entry(pathname, delete_nonmatch, delete_all); 1173 1174 if (!rc && dir_xattr_list) 1175 *xattr_list = &dir_xattr_list; 1176 else if (rc == -1) 1177 return rc; 1178 1179 return 0; 1180 } 1181 1182 paths[0] = (char *)pathname; 1183 fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 1184 1185 fts = fts_open(paths, fts_flags, NULL); 1186 if (!fts) { 1187 selinux_log(SELINUX_ERROR, 1188 "fts error on %s: %s\n", 1189 paths[0], strerror(errno)); 1190 return -1; 1191 } 1192 1193 while ((ftsent = fts_read(fts)) != NULL) { 1194 switch (ftsent->fts_info) { 1195 case FTS_DP: 1196 continue; 1197 case FTS_D: 1198 if (statfs(ftsent->fts_path, &sfsb) == 0) { 1199 if (sfsb.f_type == RAMFS_MAGIC || 1200 sfsb.f_type == TMPFS_MAGIC) 1201 continue; 1202 } 1203 if (check_excluded(ftsent->fts_path)) { 1204 fts_set(fts, ftsent, FTS_SKIP); 1205 continue; 1206 } 1207 1208 rc = add_xattr_entry(ftsent->fts_path, 1209 delete_nonmatch, delete_all); 1210 if (rc == 1) 1211 continue; 1212 else if (rc == -1) 1213 goto cleanup; 1214 break; 1215 default: 1216 break; 1217 } 1218 } 1219 1220 if (dir_xattr_list) 1221 *xattr_list = &dir_xattr_list; 1222 1223 (void) fts_close(fts); 1224 return 0; 1225 1226cleanup: 1227 rc = errno; 1228 (void) fts_close(fts); 1229 errno = rc; 1230 1231 if (dir_xattr_list) { 1232 /* Free any used memory */ 1233 current = dir_xattr_list; 1234 while (current) { 1235 next = current->next; 1236 free(current->directory); 1237 free(current->digest); 1238 free(current); 1239 current = next; 1240 } 1241 } 1242 return -1; 1243} 1244