16cd6a6acSopenharmony_ci/* 26cd6a6acSopenharmony_ci * The majority of this code is from Android's 36cd6a6acSopenharmony_ci * external/libselinux/src/android.c and upstream 46cd6a6acSopenharmony_ci * selinux/policycoreutils/setfiles/restore.c 56cd6a6acSopenharmony_ci * 66cd6a6acSopenharmony_ci * See selinux_restorecon(3) for details. 76cd6a6acSopenharmony_ci */ 86cd6a6acSopenharmony_ci 96cd6a6acSopenharmony_ci#include <unistd.h> 106cd6a6acSopenharmony_ci#include <string.h> 116cd6a6acSopenharmony_ci#include <stdio.h> 126cd6a6acSopenharmony_ci#include <stdlib.h> 136cd6a6acSopenharmony_ci#include <stdbool.h> 146cd6a6acSopenharmony_ci#include <ctype.h> 156cd6a6acSopenharmony_ci#include <errno.h> 166cd6a6acSopenharmony_ci#include <fcntl.h> 176cd6a6acSopenharmony_ci#include <include/fts.h> 186cd6a6acSopenharmony_ci#include <inttypes.h> 196cd6a6acSopenharmony_ci#include <limits.h> 206cd6a6acSopenharmony_ci#include <stdint.h> 216cd6a6acSopenharmony_ci#include <sys/types.h> 226cd6a6acSopenharmony_ci#include <sys/stat.h> 236cd6a6acSopenharmony_ci#include <sys/xattr.h> 246cd6a6acSopenharmony_ci#include <sys/vfs.h> 256cd6a6acSopenharmony_ci#include <sys/statvfs.h> 266cd6a6acSopenharmony_ci#include <sys/utsname.h> 276cd6a6acSopenharmony_ci#include <linux/magic.h> 286cd6a6acSopenharmony_ci#include <libgen.h> 296cd6a6acSopenharmony_ci#include <syslog.h> 306cd6a6acSopenharmony_ci#include <assert.h> 316cd6a6acSopenharmony_ci 326cd6a6acSopenharmony_ci#include <selinux/selinux.h> 336cd6a6acSopenharmony_ci#include <selinux/context.h> 346cd6a6acSopenharmony_ci#include <selinux/label.h> 356cd6a6acSopenharmony_ci#include <selinux/restorecon.h> 366cd6a6acSopenharmony_ci#include <selinux/skip_elx_constants.h> 376cd6a6acSopenharmony_ci 386cd6a6acSopenharmony_ci#include "ignore_path.h" 396cd6a6acSopenharmony_ci#include "callbacks.h" 406cd6a6acSopenharmony_ci#include "selinux_internal.h" 416cd6a6acSopenharmony_ci#include "label_file.h" 426cd6a6acSopenharmony_ci#include "sha1.h" 436cd6a6acSopenharmony_ci 446cd6a6acSopenharmony_ci#define STAR_COUNT 1024 456cd6a6acSopenharmony_ci 466cd6a6acSopenharmony_cistatic struct selabel_handle *fc_sehandle = NULL; 476cd6a6acSopenharmony_cistatic bool selabel_no_digest; 486cd6a6acSopenharmony_cistatic char *rootpath = NULL; 496cd6a6acSopenharmony_cistatic size_t rootpathlen; 506cd6a6acSopenharmony_ci 516cd6a6acSopenharmony_ci/* Information on excluded fs and directories. */ 526cd6a6acSopenharmony_cistruct edir { 536cd6a6acSopenharmony_ci char *directory; 546cd6a6acSopenharmony_ci size_t size; 556cd6a6acSopenharmony_ci /* True if excluded by selinux_restorecon_set_exclude_list(3). */ 566cd6a6acSopenharmony_ci bool caller_excluded; 576cd6a6acSopenharmony_ci}; 586cd6a6acSopenharmony_ci#define CALLER_EXCLUDED true 596cd6a6acSopenharmony_cistatic bool ignore_mounts; 606cd6a6acSopenharmony_cistatic uint64_t exclude_non_seclabel_mounts(void); 616cd6a6acSopenharmony_cistatic int exclude_count = 0; 626cd6a6acSopenharmony_cistatic struct edir *exclude_lst = NULL; 636cd6a6acSopenharmony_cistatic uint64_t fc_count = 0; /* Number of files processed so far */ 646cd6a6acSopenharmony_cistatic uint64_t efile_count; /* Estimated total number of files */ 656cd6a6acSopenharmony_cistatic pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER; 666cd6a6acSopenharmony_ci 676cd6a6acSopenharmony_ci/* Store information on directories with xattr's. */ 686cd6a6acSopenharmony_cistatic struct dir_xattr *dir_xattr_list; 696cd6a6acSopenharmony_cistatic struct dir_xattr *dir_xattr_last; 706cd6a6acSopenharmony_ci 716cd6a6acSopenharmony_ci/* Number of errors ignored during the file tree walk. */ 726cd6a6acSopenharmony_cistatic long unsigned skipped_errors; 736cd6a6acSopenharmony_ci 746cd6a6acSopenharmony_ci/* restorecon_flags for passing to restorecon_sb() */ 756cd6a6acSopenharmony_cistruct rest_flags { 766cd6a6acSopenharmony_ci bool nochange; 776cd6a6acSopenharmony_ci bool verbose; 786cd6a6acSopenharmony_ci bool progress; 796cd6a6acSopenharmony_ci bool mass_relabel; 806cd6a6acSopenharmony_ci bool set_specctx; 816cd6a6acSopenharmony_ci bool add_assoc; 826cd6a6acSopenharmony_ci bool recurse; 836cd6a6acSopenharmony_ci bool userealpath; 846cd6a6acSopenharmony_ci bool set_xdev; 856cd6a6acSopenharmony_ci bool abort_on_error; 866cd6a6acSopenharmony_ci bool syslog_changes; 876cd6a6acSopenharmony_ci bool log_matches; 886cd6a6acSopenharmony_ci bool ignore_noent; 896cd6a6acSopenharmony_ci bool warnonnomatch; 906cd6a6acSopenharmony_ci bool conflicterror; 916cd6a6acSopenharmony_ci bool count_errors; 926cd6a6acSopenharmony_ci bool skipelx; 936cd6a6acSopenharmony_ci}; 946cd6a6acSopenharmony_ci 956cd6a6acSopenharmony_cistatic void restorecon_init(void) 966cd6a6acSopenharmony_ci{ 976cd6a6acSopenharmony_ci struct selabel_handle *sehandle = NULL; 986cd6a6acSopenharmony_ci 996cd6a6acSopenharmony_ci if (!fc_sehandle) { 1006cd6a6acSopenharmony_ci sehandle = selinux_restorecon_default_handle(); 1016cd6a6acSopenharmony_ci selinux_restorecon_set_sehandle(sehandle); 1026cd6a6acSopenharmony_ci } 1036cd6a6acSopenharmony_ci 1046cd6a6acSopenharmony_ci efile_count = 0; 1056cd6a6acSopenharmony_ci if (!ignore_mounts) 1066cd6a6acSopenharmony_ci efile_count = exclude_non_seclabel_mounts(); 1076cd6a6acSopenharmony_ci} 1086cd6a6acSopenharmony_ci 1096cd6a6acSopenharmony_cistatic pthread_once_t fc_once = PTHREAD_ONCE_INIT; 1106cd6a6acSopenharmony_ci 1116cd6a6acSopenharmony_ci/* 1126cd6a6acSopenharmony_ci * Manage excluded directories: 1136cd6a6acSopenharmony_ci * remove_exclude() - This removes any conflicting entries as there could be 1146cd6a6acSopenharmony_ci * a case where a non-seclabel fs is mounted on /foo and 1156cd6a6acSopenharmony_ci * then a seclabel fs is mounted on top of it. 1166cd6a6acSopenharmony_ci * However if an entry has been added via 1176cd6a6acSopenharmony_ci * selinux_restorecon_set_exclude_list(3) do not remove. 1186cd6a6acSopenharmony_ci * 1196cd6a6acSopenharmony_ci * add_exclude() - Add a directory/fs to be excluded from labeling. If it 1206cd6a6acSopenharmony_ci * has already been added, then ignore. 1216cd6a6acSopenharmony_ci * 1226cd6a6acSopenharmony_ci * check_excluded() - Check if directory/fs is to be excluded when relabeling. 1236cd6a6acSopenharmony_ci * 1246cd6a6acSopenharmony_ci * file_system_count() - Calculates the number of files to be processed. 1256cd6a6acSopenharmony_ci * The count is only used if SELINUX_RESTORECON_PROGRESS 1266cd6a6acSopenharmony_ci * is set and a mass relabel is requested. 1276cd6a6acSopenharmony_ci * 1286cd6a6acSopenharmony_ci * exclude_non_seclabel_mounts() - Reads /proc/mounts to determine what 1296cd6a6acSopenharmony_ci * non-seclabel mounts to exclude from 1306cd6a6acSopenharmony_ci * relabeling. restorecon_init() will not 1316cd6a6acSopenharmony_ci * call this function if the 1326cd6a6acSopenharmony_ci * SELINUX_RESTORECON_IGNORE_MOUNTS 1336cd6a6acSopenharmony_ci * flag is set. 1346cd6a6acSopenharmony_ci * Setting SELINUX_RESTORECON_IGNORE_MOUNTS 1356cd6a6acSopenharmony_ci * is useful where there is a non-seclabel fs 1366cd6a6acSopenharmony_ci * mounted on /foo and then a seclabel fs is 1376cd6a6acSopenharmony_ci * mounted on a directory below this. 1386cd6a6acSopenharmony_ci */ 1396cd6a6acSopenharmony_cistatic void remove_exclude(const char *directory) 1406cd6a6acSopenharmony_ci{ 1416cd6a6acSopenharmony_ci int i; 1426cd6a6acSopenharmony_ci 1436cd6a6acSopenharmony_ci for (i = 0; i < exclude_count; i++) { 1446cd6a6acSopenharmony_ci if (strcmp(directory, exclude_lst[i].directory) == 0 && 1456cd6a6acSopenharmony_ci !exclude_lst[i].caller_excluded) { 1466cd6a6acSopenharmony_ci free(exclude_lst[i].directory); 1476cd6a6acSopenharmony_ci if (i != exclude_count - 1) 1486cd6a6acSopenharmony_ci exclude_lst[i] = exclude_lst[exclude_count - 1]; 1496cd6a6acSopenharmony_ci exclude_count--; 1506cd6a6acSopenharmony_ci return; 1516cd6a6acSopenharmony_ci } 1526cd6a6acSopenharmony_ci } 1536cd6a6acSopenharmony_ci} 1546cd6a6acSopenharmony_ci 1556cd6a6acSopenharmony_cistatic int add_exclude(const char *directory, bool who) 1566cd6a6acSopenharmony_ci{ 1576cd6a6acSopenharmony_ci struct edir *tmp_list, *current; 1586cd6a6acSopenharmony_ci size_t len = 0; 1596cd6a6acSopenharmony_ci int i; 1606cd6a6acSopenharmony_ci 1616cd6a6acSopenharmony_ci /* Check if already present. */ 1626cd6a6acSopenharmony_ci for (i = 0; i < exclude_count; i++) { 1636cd6a6acSopenharmony_ci if (strcmp(directory, exclude_lst[i].directory) == 0) 1646cd6a6acSopenharmony_ci return 0; 1656cd6a6acSopenharmony_ci } 1666cd6a6acSopenharmony_ci 1676cd6a6acSopenharmony_ci if (directory == NULL || directory[0] != '/') { 1686cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 1696cd6a6acSopenharmony_ci "Full path required for exclude: %s.\n", 1706cd6a6acSopenharmony_ci directory); 1716cd6a6acSopenharmony_ci errno = EINVAL; 1726cd6a6acSopenharmony_ci return -1; 1736cd6a6acSopenharmony_ci } 1746cd6a6acSopenharmony_ci 1756cd6a6acSopenharmony_ci if (exclude_count >= INT_MAX - 1) { 1766cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "Too many directory excludes: %d.\n", exclude_count); 1776cd6a6acSopenharmony_ci errno = EOVERFLOW; 1786cd6a6acSopenharmony_ci return -1; 1796cd6a6acSopenharmony_ci } 1806cd6a6acSopenharmony_ci 1816cd6a6acSopenharmony_ci tmp_list = realloc(exclude_lst, 1826cd6a6acSopenharmony_ci sizeof(struct edir) * (exclude_count + 1)); 1836cd6a6acSopenharmony_ci if (!tmp_list) 1846cd6a6acSopenharmony_ci goto oom; 1856cd6a6acSopenharmony_ci 1866cd6a6acSopenharmony_ci exclude_lst = tmp_list; 1876cd6a6acSopenharmony_ci 1886cd6a6acSopenharmony_ci len = strlen(directory); 1896cd6a6acSopenharmony_ci while (len > 1 && directory[len - 1] == '/') 1906cd6a6acSopenharmony_ci len--; 1916cd6a6acSopenharmony_ci 1926cd6a6acSopenharmony_ci current = (exclude_lst + exclude_count); 1936cd6a6acSopenharmony_ci 1946cd6a6acSopenharmony_ci current->directory = strndup(directory, len); 1956cd6a6acSopenharmony_ci if (!current->directory) 1966cd6a6acSopenharmony_ci goto oom; 1976cd6a6acSopenharmony_ci 1986cd6a6acSopenharmony_ci current->size = len; 1996cd6a6acSopenharmony_ci current->caller_excluded = who; 2006cd6a6acSopenharmony_ci exclude_count++; 2016cd6a6acSopenharmony_ci return 0; 2026cd6a6acSopenharmony_ci 2036cd6a6acSopenharmony_cioom: 2046cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 2056cd6a6acSopenharmony_ci return -1; 2066cd6a6acSopenharmony_ci} 2076cd6a6acSopenharmony_ci 2086cd6a6acSopenharmony_cistatic int check_excluded(const char *file) 2096cd6a6acSopenharmony_ci{ 2106cd6a6acSopenharmony_ci int i; 2116cd6a6acSopenharmony_ci 2126cd6a6acSopenharmony_ci for (i = 0; i < exclude_count; i++) { 2136cd6a6acSopenharmony_ci if (strncmp(file, exclude_lst[i].directory, 2146cd6a6acSopenharmony_ci exclude_lst[i].size) == 0) { 2156cd6a6acSopenharmony_ci if (file[exclude_lst[i].size] == 0 || 2166cd6a6acSopenharmony_ci file[exclude_lst[i].size] == '/') 2176cd6a6acSopenharmony_ci return 1; 2186cd6a6acSopenharmony_ci } 2196cd6a6acSopenharmony_ci } 2206cd6a6acSopenharmony_ci return 0; 2216cd6a6acSopenharmony_ci} 2226cd6a6acSopenharmony_ci 2236cd6a6acSopenharmony_cistatic uint64_t file_system_count(const char *name) 2246cd6a6acSopenharmony_ci{ 2256cd6a6acSopenharmony_ci struct statvfs statvfs_buf; 2266cd6a6acSopenharmony_ci uint64_t nfile = 0; 2276cd6a6acSopenharmony_ci 2286cd6a6acSopenharmony_ci memset(&statvfs_buf, 0, sizeof(statvfs_buf)); 2296cd6a6acSopenharmony_ci if (!statvfs(name, &statvfs_buf)) 2306cd6a6acSopenharmony_ci nfile = statvfs_buf.f_files - statvfs_buf.f_ffree; 2316cd6a6acSopenharmony_ci 2326cd6a6acSopenharmony_ci return nfile; 2336cd6a6acSopenharmony_ci} 2346cd6a6acSopenharmony_ci 2356cd6a6acSopenharmony_ci/* 2366cd6a6acSopenharmony_ci * This is called once when selinux_restorecon() is first called. 2376cd6a6acSopenharmony_ci * Searches /proc/mounts for all file systems that do not support extended 2386cd6a6acSopenharmony_ci * attributes and adds them to the exclude directory table. File systems 2396cd6a6acSopenharmony_ci * that support security labels have the seclabel option, return 2406cd6a6acSopenharmony_ci * approximate total file count. 2416cd6a6acSopenharmony_ci */ 2426cd6a6acSopenharmony_cistatic uint64_t exclude_non_seclabel_mounts(void) 2436cd6a6acSopenharmony_ci{ 2446cd6a6acSopenharmony_ci struct utsname uts; 2456cd6a6acSopenharmony_ci FILE *fp; 2466cd6a6acSopenharmony_ci size_t len; 2476cd6a6acSopenharmony_ci int index = 0, found = 0; 2486cd6a6acSopenharmony_ci uint64_t nfile = 0; 2496cd6a6acSopenharmony_ci char *mount_info[4]; 2506cd6a6acSopenharmony_ci char *buf = NULL, *item; 2516cd6a6acSopenharmony_ci 2526cd6a6acSopenharmony_ci /* Check to see if the kernel supports seclabel */ 2536cd6a6acSopenharmony_ci if (uname(&uts) == 0 && strverscmp(uts.release, "2.6.30") < 0) 2546cd6a6acSopenharmony_ci return 0; 2556cd6a6acSopenharmony_ci if (is_selinux_enabled() <= 0) 2566cd6a6acSopenharmony_ci return 0; 2576cd6a6acSopenharmony_ci 2586cd6a6acSopenharmony_ci fp = fopen("/proc/mounts", "re"); 2596cd6a6acSopenharmony_ci if (!fp) 2606cd6a6acSopenharmony_ci return 0; 2616cd6a6acSopenharmony_ci 2626cd6a6acSopenharmony_ci while (getline(&buf, &len, fp) != -1) { 2636cd6a6acSopenharmony_ci found = 0; 2646cd6a6acSopenharmony_ci index = 0; 2656cd6a6acSopenharmony_ci item = strtok(buf, " "); 2666cd6a6acSopenharmony_ci while (item != NULL) { 2676cd6a6acSopenharmony_ci mount_info[index] = item; 2686cd6a6acSopenharmony_ci index++; 2696cd6a6acSopenharmony_ci if (index == 4) 2706cd6a6acSopenharmony_ci break; 2716cd6a6acSopenharmony_ci item = strtok(NULL, " "); 2726cd6a6acSopenharmony_ci } 2736cd6a6acSopenharmony_ci if (index < 4) { 2746cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 2756cd6a6acSopenharmony_ci "/proc/mounts record \"%s\" has incorrect format.\n", 2766cd6a6acSopenharmony_ci buf); 2776cd6a6acSopenharmony_ci continue; 2786cd6a6acSopenharmony_ci } 2796cd6a6acSopenharmony_ci 2806cd6a6acSopenharmony_ci /* Remove pre-existing entry */ 2816cd6a6acSopenharmony_ci remove_exclude(mount_info[1]); 2826cd6a6acSopenharmony_ci 2836cd6a6acSopenharmony_ci item = strtok(mount_info[3], ","); 2846cd6a6acSopenharmony_ci while (item != NULL) { 2856cd6a6acSopenharmony_ci if (strcmp(item, "seclabel") == 0) { 2866cd6a6acSopenharmony_ci found = 1; 2876cd6a6acSopenharmony_ci nfile += file_system_count(mount_info[1]); 2886cd6a6acSopenharmony_ci break; 2896cd6a6acSopenharmony_ci } 2906cd6a6acSopenharmony_ci item = strtok(NULL, ","); 2916cd6a6acSopenharmony_ci } 2926cd6a6acSopenharmony_ci 2936cd6a6acSopenharmony_ci /* Exclude mount points without the seclabel option */ 2946cd6a6acSopenharmony_ci if (!found) { 2956cd6a6acSopenharmony_ci if (add_exclude(mount_info[1], !CALLER_EXCLUDED) && 2966cd6a6acSopenharmony_ci errno == ENOMEM) 2976cd6a6acSopenharmony_ci assert(0); 2986cd6a6acSopenharmony_ci } 2996cd6a6acSopenharmony_ci } 3006cd6a6acSopenharmony_ci 3016cd6a6acSopenharmony_ci free(buf); 3026cd6a6acSopenharmony_ci fclose(fp); 3036cd6a6acSopenharmony_ci /* return estimated #Files + 5% for directories and hard links */ 3046cd6a6acSopenharmony_ci return nfile * 1.05; 3056cd6a6acSopenharmony_ci} 3066cd6a6acSopenharmony_ci 3076cd6a6acSopenharmony_ci/* Called by selinux_restorecon_xattr(3) to build a linked list of entries. */ 3086cd6a6acSopenharmony_cistatic int add_xattr_entry(const char *directory, bool delete_nonmatch, 3096cd6a6acSopenharmony_ci bool delete_all) 3106cd6a6acSopenharmony_ci{ 3116cd6a6acSopenharmony_ci char *sha1_buf = NULL; 3126cd6a6acSopenharmony_ci size_t i, digest_len = 0; 3136cd6a6acSopenharmony_ci int rc; 3146cd6a6acSopenharmony_ci enum digest_result digest_result; 3156cd6a6acSopenharmony_ci bool match; 3166cd6a6acSopenharmony_ci struct dir_xattr *new_entry; 3176cd6a6acSopenharmony_ci uint8_t *xattr_digest = NULL; 3186cd6a6acSopenharmony_ci uint8_t *calculated_digest = NULL; 3196cd6a6acSopenharmony_ci 3206cd6a6acSopenharmony_ci if (!directory) { 3216cd6a6acSopenharmony_ci errno = EINVAL; 3226cd6a6acSopenharmony_ci return -1; 3236cd6a6acSopenharmony_ci } 3246cd6a6acSopenharmony_ci 3256cd6a6acSopenharmony_ci match = selabel_get_digests_all_partial_matches(fc_sehandle, directory, 3266cd6a6acSopenharmony_ci &calculated_digest, &xattr_digest, 3276cd6a6acSopenharmony_ci &digest_len); 3286cd6a6acSopenharmony_ci 3296cd6a6acSopenharmony_ci if (!xattr_digest || !digest_len) { 3306cd6a6acSopenharmony_ci free(calculated_digest); 3316cd6a6acSopenharmony_ci return 1; 3326cd6a6acSopenharmony_ci } 3336cd6a6acSopenharmony_ci 3346cd6a6acSopenharmony_ci /* Convert entry to a hex encoded string. */ 3356cd6a6acSopenharmony_ci sha1_buf = malloc(digest_len * 2 + 1); 3366cd6a6acSopenharmony_ci if (!sha1_buf) { 3376cd6a6acSopenharmony_ci free(xattr_digest); 3386cd6a6acSopenharmony_ci free(calculated_digest); 3396cd6a6acSopenharmony_ci goto oom; 3406cd6a6acSopenharmony_ci } 3416cd6a6acSopenharmony_ci 3426cd6a6acSopenharmony_ci for (i = 0; i < digest_len; i++) 3436cd6a6acSopenharmony_ci sprintf((&sha1_buf[i * 2]), "%02x", xattr_digest[i]); 3446cd6a6acSopenharmony_ci 3456cd6a6acSopenharmony_ci digest_result = match ? MATCH : NOMATCH; 3466cd6a6acSopenharmony_ci 3476cd6a6acSopenharmony_ci if ((delete_nonmatch && !match) || delete_all) { 3486cd6a6acSopenharmony_ci digest_result = match ? DELETED_MATCH : DELETED_NOMATCH; 3496cd6a6acSopenharmony_ci rc = removexattr(directory, RESTORECON_PARTIAL_MATCH_DIGEST); 3506cd6a6acSopenharmony_ci if (rc) { 3516cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 3526cd6a6acSopenharmony_ci "Error: %m removing xattr \"%s\" from: %s\n", 3536cd6a6acSopenharmony_ci RESTORECON_PARTIAL_MATCH_DIGEST, directory); 3546cd6a6acSopenharmony_ci digest_result = ERROR; 3556cd6a6acSopenharmony_ci } 3566cd6a6acSopenharmony_ci } 3576cd6a6acSopenharmony_ci free(xattr_digest); 3586cd6a6acSopenharmony_ci free(calculated_digest); 3596cd6a6acSopenharmony_ci 3606cd6a6acSopenharmony_ci /* Now add entries to link list. */ 3616cd6a6acSopenharmony_ci new_entry = malloc(sizeof(struct dir_xattr)); 3626cd6a6acSopenharmony_ci if (!new_entry) { 3636cd6a6acSopenharmony_ci free(sha1_buf); 3646cd6a6acSopenharmony_ci goto oom; 3656cd6a6acSopenharmony_ci } 3666cd6a6acSopenharmony_ci new_entry->next = NULL; 3676cd6a6acSopenharmony_ci 3686cd6a6acSopenharmony_ci new_entry->directory = strdup(directory); 3696cd6a6acSopenharmony_ci if (!new_entry->directory) { 3706cd6a6acSopenharmony_ci free(new_entry); 3716cd6a6acSopenharmony_ci free(sha1_buf); 3726cd6a6acSopenharmony_ci goto oom; 3736cd6a6acSopenharmony_ci } 3746cd6a6acSopenharmony_ci 3756cd6a6acSopenharmony_ci new_entry->digest = strdup(sha1_buf); 3766cd6a6acSopenharmony_ci if (!new_entry->digest) { 3776cd6a6acSopenharmony_ci free(new_entry->directory); 3786cd6a6acSopenharmony_ci free(new_entry); 3796cd6a6acSopenharmony_ci free(sha1_buf); 3806cd6a6acSopenharmony_ci goto oom; 3816cd6a6acSopenharmony_ci } 3826cd6a6acSopenharmony_ci 3836cd6a6acSopenharmony_ci new_entry->result = digest_result; 3846cd6a6acSopenharmony_ci 3856cd6a6acSopenharmony_ci if (!dir_xattr_list) { 3866cd6a6acSopenharmony_ci dir_xattr_list = new_entry; 3876cd6a6acSopenharmony_ci dir_xattr_last = new_entry; 3886cd6a6acSopenharmony_ci } else { 3896cd6a6acSopenharmony_ci dir_xattr_last->next = new_entry; 3906cd6a6acSopenharmony_ci dir_xattr_last = new_entry; 3916cd6a6acSopenharmony_ci } 3926cd6a6acSopenharmony_ci 3936cd6a6acSopenharmony_ci free(sha1_buf); 3946cd6a6acSopenharmony_ci return 0; 3956cd6a6acSopenharmony_ci 3966cd6a6acSopenharmony_cioom: 3976cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 3986cd6a6acSopenharmony_ci return -1; 3996cd6a6acSopenharmony_ci} 4006cd6a6acSopenharmony_ci 4016cd6a6acSopenharmony_ci/* 4026cd6a6acSopenharmony_ci * Support filespec services filespec_add(), filespec_eval() and 4036cd6a6acSopenharmony_ci * filespec_destroy(). 4046cd6a6acSopenharmony_ci * 4056cd6a6acSopenharmony_ci * selinux_restorecon(3) uses filespec services when the 4066cd6a6acSopenharmony_ci * SELINUX_RESTORECON_ADD_ASSOC flag is set for adding associations between 4076cd6a6acSopenharmony_ci * an inode and a specification. 4086cd6a6acSopenharmony_ci */ 4096cd6a6acSopenharmony_ci 4106cd6a6acSopenharmony_ci/* 4116cd6a6acSopenharmony_ci * The hash table of associations, hashed by inode number. Chaining is used 4126cd6a6acSopenharmony_ci * for collisions, with elements ordered by inode number in each bucket. 4136cd6a6acSopenharmony_ci * Each hash bucket has a dummy header. 4146cd6a6acSopenharmony_ci */ 4156cd6a6acSopenharmony_ci#define HASH_BITS 16 4166cd6a6acSopenharmony_ci#define HASH_BUCKETS (1 << HASH_BITS) 4176cd6a6acSopenharmony_ci#define HASH_MASK (HASH_BUCKETS-1) 4186cd6a6acSopenharmony_ci 4196cd6a6acSopenharmony_ci/* 4206cd6a6acSopenharmony_ci * An association between an inode and a context. 4216cd6a6acSopenharmony_ci */ 4226cd6a6acSopenharmony_citypedef struct file_spec { 4236cd6a6acSopenharmony_ci ino_t ino; /* inode number */ 4246cd6a6acSopenharmony_ci char *con; /* matched context */ 4256cd6a6acSopenharmony_ci char *file; /* full pathname */ 4266cd6a6acSopenharmony_ci struct file_spec *next; /* next association in hash bucket chain */ 4276cd6a6acSopenharmony_ci} file_spec_t; 4286cd6a6acSopenharmony_ci 4296cd6a6acSopenharmony_cistatic file_spec_t *fl_head; 4306cd6a6acSopenharmony_cistatic pthread_mutex_t fl_mutex = PTHREAD_MUTEX_INITIALIZER; 4316cd6a6acSopenharmony_ci 4326cd6a6acSopenharmony_ci/* 4336cd6a6acSopenharmony_ci * Try to add an association between an inode and a context. If there is a 4346cd6a6acSopenharmony_ci * different context that matched the inode, then use the first context 4356cd6a6acSopenharmony_ci * that matched. 4366cd6a6acSopenharmony_ci */ 4376cd6a6acSopenharmony_cistatic int filespec_add(ino_t ino, const char *con, const char *file, 4386cd6a6acSopenharmony_ci const struct rest_flags *flags) 4396cd6a6acSopenharmony_ci{ 4406cd6a6acSopenharmony_ci file_spec_t *prevfl, *fl; 4416cd6a6acSopenharmony_ci uint32_t h; 4426cd6a6acSopenharmony_ci int ret; 4436cd6a6acSopenharmony_ci struct stat64 sb; 4446cd6a6acSopenharmony_ci 4456cd6a6acSopenharmony_ci __pthread_mutex_lock(&fl_mutex); 4466cd6a6acSopenharmony_ci 4476cd6a6acSopenharmony_ci if (!fl_head) { 4486cd6a6acSopenharmony_ci fl_head = calloc(HASH_BUCKETS, sizeof(file_spec_t)); 4496cd6a6acSopenharmony_ci if (!fl_head) 4506cd6a6acSopenharmony_ci goto oom; 4516cd6a6acSopenharmony_ci } 4526cd6a6acSopenharmony_ci 4536cd6a6acSopenharmony_ci h = (ino + (ino >> HASH_BITS)) & HASH_MASK; 4546cd6a6acSopenharmony_ci for (prevfl = &fl_head[h], fl = fl_head[h].next; fl; 4556cd6a6acSopenharmony_ci prevfl = fl, fl = fl->next) { 4566cd6a6acSopenharmony_ci if (ino == fl->ino) { 4576cd6a6acSopenharmony_ci ret = lstat64(fl->file, &sb); 4586cd6a6acSopenharmony_ci if (ret < 0 || sb.st_ino != ino) { 4596cd6a6acSopenharmony_ci freecon(fl->con); 4606cd6a6acSopenharmony_ci free(fl->file); 4616cd6a6acSopenharmony_ci fl->file = strdup(file); 4626cd6a6acSopenharmony_ci if (!fl->file) 4636cd6a6acSopenharmony_ci goto oom; 4646cd6a6acSopenharmony_ci fl->con = strdup(con); 4656cd6a6acSopenharmony_ci if (!fl->con) 4666cd6a6acSopenharmony_ci goto oom; 4676cd6a6acSopenharmony_ci goto unlock_1; 4686cd6a6acSopenharmony_ci } 4696cd6a6acSopenharmony_ci 4706cd6a6acSopenharmony_ci if (strcmp(fl->con, con) == 0) 4716cd6a6acSopenharmony_ci goto unlock_1; 4726cd6a6acSopenharmony_ci 4736cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 4746cd6a6acSopenharmony_ci "conflicting specifications for %s and %s, using %s.\n", 4756cd6a6acSopenharmony_ci file, fl->file, fl->con); 4766cd6a6acSopenharmony_ci free(fl->file); 4776cd6a6acSopenharmony_ci fl->file = strdup(file); 4786cd6a6acSopenharmony_ci if (!fl->file) 4796cd6a6acSopenharmony_ci goto oom; 4806cd6a6acSopenharmony_ci 4816cd6a6acSopenharmony_ci __pthread_mutex_unlock(&fl_mutex); 4826cd6a6acSopenharmony_ci 4836cd6a6acSopenharmony_ci if (flags->conflicterror) { 4846cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 4856cd6a6acSopenharmony_ci "treating conflicting specifications as an error.\n"); 4866cd6a6acSopenharmony_ci return -1; 4876cd6a6acSopenharmony_ci } 4886cd6a6acSopenharmony_ci return 1; 4896cd6a6acSopenharmony_ci } 4906cd6a6acSopenharmony_ci 4916cd6a6acSopenharmony_ci if (ino > fl->ino) 4926cd6a6acSopenharmony_ci break; 4936cd6a6acSopenharmony_ci } 4946cd6a6acSopenharmony_ci 4956cd6a6acSopenharmony_ci fl = malloc(sizeof(file_spec_t)); 4966cd6a6acSopenharmony_ci if (!fl) 4976cd6a6acSopenharmony_ci goto oom; 4986cd6a6acSopenharmony_ci fl->ino = ino; 4996cd6a6acSopenharmony_ci fl->con = strdup(con); 5006cd6a6acSopenharmony_ci if (!fl->con) 5016cd6a6acSopenharmony_ci goto oom_freefl; 5026cd6a6acSopenharmony_ci fl->file = strdup(file); 5036cd6a6acSopenharmony_ci if (!fl->file) 5046cd6a6acSopenharmony_ci goto oom_freeflcon; 5056cd6a6acSopenharmony_ci fl->next = prevfl->next; 5066cd6a6acSopenharmony_ci prevfl->next = fl; 5076cd6a6acSopenharmony_ci 5086cd6a6acSopenharmony_ci __pthread_mutex_unlock(&fl_mutex); 5096cd6a6acSopenharmony_ci return 0; 5106cd6a6acSopenharmony_ci 5116cd6a6acSopenharmony_cioom_freeflcon: 5126cd6a6acSopenharmony_ci free(fl->con); 5136cd6a6acSopenharmony_cioom_freefl: 5146cd6a6acSopenharmony_ci free(fl); 5156cd6a6acSopenharmony_cioom: 5166cd6a6acSopenharmony_ci __pthread_mutex_unlock(&fl_mutex); 5176cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 5186cd6a6acSopenharmony_ci return -1; 5196cd6a6acSopenharmony_ciunlock_1: 5206cd6a6acSopenharmony_ci __pthread_mutex_unlock(&fl_mutex); 5216cd6a6acSopenharmony_ci return 1; 5226cd6a6acSopenharmony_ci} 5236cd6a6acSopenharmony_ci 5246cd6a6acSopenharmony_ci/* 5256cd6a6acSopenharmony_ci * Evaluate the association hash table distribution. 5266cd6a6acSopenharmony_ci */ 5276cd6a6acSopenharmony_ci#ifdef DEBUG 5286cd6a6acSopenharmony_cistatic void filespec_eval(void) 5296cd6a6acSopenharmony_ci{ 5306cd6a6acSopenharmony_ci file_spec_t *fl; 5316cd6a6acSopenharmony_ci uint32_t h; 5326cd6a6acSopenharmony_ci size_t used, nel, len, longest; 5336cd6a6acSopenharmony_ci 5346cd6a6acSopenharmony_ci if (!fl_head) 5356cd6a6acSopenharmony_ci return; 5366cd6a6acSopenharmony_ci 5376cd6a6acSopenharmony_ci used = 0; 5386cd6a6acSopenharmony_ci longest = 0; 5396cd6a6acSopenharmony_ci nel = 0; 5406cd6a6acSopenharmony_ci for (h = 0; h < HASH_BUCKETS; h++) { 5416cd6a6acSopenharmony_ci len = 0; 5426cd6a6acSopenharmony_ci for (fl = fl_head[h].next; fl; fl = fl->next) 5436cd6a6acSopenharmony_ci len++; 5446cd6a6acSopenharmony_ci if (len) 5456cd6a6acSopenharmony_ci used++; 5466cd6a6acSopenharmony_ci if (len > longest) 5476cd6a6acSopenharmony_ci longest = len; 5486cd6a6acSopenharmony_ci nel += len; 5496cd6a6acSopenharmony_ci } 5506cd6a6acSopenharmony_ci 5516cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 5526cd6a6acSopenharmony_ci "filespec hash table stats: %zu elements, %zu/%zu buckets used, longest chain length %zu\n", 5536cd6a6acSopenharmony_ci nel, used, HASH_BUCKETS, longest); 5546cd6a6acSopenharmony_ci} 5556cd6a6acSopenharmony_ci#else 5566cd6a6acSopenharmony_cistatic void filespec_eval(void) 5576cd6a6acSopenharmony_ci{ 5586cd6a6acSopenharmony_ci} 5596cd6a6acSopenharmony_ci#endif 5606cd6a6acSopenharmony_ci 5616cd6a6acSopenharmony_ci/* 5626cd6a6acSopenharmony_ci * Destroy the association hash table. 5636cd6a6acSopenharmony_ci */ 5646cd6a6acSopenharmony_cistatic void filespec_destroy(void) 5656cd6a6acSopenharmony_ci{ 5666cd6a6acSopenharmony_ci file_spec_t *fl, *tmp; 5676cd6a6acSopenharmony_ci uint32_t h; 5686cd6a6acSopenharmony_ci 5696cd6a6acSopenharmony_ci if (!fl_head) 5706cd6a6acSopenharmony_ci return; 5716cd6a6acSopenharmony_ci 5726cd6a6acSopenharmony_ci for (h = 0; h < HASH_BUCKETS; h++) { 5736cd6a6acSopenharmony_ci fl = fl_head[h].next; 5746cd6a6acSopenharmony_ci while (fl) { 5756cd6a6acSopenharmony_ci tmp = fl; 5766cd6a6acSopenharmony_ci fl = fl->next; 5776cd6a6acSopenharmony_ci freecon(tmp->con); 5786cd6a6acSopenharmony_ci free(tmp->file); 5796cd6a6acSopenharmony_ci free(tmp); 5806cd6a6acSopenharmony_ci } 5816cd6a6acSopenharmony_ci fl_head[h].next = NULL; 5826cd6a6acSopenharmony_ci } 5836cd6a6acSopenharmony_ci free(fl_head); 5846cd6a6acSopenharmony_ci fl_head = NULL; 5856cd6a6acSopenharmony_ci} 5866cd6a6acSopenharmony_ci 5876cd6a6acSopenharmony_ci/* 5886cd6a6acSopenharmony_ci * Called if SELINUX_RESTORECON_SET_SPECFILE_CTX is not set to check if 5896cd6a6acSopenharmony_ci * the type components differ, updating newtypecon if so. 5906cd6a6acSopenharmony_ci */ 5916cd6a6acSopenharmony_cistatic int compare_types(const char *curcon, const char *newcon, char **newtypecon) 5926cd6a6acSopenharmony_ci{ 5936cd6a6acSopenharmony_ci int types_differ = 0; 5946cd6a6acSopenharmony_ci context_t cona; 5956cd6a6acSopenharmony_ci context_t conb; 5966cd6a6acSopenharmony_ci int rc = 0; 5976cd6a6acSopenharmony_ci 5986cd6a6acSopenharmony_ci cona = context_new(curcon); 5996cd6a6acSopenharmony_ci if (!cona) { 6006cd6a6acSopenharmony_ci rc = -1; 6016cd6a6acSopenharmony_ci goto out; 6026cd6a6acSopenharmony_ci } 6036cd6a6acSopenharmony_ci conb = context_new(newcon); 6046cd6a6acSopenharmony_ci if (!conb) { 6056cd6a6acSopenharmony_ci context_free(cona); 6066cd6a6acSopenharmony_ci rc = -1; 6076cd6a6acSopenharmony_ci goto out; 6086cd6a6acSopenharmony_ci } 6096cd6a6acSopenharmony_ci 6106cd6a6acSopenharmony_ci types_differ = strcmp(context_type_get(cona), context_type_get(conb)); 6116cd6a6acSopenharmony_ci if (types_differ) { 6126cd6a6acSopenharmony_ci rc |= context_user_set(conb, context_user_get(cona)); 6136cd6a6acSopenharmony_ci rc |= context_role_set(conb, context_role_get(cona)); 6146cd6a6acSopenharmony_ci rc |= context_range_set(conb, context_range_get(cona)); 6156cd6a6acSopenharmony_ci if (!rc) { 6166cd6a6acSopenharmony_ci *newtypecon = strdup(context_str(conb)); 6176cd6a6acSopenharmony_ci if (!*newtypecon) { 6186cd6a6acSopenharmony_ci rc = -1; 6196cd6a6acSopenharmony_ci goto err; 6206cd6a6acSopenharmony_ci } 6216cd6a6acSopenharmony_ci } 6226cd6a6acSopenharmony_ci } 6236cd6a6acSopenharmony_ci 6246cd6a6acSopenharmony_cierr: 6256cd6a6acSopenharmony_ci context_free(cona); 6266cd6a6acSopenharmony_ci context_free(conb); 6276cd6a6acSopenharmony_ciout: 6286cd6a6acSopenharmony_ci return rc; 6296cd6a6acSopenharmony_ci} 6306cd6a6acSopenharmony_ci 6316cd6a6acSopenharmony_ci#define DATA_APP_EL1 "/data/app/el1/" 6326cd6a6acSopenharmony_ci#define DATA_APP_EL2 "/data/app/el2/" 6336cd6a6acSopenharmony_ci#define DATA_APP_EL3 "/data/app/el3/" 6346cd6a6acSopenharmony_ci#define DATA_APP_EL4 "/data/app/el4/" 6356cd6a6acSopenharmony_ci#define DATA_ACCOUNTS_ACCOUNT_0 "/data/accounts/account_0/" 6366cd6a6acSopenharmony_ci#define HNP_ROOT_PATH "/data/app/el1/bundle/" 6376cd6a6acSopenharmony_ci#define HNP_PUBLIC_DIR "/hnppublic" 6386cd6a6acSopenharmony_ci#define HNP_ROOT_PATH_LEN 21 6396cd6a6acSopenharmony_ci#define HNP_PUBLIC_DIR_LEN 10 6406cd6a6acSopenharmony_ci 6416cd6a6acSopenharmony_ci// Allow the hnp process to refresh the labels of files in the HNP_ROOT_PATH directory 6426cd6a6acSopenharmony_cistatic bool is_hnp_path(const char *path) 6436cd6a6acSopenharmony_ci{ 6446cd6a6acSopenharmony_ci size_t pathLen = strlen(path); 6456cd6a6acSopenharmony_ci if ((pathLen < HNP_ROOT_PATH_LEN + 1 + HNP_PUBLIC_DIR_LEN + 1) || 6466cd6a6acSopenharmony_ci (strstr(path, HNP_PUBLIC_DIR) == NULL)) { 6476cd6a6acSopenharmony_ci return false; 6486cd6a6acSopenharmony_ci } 6496cd6a6acSopenharmony_ci 6506cd6a6acSopenharmony_ci if (strncmp(path, HNP_ROOT_PATH, HNP_ROOT_PATH_LEN) != 0) { 6516cd6a6acSopenharmony_ci return false; 6526cd6a6acSopenharmony_ci } 6536cd6a6acSopenharmony_ci return true; 6546cd6a6acSopenharmony_ci} 6556cd6a6acSopenharmony_ci 6566cd6a6acSopenharmony_cistatic bool check_path_allow_restorecon(const char *pathname) 6576cd6a6acSopenharmony_ci{ 6586cd6a6acSopenharmony_ci if ((!strncmp(pathname, DATA_APP_EL1, sizeof(DATA_APP_EL1) - 1) && (!is_hnp_path(pathname))) || 6596cd6a6acSopenharmony_ci !strncmp(pathname, DATA_APP_EL2, sizeof(DATA_APP_EL2) - 1) || 6606cd6a6acSopenharmony_ci !strncmp(pathname, DATA_APP_EL3, sizeof(DATA_APP_EL3) - 1) || 6616cd6a6acSopenharmony_ci !strncmp(pathname, DATA_APP_EL4, sizeof(DATA_APP_EL4) - 1) || 6626cd6a6acSopenharmony_ci !strncmp(pathname, DATA_ACCOUNTS_ACCOUNT_0, sizeof(DATA_ACCOUNTS_ACCOUNT_0) - 1)) { 6636cd6a6acSopenharmony_ci return false; 6646cd6a6acSopenharmony_ci } 6656cd6a6acSopenharmony_ci return true; 6666cd6a6acSopenharmony_ci} 6676cd6a6acSopenharmony_ci 6686cd6a6acSopenharmony_ci 6696cd6a6acSopenharmony_cistatic int restorecon_sb(const char *pathname, const struct stat *sb, 6706cd6a6acSopenharmony_ci const struct rest_flags *flags, bool first) 6716cd6a6acSopenharmony_ci{ 6726cd6a6acSopenharmony_ci char *newcon = NULL; 6736cd6a6acSopenharmony_ci char *curcon = NULL; 6746cd6a6acSopenharmony_ci char *newtypecon = NULL; 6756cd6a6acSopenharmony_ci int rc; 6766cd6a6acSopenharmony_ci const char *lookup_path = pathname; 6776cd6a6acSopenharmony_ci 6786cd6a6acSopenharmony_ci if (!check_path_allow_restorecon(pathname)) { 6796cd6a6acSopenharmony_ci goto out; 6806cd6a6acSopenharmony_ci } 6816cd6a6acSopenharmony_ci 6826cd6a6acSopenharmony_ci if (rootpath) { 6836cd6a6acSopenharmony_ci if (strncmp(rootpath, lookup_path, rootpathlen) != 0) { 6846cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 6856cd6a6acSopenharmony_ci "%s is not located in alt_rootpath %s\n", 6866cd6a6acSopenharmony_ci lookup_path, rootpath); 6876cd6a6acSopenharmony_ci return -1; 6886cd6a6acSopenharmony_ci } 6896cd6a6acSopenharmony_ci lookup_path += rootpathlen; 6906cd6a6acSopenharmony_ci } 6916cd6a6acSopenharmony_ci 6926cd6a6acSopenharmony_ci if (rootpath != NULL && lookup_path[0] == '\0') 6936cd6a6acSopenharmony_ci /* this is actually the root dir of the alt root. */ 6946cd6a6acSopenharmony_ci rc = selabel_lookup_raw(fc_sehandle, &newcon, "/", 6956cd6a6acSopenharmony_ci sb->st_mode & S_IFMT); 6966cd6a6acSopenharmony_ci else 6976cd6a6acSopenharmony_ci rc = selabel_lookup_raw(fc_sehandle, &newcon, lookup_path, 6986cd6a6acSopenharmony_ci sb->st_mode & S_IFMT); 6996cd6a6acSopenharmony_ci 7006cd6a6acSopenharmony_ci if (rc < 0) { 7016cd6a6acSopenharmony_ci if (errno == ENOENT) { 7026cd6a6acSopenharmony_ci if (flags->warnonnomatch && first) 7036cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 7046cd6a6acSopenharmony_ci "Warning no default label for %s\n", 7056cd6a6acSopenharmony_ci lookup_path); 7066cd6a6acSopenharmony_ci 7076cd6a6acSopenharmony_ci return 0; /* no match, but not an error */ 7086cd6a6acSopenharmony_ci } 7096cd6a6acSopenharmony_ci 7106cd6a6acSopenharmony_ci return -1; 7116cd6a6acSopenharmony_ci } 7126cd6a6acSopenharmony_ci 7136cd6a6acSopenharmony_ci if (flags->progress) { 7146cd6a6acSopenharmony_ci __pthread_mutex_lock(&progress_mutex); 7156cd6a6acSopenharmony_ci fc_count++; 7166cd6a6acSopenharmony_ci if (fc_count % STAR_COUNT == 0) { 7176cd6a6acSopenharmony_ci if (flags->mass_relabel && efile_count > 0) { 7186cd6a6acSopenharmony_ci float pc = (fc_count < efile_count) ? (100.0 * 7196cd6a6acSopenharmony_ci fc_count / efile_count) : 100; 7206cd6a6acSopenharmony_ci fprintf(stdout, "\r%-.1f%%", (double)pc); 7216cd6a6acSopenharmony_ci } else { 7226cd6a6acSopenharmony_ci fprintf(stdout, "\r%" PRIu64 "k", fc_count / STAR_COUNT); 7236cd6a6acSopenharmony_ci } 7246cd6a6acSopenharmony_ci fflush(stdout); 7256cd6a6acSopenharmony_ci } 7266cd6a6acSopenharmony_ci __pthread_mutex_unlock(&progress_mutex); 7276cd6a6acSopenharmony_ci } 7286cd6a6acSopenharmony_ci 7296cd6a6acSopenharmony_ci if (flags->add_assoc) { 7306cd6a6acSopenharmony_ci rc = filespec_add(sb->st_ino, newcon, pathname, flags); 7316cd6a6acSopenharmony_ci 7326cd6a6acSopenharmony_ci if (rc < 0) { 7336cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 7346cd6a6acSopenharmony_ci "filespec_add error: %s\n", pathname); 7356cd6a6acSopenharmony_ci freecon(newcon); 7366cd6a6acSopenharmony_ci return -1; 7376cd6a6acSopenharmony_ci } 7386cd6a6acSopenharmony_ci 7396cd6a6acSopenharmony_ci if (rc > 0) { 7406cd6a6acSopenharmony_ci /* Already an association and it took precedence. */ 7416cd6a6acSopenharmony_ci freecon(newcon); 7426cd6a6acSopenharmony_ci return 0; 7436cd6a6acSopenharmony_ci } 7446cd6a6acSopenharmony_ci } 7456cd6a6acSopenharmony_ci 7466cd6a6acSopenharmony_ci if (flags->log_matches) 7476cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, "%s matched by %s\n", 7486cd6a6acSopenharmony_ci pathname, newcon); 7496cd6a6acSopenharmony_ci 7506cd6a6acSopenharmony_ci if (lgetfilecon_raw(pathname, &curcon) < 0) { 7516cd6a6acSopenharmony_ci if (errno != ENODATA) 7526cd6a6acSopenharmony_ci goto err; 7536cd6a6acSopenharmony_ci 7546cd6a6acSopenharmony_ci curcon = NULL; 7556cd6a6acSopenharmony_ci } 7566cd6a6acSopenharmony_ci 7576cd6a6acSopenharmony_ci if (curcon == NULL || strcmp(curcon, newcon) != 0) { 7586cd6a6acSopenharmony_ci bool updated = false; 7596cd6a6acSopenharmony_ci 7606cd6a6acSopenharmony_ci if (!flags->set_specctx && curcon && 7616cd6a6acSopenharmony_ci (is_context_customizable(curcon) > 0)) { 7626cd6a6acSopenharmony_ci if (flags->verbose) { 7636cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 7646cd6a6acSopenharmony_ci "%s not reset as customized by admin to %s\n", 7656cd6a6acSopenharmony_ci pathname, curcon); 7666cd6a6acSopenharmony_ci } 7676cd6a6acSopenharmony_ci goto out; 7686cd6a6acSopenharmony_ci } 7696cd6a6acSopenharmony_ci 7706cd6a6acSopenharmony_ci if (!flags->set_specctx && curcon) { 7716cd6a6acSopenharmony_ci /* If types different then update newcon. */ 7726cd6a6acSopenharmony_ci rc = compare_types(curcon, newcon, &newtypecon); 7736cd6a6acSopenharmony_ci if (rc) 7746cd6a6acSopenharmony_ci goto err; 7756cd6a6acSopenharmony_ci 7766cd6a6acSopenharmony_ci if (newtypecon) { 7776cd6a6acSopenharmony_ci freecon(newcon); 7786cd6a6acSopenharmony_ci newcon = newtypecon; 7796cd6a6acSopenharmony_ci } else { 7806cd6a6acSopenharmony_ci goto out; 7816cd6a6acSopenharmony_ci } 7826cd6a6acSopenharmony_ci } 7836cd6a6acSopenharmony_ci 7846cd6a6acSopenharmony_ci if (!flags->nochange) { 7856cd6a6acSopenharmony_ci if (lsetfilecon(pathname, newcon) < 0) 7866cd6a6acSopenharmony_ci goto err; 7876cd6a6acSopenharmony_ci updated = true; 7886cd6a6acSopenharmony_ci } 7896cd6a6acSopenharmony_ci 7906cd6a6acSopenharmony_ci if (flags->verbose) 7916cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 7926cd6a6acSopenharmony_ci "%s %s from %s to %s\n", 7936cd6a6acSopenharmony_ci updated ? "Relabeled" : "Would relabel", 7946cd6a6acSopenharmony_ci pathname, 7956cd6a6acSopenharmony_ci curcon ? curcon : "<no context>", 7966cd6a6acSopenharmony_ci newcon); 7976cd6a6acSopenharmony_ci 7986cd6a6acSopenharmony_ci if (flags->syslog_changes && !flags->nochange) { 7996cd6a6acSopenharmony_ci if (curcon) 8006cd6a6acSopenharmony_ci syslog(LOG_INFO, 8016cd6a6acSopenharmony_ci "relabeling %s from %s to %s\n", 8026cd6a6acSopenharmony_ci pathname, curcon, newcon); 8036cd6a6acSopenharmony_ci else 8046cd6a6acSopenharmony_ci syslog(LOG_INFO, "labeling %s to %s\n", 8056cd6a6acSopenharmony_ci pathname, newcon); 8066cd6a6acSopenharmony_ci } 8076cd6a6acSopenharmony_ci } 8086cd6a6acSopenharmony_ci 8096cd6a6acSopenharmony_ciout: 8106cd6a6acSopenharmony_ci rc = 0; 8116cd6a6acSopenharmony_ciout1: 8126cd6a6acSopenharmony_ci freecon(curcon); 8136cd6a6acSopenharmony_ci freecon(newcon); 8146cd6a6acSopenharmony_ci return rc; 8156cd6a6acSopenharmony_cierr: 8166cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 8176cd6a6acSopenharmony_ci "Could not set context for %s: %m\n", 8186cd6a6acSopenharmony_ci pathname); 8196cd6a6acSopenharmony_ci rc = -1; 8206cd6a6acSopenharmony_ci goto out1; 8216cd6a6acSopenharmony_ci} 8226cd6a6acSopenharmony_ci 8236cd6a6acSopenharmony_cistruct dir_hash_node { 8246cd6a6acSopenharmony_ci char *path; 8256cd6a6acSopenharmony_ci uint8_t digest[SHA1_HASH_SIZE]; 8266cd6a6acSopenharmony_ci struct dir_hash_node *next; 8276cd6a6acSopenharmony_ci}; 8286cd6a6acSopenharmony_ci/* 8296cd6a6acSopenharmony_ci * Returns true if the digest of all partial matched contexts is the same as 8306cd6a6acSopenharmony_ci * the one saved by setxattr. Otherwise returns false and constructs a 8316cd6a6acSopenharmony_ci * dir_hash_node with the newly calculated digest. 8326cd6a6acSopenharmony_ci */ 8336cd6a6acSopenharmony_cistatic bool check_context_match_for_dir(const char *pathname, 8346cd6a6acSopenharmony_ci struct dir_hash_node **new_node, 8356cd6a6acSopenharmony_ci int error) 8366cd6a6acSopenharmony_ci{ 8376cd6a6acSopenharmony_ci bool status; 8386cd6a6acSopenharmony_ci size_t digest_len = 0; 8396cd6a6acSopenharmony_ci uint8_t *read_digest = NULL; 8406cd6a6acSopenharmony_ci uint8_t *calculated_digest = NULL; 8416cd6a6acSopenharmony_ci 8426cd6a6acSopenharmony_ci if (!new_node) 8436cd6a6acSopenharmony_ci return false; 8446cd6a6acSopenharmony_ci 8456cd6a6acSopenharmony_ci *new_node = NULL; 8466cd6a6acSopenharmony_ci 8476cd6a6acSopenharmony_ci /* status = true if digests match, false otherwise. */ 8486cd6a6acSopenharmony_ci status = selabel_get_digests_all_partial_matches(fc_sehandle, pathname, 8496cd6a6acSopenharmony_ci &calculated_digest, 8506cd6a6acSopenharmony_ci &read_digest, 8516cd6a6acSopenharmony_ci &digest_len); 8526cd6a6acSopenharmony_ci 8536cd6a6acSopenharmony_ci if (status) 8546cd6a6acSopenharmony_ci goto free; 8556cd6a6acSopenharmony_ci 8566cd6a6acSopenharmony_ci /* Save digest of all matched contexts for the current directory. */ 8576cd6a6acSopenharmony_ci if (!error && calculated_digest) { 8586cd6a6acSopenharmony_ci *new_node = calloc(1, sizeof(struct dir_hash_node)); 8596cd6a6acSopenharmony_ci 8606cd6a6acSopenharmony_ci if (!*new_node) 8616cd6a6acSopenharmony_ci goto oom; 8626cd6a6acSopenharmony_ci 8636cd6a6acSopenharmony_ci (*new_node)->path = strdup(pathname); 8646cd6a6acSopenharmony_ci 8656cd6a6acSopenharmony_ci if (!(*new_node)->path) { 8666cd6a6acSopenharmony_ci free(*new_node); 8676cd6a6acSopenharmony_ci *new_node = NULL; 8686cd6a6acSopenharmony_ci goto oom; 8696cd6a6acSopenharmony_ci } 8706cd6a6acSopenharmony_ci memcpy((*new_node)->digest, calculated_digest, digest_len); 8716cd6a6acSopenharmony_ci (*new_node)->next = NULL; 8726cd6a6acSopenharmony_ci } 8736cd6a6acSopenharmony_ci 8746cd6a6acSopenharmony_cifree: 8756cd6a6acSopenharmony_ci free(calculated_digest); 8766cd6a6acSopenharmony_ci free(read_digest); 8776cd6a6acSopenharmony_ci return status; 8786cd6a6acSopenharmony_ci 8796cd6a6acSopenharmony_cioom: 8806cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 8816cd6a6acSopenharmony_ci goto free; 8826cd6a6acSopenharmony_ci} 8836cd6a6acSopenharmony_ci 8846cd6a6acSopenharmony_cistatic bool is_in_skip_elx(const char *path) { 8856cd6a6acSopenharmony_ci if (sizeof(skip_elx_path) == 0) { 8866cd6a6acSopenharmony_ci return false; 8876cd6a6acSopenharmony_ci } 8886cd6a6acSopenharmony_ci size_t len = sizeof(skip_elx_path) / sizeof(skip_elx_path[0]); 8896cd6a6acSopenharmony_ci for (size_t i = 0; i < len; i++) { 8906cd6a6acSopenharmony_ci if (strncmp(path, skip_elx_path[i], strlen(skip_elx_path[i])) == 0) { 8916cd6a6acSopenharmony_ci return true; 8926cd6a6acSopenharmony_ci } 8936cd6a6acSopenharmony_ci } 8946cd6a6acSopenharmony_ci return false; 8956cd6a6acSopenharmony_ci} 8966cd6a6acSopenharmony_ci 8976cd6a6acSopenharmony_cistruct rest_state { 8986cd6a6acSopenharmony_ci struct rest_flags flags; 8996cd6a6acSopenharmony_ci dev_t dev_num; 9006cd6a6acSopenharmony_ci struct statfs sfsb; 9016cd6a6acSopenharmony_ci bool ignore_digest; 9026cd6a6acSopenharmony_ci bool setrestorecondigest; 9036cd6a6acSopenharmony_ci bool parallel; 9046cd6a6acSopenharmony_ci 9056cd6a6acSopenharmony_ci FTS *fts; 9066cd6a6acSopenharmony_ci FTSENT *ftsent_first; 9076cd6a6acSopenharmony_ci struct dir_hash_node *head, *current; 9086cd6a6acSopenharmony_ci bool abort; 9096cd6a6acSopenharmony_ci int error; 9106cd6a6acSopenharmony_ci long unsigned skipped_errors; 9116cd6a6acSopenharmony_ci int saved_errno; 9126cd6a6acSopenharmony_ci pthread_mutex_t mutex; 9136cd6a6acSopenharmony_ci}; 9146cd6a6acSopenharmony_ci 9156cd6a6acSopenharmony_cistatic void *selinux_restorecon_thread(void *arg) 9166cd6a6acSopenharmony_ci{ 9176cd6a6acSopenharmony_ci struct rest_state *state = arg; 9186cd6a6acSopenharmony_ci FTS *fts = state->fts; 9196cd6a6acSopenharmony_ci FTSENT *ftsent; 9206cd6a6acSopenharmony_ci int error; 9216cd6a6acSopenharmony_ci char ent_path[PATH_MAX]; 9226cd6a6acSopenharmony_ci struct stat ent_st; 9236cd6a6acSopenharmony_ci bool first = false; 9246cd6a6acSopenharmony_ci 9256cd6a6acSopenharmony_ci if (state->parallel) 9266cd6a6acSopenharmony_ci pthread_mutex_lock(&state->mutex); 9276cd6a6acSopenharmony_ci 9286cd6a6acSopenharmony_ci if (state->ftsent_first) { 9296cd6a6acSopenharmony_ci ftsent = state->ftsent_first; 9306cd6a6acSopenharmony_ci state->ftsent_first = NULL; 9316cd6a6acSopenharmony_ci first = true; 9326cd6a6acSopenharmony_ci goto loop_body; 9336cd6a6acSopenharmony_ci } 9346cd6a6acSopenharmony_ci 9356cd6a6acSopenharmony_ci while (((void)(errno = 0), ftsent = fts_read(fts)) != NULL) { 9366cd6a6acSopenharmony_ciloop_body: 9376cd6a6acSopenharmony_ci /* If the FTS_XDEV flag is set and the device is different */ 9386cd6a6acSopenharmony_ci if (state->flags.set_xdev && 9396cd6a6acSopenharmony_ci ftsent->fts_statp->st_dev != state->dev_num) 9406cd6a6acSopenharmony_ci continue; 9416cd6a6acSopenharmony_ci 9426cd6a6acSopenharmony_ci switch (ftsent->fts_info) { 9436cd6a6acSopenharmony_ci case FTS_DC: 9446cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 9456cd6a6acSopenharmony_ci "Directory cycle on %s.\n", 9466cd6a6acSopenharmony_ci ftsent->fts_path); 9476cd6a6acSopenharmony_ci errno = ELOOP; 9486cd6a6acSopenharmony_ci state->error = -1; 9496cd6a6acSopenharmony_ci state->abort = true; 9506cd6a6acSopenharmony_ci goto finish; 9516cd6a6acSopenharmony_ci case FTS_DP: 9526cd6a6acSopenharmony_ci continue; 9536cd6a6acSopenharmony_ci case FTS_DNR: 9546cd6a6acSopenharmony_ci error = errno; 9556cd6a6acSopenharmony_ci errno = ftsent->fts_errno; 9566cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 9576cd6a6acSopenharmony_ci "Could not read %s: %m.\n", 9586cd6a6acSopenharmony_ci ftsent->fts_path); 9596cd6a6acSopenharmony_ci errno = error; 9606cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 9616cd6a6acSopenharmony_ci continue; 9626cd6a6acSopenharmony_ci case FTS_NS: 9636cd6a6acSopenharmony_ci error = errno; 9646cd6a6acSopenharmony_ci errno = ftsent->fts_errno; 9656cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 9666cd6a6acSopenharmony_ci "Could not stat %s: %m.\n", 9676cd6a6acSopenharmony_ci ftsent->fts_path); 9686cd6a6acSopenharmony_ci errno = error; 9696cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 9706cd6a6acSopenharmony_ci continue; 9716cd6a6acSopenharmony_ci case FTS_ERR: 9726cd6a6acSopenharmony_ci error = errno; 9736cd6a6acSopenharmony_ci errno = ftsent->fts_errno; 9746cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 9756cd6a6acSopenharmony_ci "Error on %s: %m.\n", 9766cd6a6acSopenharmony_ci ftsent->fts_path); 9776cd6a6acSopenharmony_ci errno = error; 9786cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 9796cd6a6acSopenharmony_ci continue; 9806cd6a6acSopenharmony_ci case FTS_D: 9816cd6a6acSopenharmony_ci if (state->sfsb.f_type == SYSFS_MAGIC && 9826cd6a6acSopenharmony_ci !selabel_partial_match(fc_sehandle, 9836cd6a6acSopenharmony_ci ftsent->fts_path)) { 9846cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 9856cd6a6acSopenharmony_ci continue; 9866cd6a6acSopenharmony_ci } 9876cd6a6acSopenharmony_ci 9886cd6a6acSopenharmony_ci if (check_excluded(ftsent->fts_path)) { 9896cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 9906cd6a6acSopenharmony_ci continue; 9916cd6a6acSopenharmony_ci } 9926cd6a6acSopenharmony_ci 9936cd6a6acSopenharmony_ci if (state->setrestorecondigest) { 9946cd6a6acSopenharmony_ci struct dir_hash_node *new_node = NULL; 9956cd6a6acSopenharmony_ci 9966cd6a6acSopenharmony_ci if (check_context_match_for_dir(ftsent->fts_path, 9976cd6a6acSopenharmony_ci &new_node, 9986cd6a6acSopenharmony_ci state->error) && 9996cd6a6acSopenharmony_ci !state->ignore_digest) { 10006cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 10016cd6a6acSopenharmony_ci "Skipping restorecon on directory(%s)\n", 10026cd6a6acSopenharmony_ci ftsent->fts_path); 10036cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 10046cd6a6acSopenharmony_ci continue; 10056cd6a6acSopenharmony_ci } 10066cd6a6acSopenharmony_ci 10076cd6a6acSopenharmony_ci if (new_node && !state->error) { 10086cd6a6acSopenharmony_ci if (!state->current) { 10096cd6a6acSopenharmony_ci state->current = new_node; 10106cd6a6acSopenharmony_ci state->head = state->current; 10116cd6a6acSopenharmony_ci } else { 10126cd6a6acSopenharmony_ci state->current->next = new_node; 10136cd6a6acSopenharmony_ci state->current = new_node; 10146cd6a6acSopenharmony_ci } 10156cd6a6acSopenharmony_ci } 10166cd6a6acSopenharmony_ci } 10176cd6a6acSopenharmony_ci 10186cd6a6acSopenharmony_ci if (state->flags.skipelx && is_in_skip_elx(ftsent->fts_path)) { 10196cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 10206cd6a6acSopenharmony_ci continue; 10216cd6a6acSopenharmony_ci } 10226cd6a6acSopenharmony_ci 10236cd6a6acSopenharmony_ci enum skip_type skip_ignore_flag = skip_ignore_relabel(ftsent->fts_path); 10246cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 10256cd6a6acSopenharmony_ci "ignore cfg parsing result %d \n", 10266cd6a6acSopenharmony_ci skip_ignore_flag); 10276cd6a6acSopenharmony_ci switch (skip_ignore_flag) { 10286cd6a6acSopenharmony_ci case SKIP_SELF_SUB_DIR: 10296cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 10306cd6a6acSopenharmony_ci "Skipping restorecon on directory(%s), cause ignroe_cfg\n", 10316cd6a6acSopenharmony_ci ftsent->fts_path); 10326cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 10336cd6a6acSopenharmony_ci continue; 10346cd6a6acSopenharmony_ci case SKIP_SUB_DIR: 10356cd6a6acSopenharmony_ci selinux_log(SELINUX_INFO, 10366cd6a6acSopenharmony_ci "Skipping restorecon on directory(%s) sub directory, cause ignroe_cfg\n", 10376cd6a6acSopenharmony_ci ftsent->fts_path); 10386cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 10396cd6a6acSopenharmony_ci default: 10406cd6a6acSopenharmony_ci break; 10416cd6a6acSopenharmony_ci } 10426cd6a6acSopenharmony_ci 10436cd6a6acSopenharmony_ci /* fall through */ 10446cd6a6acSopenharmony_ci default: 10456cd6a6acSopenharmony_ci if (strlcpy(ent_path, ftsent->fts_path, sizeof(ent_path)) >= sizeof(ent_path)) { 10466cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 10476cd6a6acSopenharmony_ci "Path name too long on %s.\n", 10486cd6a6acSopenharmony_ci ftsent->fts_path); 10496cd6a6acSopenharmony_ci errno = ENAMETOOLONG; 10506cd6a6acSopenharmony_ci state->error = -1; 10516cd6a6acSopenharmony_ci state->abort = true; 10526cd6a6acSopenharmony_ci goto finish; 10536cd6a6acSopenharmony_ci } 10546cd6a6acSopenharmony_ci 10556cd6a6acSopenharmony_ci ent_st = *ftsent->fts_statp; 10566cd6a6acSopenharmony_ci if (state->parallel) 10576cd6a6acSopenharmony_ci pthread_mutex_unlock(&state->mutex); 10586cd6a6acSopenharmony_ci 10596cd6a6acSopenharmony_ci error = restorecon_sb(ent_path, &ent_st, &state->flags, 10606cd6a6acSopenharmony_ci first); 10616cd6a6acSopenharmony_ci 10626cd6a6acSopenharmony_ci if (state->parallel) { 10636cd6a6acSopenharmony_ci pthread_mutex_lock(&state->mutex); 10646cd6a6acSopenharmony_ci if (state->abort) 10656cd6a6acSopenharmony_ci goto unlock; 10666cd6a6acSopenharmony_ci } 10676cd6a6acSopenharmony_ci 10686cd6a6acSopenharmony_ci first = false; 10696cd6a6acSopenharmony_ci if (error) { 10706cd6a6acSopenharmony_ci if (state->flags.abort_on_error) { 10716cd6a6acSopenharmony_ci state->error = error; 10726cd6a6acSopenharmony_ci state->abort = true; 10736cd6a6acSopenharmony_ci goto finish; 10746cd6a6acSopenharmony_ci } 10756cd6a6acSopenharmony_ci if (state->flags.count_errors) 10766cd6a6acSopenharmony_ci state->skipped_errors++; 10776cd6a6acSopenharmony_ci else 10786cd6a6acSopenharmony_ci state->error = error; 10796cd6a6acSopenharmony_ci } 10806cd6a6acSopenharmony_ci break; 10816cd6a6acSopenharmony_ci } 10826cd6a6acSopenharmony_ci } 10836cd6a6acSopenharmony_ci 10846cd6a6acSopenharmony_cifinish: 10856cd6a6acSopenharmony_ci if (!state->saved_errno) 10866cd6a6acSopenharmony_ci state->saved_errno = errno; 10876cd6a6acSopenharmony_ciunlock: 10886cd6a6acSopenharmony_ci if (state->parallel) 10896cd6a6acSopenharmony_ci pthread_mutex_unlock(&state->mutex); 10906cd6a6acSopenharmony_ci return NULL; 10916cd6a6acSopenharmony_ci} 10926cd6a6acSopenharmony_ci 10936cd6a6acSopenharmony_cistatic int selinux_restorecon_common(const char *pathname_orig, 10946cd6a6acSopenharmony_ci unsigned int restorecon_flags, 10956cd6a6acSopenharmony_ci size_t nthreads) 10966cd6a6acSopenharmony_ci{ 10976cd6a6acSopenharmony_ci struct rest_state state; 10986cd6a6acSopenharmony_ci 10996cd6a6acSopenharmony_ci state.flags.nochange = (restorecon_flags & 11006cd6a6acSopenharmony_ci SELINUX_RESTORECON_NOCHANGE) ? true : false; 11016cd6a6acSopenharmony_ci state.flags.verbose = (restorecon_flags & 11026cd6a6acSopenharmony_ci SELINUX_RESTORECON_VERBOSE) ? true : false; 11036cd6a6acSopenharmony_ci state.flags.progress = (restorecon_flags & 11046cd6a6acSopenharmony_ci SELINUX_RESTORECON_PROGRESS) ? true : false; 11056cd6a6acSopenharmony_ci state.flags.mass_relabel = (restorecon_flags & 11066cd6a6acSopenharmony_ci SELINUX_RESTORECON_MASS_RELABEL) ? true : false; 11076cd6a6acSopenharmony_ci state.flags.recurse = (restorecon_flags & 11086cd6a6acSopenharmony_ci SELINUX_RESTORECON_RECURSE) ? true : false; 11096cd6a6acSopenharmony_ci state.flags.set_specctx = (restorecon_flags & 11106cd6a6acSopenharmony_ci SELINUX_RESTORECON_SET_SPECFILE_CTX) ? true : false; 11116cd6a6acSopenharmony_ci state.flags.userealpath = (restorecon_flags & 11126cd6a6acSopenharmony_ci SELINUX_RESTORECON_REALPATH) ? true : false; 11136cd6a6acSopenharmony_ci state.flags.set_xdev = (restorecon_flags & 11146cd6a6acSopenharmony_ci SELINUX_RESTORECON_XDEV) ? true : false; 11156cd6a6acSopenharmony_ci state.flags.add_assoc = (restorecon_flags & 11166cd6a6acSopenharmony_ci SELINUX_RESTORECON_ADD_ASSOC) ? true : false; 11176cd6a6acSopenharmony_ci state.flags.abort_on_error = (restorecon_flags & 11186cd6a6acSopenharmony_ci SELINUX_RESTORECON_ABORT_ON_ERROR) ? true : false; 11196cd6a6acSopenharmony_ci state.flags.syslog_changes = (restorecon_flags & 11206cd6a6acSopenharmony_ci SELINUX_RESTORECON_SYSLOG_CHANGES) ? true : false; 11216cd6a6acSopenharmony_ci state.flags.log_matches = (restorecon_flags & 11226cd6a6acSopenharmony_ci SELINUX_RESTORECON_LOG_MATCHES) ? true : false; 11236cd6a6acSopenharmony_ci state.flags.ignore_noent = (restorecon_flags & 11246cd6a6acSopenharmony_ci SELINUX_RESTORECON_IGNORE_NOENTRY) ? true : false; 11256cd6a6acSopenharmony_ci state.flags.warnonnomatch = true; 11266cd6a6acSopenharmony_ci state.flags.conflicterror = (restorecon_flags & 11276cd6a6acSopenharmony_ci SELINUX_RESTORECON_CONFLICT_ERROR) ? true : false; 11286cd6a6acSopenharmony_ci ignore_mounts = (restorecon_flags & 11296cd6a6acSopenharmony_ci SELINUX_RESTORECON_IGNORE_MOUNTS) ? true : false; 11306cd6a6acSopenharmony_ci state.ignore_digest = (restorecon_flags & 11316cd6a6acSopenharmony_ci SELINUX_RESTORECON_IGNORE_DIGEST) ? true : false; 11326cd6a6acSopenharmony_ci state.flags.count_errors = (restorecon_flags & 11336cd6a6acSopenharmony_ci SELINUX_RESTORECON_COUNT_ERRORS) ? true : false; 11346cd6a6acSopenharmony_ci state.flags.skipelx = (restorecon_flags & 11356cd6a6acSopenharmony_ci SELINUX_RESTORECON_SKIPELX) ? true : false; 11366cd6a6acSopenharmony_ci state.setrestorecondigest = true; 11376cd6a6acSopenharmony_ci 11386cd6a6acSopenharmony_ci state.head = NULL; 11396cd6a6acSopenharmony_ci state.current = NULL; 11406cd6a6acSopenharmony_ci state.abort = false; 11416cd6a6acSopenharmony_ci state.error = 0; 11426cd6a6acSopenharmony_ci state.skipped_errors = 0; 11436cd6a6acSopenharmony_ci state.saved_errno = 0; 11446cd6a6acSopenharmony_ci 11456cd6a6acSopenharmony_ci struct stat sb; 11466cd6a6acSopenharmony_ci char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname; 11476cd6a6acSopenharmony_ci char *paths[2] = { NULL, NULL }; 11486cd6a6acSopenharmony_ci int fts_flags, error; 11496cd6a6acSopenharmony_ci struct dir_hash_node *current = NULL; 11506cd6a6acSopenharmony_ci 11516cd6a6acSopenharmony_ci if (state.flags.verbose && state.flags.progress) 11526cd6a6acSopenharmony_ci state.flags.verbose = false; 11536cd6a6acSopenharmony_ci 11546cd6a6acSopenharmony_ci __selinux_once(fc_once, restorecon_init); 11556cd6a6acSopenharmony_ci 11566cd6a6acSopenharmony_ci if (!fc_sehandle) 11576cd6a6acSopenharmony_ci return -1; 11586cd6a6acSopenharmony_ci 11596cd6a6acSopenharmony_ci /* 11606cd6a6acSopenharmony_ci * If selabel_no_digest = true then no digest has been requested by 11616cd6a6acSopenharmony_ci * an external selabel_open(3) call. 11626cd6a6acSopenharmony_ci */ 11636cd6a6acSopenharmony_ci if (selabel_no_digest || 11646cd6a6acSopenharmony_ci (restorecon_flags & SELINUX_RESTORECON_SKIP_DIGEST)) 11656cd6a6acSopenharmony_ci state.setrestorecondigest = false; 11666cd6a6acSopenharmony_ci 11676cd6a6acSopenharmony_ci if (!__pthread_supported) { 11686cd6a6acSopenharmony_ci if (nthreads != 1) { 11696cd6a6acSopenharmony_ci nthreads = 1; 11706cd6a6acSopenharmony_ci selinux_log(SELINUX_WARNING, 11716cd6a6acSopenharmony_ci "Threading functionality not available, falling back to 1 thread."); 11726cd6a6acSopenharmony_ci } 11736cd6a6acSopenharmony_ci } else if (nthreads == 0) { 11746cd6a6acSopenharmony_ci long nproc = sysconf(_SC_NPROCESSORS_ONLN); 11756cd6a6acSopenharmony_ci 11766cd6a6acSopenharmony_ci if (nproc > 0) { 11776cd6a6acSopenharmony_ci nthreads = nproc; 11786cd6a6acSopenharmony_ci } else { 11796cd6a6acSopenharmony_ci nthreads = 1; 11806cd6a6acSopenharmony_ci selinux_log(SELINUX_WARNING, 11816cd6a6acSopenharmony_ci "Unable to detect CPU count, falling back to 1 thread."); 11826cd6a6acSopenharmony_ci } 11836cd6a6acSopenharmony_ci } 11846cd6a6acSopenharmony_ci 11856cd6a6acSopenharmony_ci /* 11866cd6a6acSopenharmony_ci * Convert passed-in pathname to canonical pathname by resolving 11876cd6a6acSopenharmony_ci * realpath of containing dir, then appending last component name. 11886cd6a6acSopenharmony_ci */ 11896cd6a6acSopenharmony_ci if (state.flags.userealpath) { 11906cd6a6acSopenharmony_ci char *basename_cpy = strdup(pathname_orig); 11916cd6a6acSopenharmony_ci if (!basename_cpy) 11926cd6a6acSopenharmony_ci goto realpatherr; 11936cd6a6acSopenharmony_ci pathbname = basename(basename_cpy); 11946cd6a6acSopenharmony_ci if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || 11956cd6a6acSopenharmony_ci !strcmp(pathbname, "..")) { 11966cd6a6acSopenharmony_ci pathname = realpath(pathname_orig, NULL); 11976cd6a6acSopenharmony_ci if (!pathname) { 11986cd6a6acSopenharmony_ci free(basename_cpy); 11996cd6a6acSopenharmony_ci /* missing parent directory */ 12006cd6a6acSopenharmony_ci if (state.flags.ignore_noent && errno == ENOENT) { 12016cd6a6acSopenharmony_ci return 0; 12026cd6a6acSopenharmony_ci } 12036cd6a6acSopenharmony_ci goto realpatherr; 12046cd6a6acSopenharmony_ci } 12056cd6a6acSopenharmony_ci } else { 12066cd6a6acSopenharmony_ci char *dirname_cpy = strdup(pathname_orig); 12076cd6a6acSopenharmony_ci if (!dirname_cpy) { 12086cd6a6acSopenharmony_ci free(basename_cpy); 12096cd6a6acSopenharmony_ci goto realpatherr; 12106cd6a6acSopenharmony_ci } 12116cd6a6acSopenharmony_ci pathdname = dirname(dirname_cpy); 12126cd6a6acSopenharmony_ci pathdnamer = realpath(pathdname, NULL); 12136cd6a6acSopenharmony_ci free(dirname_cpy); 12146cd6a6acSopenharmony_ci if (!pathdnamer) { 12156cd6a6acSopenharmony_ci free(basename_cpy); 12166cd6a6acSopenharmony_ci if (state.flags.ignore_noent && errno == ENOENT) { 12176cd6a6acSopenharmony_ci return 0; 12186cd6a6acSopenharmony_ci } 12196cd6a6acSopenharmony_ci goto realpatherr; 12206cd6a6acSopenharmony_ci } 12216cd6a6acSopenharmony_ci if (!strcmp(pathdnamer, "/")) 12226cd6a6acSopenharmony_ci error = asprintf(&pathname, "/%s", pathbname); 12236cd6a6acSopenharmony_ci else 12246cd6a6acSopenharmony_ci error = asprintf(&pathname, "%s/%s", 12256cd6a6acSopenharmony_ci pathdnamer, pathbname); 12266cd6a6acSopenharmony_ci if (error < 0) { 12276cd6a6acSopenharmony_ci free(basename_cpy); 12286cd6a6acSopenharmony_ci goto oom; 12296cd6a6acSopenharmony_ci } 12306cd6a6acSopenharmony_ci } 12316cd6a6acSopenharmony_ci free(basename_cpy); 12326cd6a6acSopenharmony_ci } else { 12336cd6a6acSopenharmony_ci pathname = strdup(pathname_orig); 12346cd6a6acSopenharmony_ci if (!pathname) 12356cd6a6acSopenharmony_ci goto oom; 12366cd6a6acSopenharmony_ci } 12376cd6a6acSopenharmony_ci 12386cd6a6acSopenharmony_ci paths[0] = pathname; 12396cd6a6acSopenharmony_ci 12406cd6a6acSopenharmony_ci if (lstat(pathname, &sb) < 0) { 12416cd6a6acSopenharmony_ci if (state.flags.ignore_noent && errno == ENOENT) { 12426cd6a6acSopenharmony_ci free(pathdnamer); 12436cd6a6acSopenharmony_ci free(pathname); 12446cd6a6acSopenharmony_ci return 0; 12456cd6a6acSopenharmony_ci } else { 12466cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 12476cd6a6acSopenharmony_ci "lstat(%s) failed: %m\n", 12486cd6a6acSopenharmony_ci pathname); 12496cd6a6acSopenharmony_ci error = -1; 12506cd6a6acSopenharmony_ci goto cleanup; 12516cd6a6acSopenharmony_ci } 12526cd6a6acSopenharmony_ci } 12536cd6a6acSopenharmony_ci 12546cd6a6acSopenharmony_ci /* Skip digest if not a directory */ 12556cd6a6acSopenharmony_ci if (!S_ISDIR(sb.st_mode)) 12566cd6a6acSopenharmony_ci state.setrestorecondigest = false; 12576cd6a6acSopenharmony_ci 12586cd6a6acSopenharmony_ci if (!state.flags.recurse) { 12596cd6a6acSopenharmony_ci if (check_excluded(pathname)) { 12606cd6a6acSopenharmony_ci error = 0; 12616cd6a6acSopenharmony_ci goto cleanup; 12626cd6a6acSopenharmony_ci } 12636cd6a6acSopenharmony_ci 12646cd6a6acSopenharmony_ci error = restorecon_sb(pathname, &sb, &state.flags, true); 12656cd6a6acSopenharmony_ci goto cleanup; 12666cd6a6acSopenharmony_ci } 12676cd6a6acSopenharmony_ci 12686cd6a6acSopenharmony_ci /* Obtain fs type */ 12696cd6a6acSopenharmony_ci memset(&state.sfsb, 0, sizeof(state.sfsb)); 12706cd6a6acSopenharmony_ci if (!S_ISLNK(sb.st_mode) && statfs(pathname, &state.sfsb) < 0) { 12716cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 12726cd6a6acSopenharmony_ci "statfs(%s) failed: %m\n", 12736cd6a6acSopenharmony_ci pathname); 12746cd6a6acSopenharmony_ci error = -1; 12756cd6a6acSopenharmony_ci goto cleanup; 12766cd6a6acSopenharmony_ci } 12776cd6a6acSopenharmony_ci 12786cd6a6acSopenharmony_ci /* Skip digest on in-memory filesystems and /sys */ 12796cd6a6acSopenharmony_ci if (state.sfsb.f_type == RAMFS_MAGIC || state.sfsb.f_type == TMPFS_MAGIC || 12806cd6a6acSopenharmony_ci state.sfsb.f_type == SYSFS_MAGIC) 12816cd6a6acSopenharmony_ci state.setrestorecondigest = false; 12826cd6a6acSopenharmony_ci 12836cd6a6acSopenharmony_ci if (state.flags.set_xdev) 12846cd6a6acSopenharmony_ci fts_flags = FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV; 12856cd6a6acSopenharmony_ci else 12866cd6a6acSopenharmony_ci fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 12876cd6a6acSopenharmony_ci 12886cd6a6acSopenharmony_ci state.fts = fts_open(paths, fts_flags, NULL); 12896cd6a6acSopenharmony_ci if (!state.fts) 12906cd6a6acSopenharmony_ci goto fts_err; 12916cd6a6acSopenharmony_ci 12926cd6a6acSopenharmony_ci state.ftsent_first = fts_read(state.fts); 12936cd6a6acSopenharmony_ci if (!state.ftsent_first) 12946cd6a6acSopenharmony_ci goto fts_err; 12956cd6a6acSopenharmony_ci 12966cd6a6acSopenharmony_ci /* 12976cd6a6acSopenharmony_ci * Keep the inode of the first device. This is because the FTS_XDEV 12986cd6a6acSopenharmony_ci * flag tells fts not to descend into directories with different 12996cd6a6acSopenharmony_ci * device numbers, but fts will still give back the actual directory. 13006cd6a6acSopenharmony_ci * By saving the device number of the directory that was passed to 13016cd6a6acSopenharmony_ci * selinux_restorecon() and then skipping all actions on any 13026cd6a6acSopenharmony_ci * directories with a different device number when the FTS_XDEV flag 13036cd6a6acSopenharmony_ci * is set (from http://marc.info/?l=selinux&m=124688830500777&w=2). 13046cd6a6acSopenharmony_ci */ 13056cd6a6acSopenharmony_ci state.dev_num = state.ftsent_first->fts_statp->st_dev; 13066cd6a6acSopenharmony_ci 13076cd6a6acSopenharmony_ci bool load_ignore_path = load_ignore_cfg(); 13086cd6a6acSopenharmony_ci if (!load_ignore_path) { 13096cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "Failed to load ignore cfg!\n"); 13106cd6a6acSopenharmony_ci } 13116cd6a6acSopenharmony_ci 13126cd6a6acSopenharmony_ci if (nthreads == 1) { 13136cd6a6acSopenharmony_ci state.parallel = false; 13146cd6a6acSopenharmony_ci selinux_restorecon_thread(&state); 13156cd6a6acSopenharmony_ci } else { 13166cd6a6acSopenharmony_ci size_t i; 13176cd6a6acSopenharmony_ci pthread_t self = pthread_self(); 13186cd6a6acSopenharmony_ci pthread_t *threads = NULL; 13196cd6a6acSopenharmony_ci 13206cd6a6acSopenharmony_ci pthread_mutex_init(&state.mutex, NULL); 13216cd6a6acSopenharmony_ci 13226cd6a6acSopenharmony_ci threads = calloc(nthreads - 1, sizeof(*threads)); 13236cd6a6acSopenharmony_ci if (!threads) 13246cd6a6acSopenharmony_ci goto oom; 13256cd6a6acSopenharmony_ci 13266cd6a6acSopenharmony_ci state.parallel = true; 13276cd6a6acSopenharmony_ci /* 13286cd6a6acSopenharmony_ci * Start (nthreads - 1) threads - the main thread is going to 13296cd6a6acSopenharmony_ci * take part, too. 13306cd6a6acSopenharmony_ci */ 13316cd6a6acSopenharmony_ci for (i = 0; i < nthreads - 1; i++) { 13326cd6a6acSopenharmony_ci if (pthread_create(&threads[i], NULL, 13336cd6a6acSopenharmony_ci selinux_restorecon_thread, &state)) { 13346cd6a6acSopenharmony_ci /* 13356cd6a6acSopenharmony_ci * If any thread fails to be created, just mark 13366cd6a6acSopenharmony_ci * it as such and let the successfully created 13376cd6a6acSopenharmony_ci * threads do the job. In the worst case the 13386cd6a6acSopenharmony_ci * main thread will do everything, but that's 13396cd6a6acSopenharmony_ci * still better than to give up. 13406cd6a6acSopenharmony_ci */ 13416cd6a6acSopenharmony_ci threads[i] = self; 13426cd6a6acSopenharmony_ci } 13436cd6a6acSopenharmony_ci } 13446cd6a6acSopenharmony_ci 13456cd6a6acSopenharmony_ci /* Let's join in on the fun! */ 13466cd6a6acSopenharmony_ci selinux_restorecon_thread(&state); 13476cd6a6acSopenharmony_ci 13486cd6a6acSopenharmony_ci /* Now wait for all threads to finish. */ 13496cd6a6acSopenharmony_ci for (i = 0; i < nthreads - 1; i++) { 13506cd6a6acSopenharmony_ci /* Skip threads that failed to be created. */ 13516cd6a6acSopenharmony_ci if (pthread_equal(threads[i], self)) 13526cd6a6acSopenharmony_ci continue; 13536cd6a6acSopenharmony_ci pthread_join(threads[i], NULL); 13546cd6a6acSopenharmony_ci } 13556cd6a6acSopenharmony_ci free(threads); 13566cd6a6acSopenharmony_ci 13576cd6a6acSopenharmony_ci pthread_mutex_destroy(&state.mutex); 13586cd6a6acSopenharmony_ci } 13596cd6a6acSopenharmony_ci 13606cd6a6acSopenharmony_ci error = state.error; 13616cd6a6acSopenharmony_ci if (state.saved_errno) 13626cd6a6acSopenharmony_ci goto out; 13636cd6a6acSopenharmony_ci 13646cd6a6acSopenharmony_ci /* 13656cd6a6acSopenharmony_ci * Labeling successful. Write partial match digests for subdirectories. 13666cd6a6acSopenharmony_ci * TODO: Write digest upon FTS_DP if no error occurs in its descents. 13676cd6a6acSopenharmony_ci * Note: we can't ignore errors here that we've masked due to 13686cd6a6acSopenharmony_ci * SELINUX_RESTORECON_COUNT_ERRORS. 13696cd6a6acSopenharmony_ci */ 13706cd6a6acSopenharmony_ci if (state.setrestorecondigest && !state.flags.nochange && !error && 13716cd6a6acSopenharmony_ci state.skipped_errors == 0) { 13726cd6a6acSopenharmony_ci current = state.head; 13736cd6a6acSopenharmony_ci while (current != NULL) { 13746cd6a6acSopenharmony_ci if (setxattr(current->path, 13756cd6a6acSopenharmony_ci RESTORECON_PARTIAL_MATCH_DIGEST, 13766cd6a6acSopenharmony_ci current->digest, 13776cd6a6acSopenharmony_ci SHA1_HASH_SIZE, 0) < 0) { 13786cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 13796cd6a6acSopenharmony_ci "setxattr failed: %s: %m\n", 13806cd6a6acSopenharmony_ci current->path); 13816cd6a6acSopenharmony_ci } 13826cd6a6acSopenharmony_ci current = current->next; 13836cd6a6acSopenharmony_ci } 13846cd6a6acSopenharmony_ci } 13856cd6a6acSopenharmony_ci 13866cd6a6acSopenharmony_ci skipped_errors = state.skipped_errors; 13876cd6a6acSopenharmony_ci 13886cd6a6acSopenharmony_ciout: 13896cd6a6acSopenharmony_ci if (state.flags.progress && state.flags.mass_relabel) 13906cd6a6acSopenharmony_ci fprintf(stdout, "\r%s 100.0%%\n", pathname); 13916cd6a6acSopenharmony_ci 13926cd6a6acSopenharmony_ci (void) fts_close(state.fts); 13936cd6a6acSopenharmony_ci errno = state.saved_errno; 13946cd6a6acSopenharmony_cicleanup: 13956cd6a6acSopenharmony_ci if (state.flags.add_assoc) { 13966cd6a6acSopenharmony_ci if (state.flags.verbose) 13976cd6a6acSopenharmony_ci filespec_eval(); 13986cd6a6acSopenharmony_ci filespec_destroy(); 13996cd6a6acSopenharmony_ci } 14006cd6a6acSopenharmony_ci free(pathdnamer); 14016cd6a6acSopenharmony_ci free(pathname); 14026cd6a6acSopenharmony_ci 14036cd6a6acSopenharmony_ci current = state.head; 14046cd6a6acSopenharmony_ci while (current != NULL) { 14056cd6a6acSopenharmony_ci struct dir_hash_node *next = current->next; 14066cd6a6acSopenharmony_ci 14076cd6a6acSopenharmony_ci free(current->path); 14086cd6a6acSopenharmony_ci free(current); 14096cd6a6acSopenharmony_ci current = next; 14106cd6a6acSopenharmony_ci } 14116cd6a6acSopenharmony_ci return error; 14126cd6a6acSopenharmony_ci 14136cd6a6acSopenharmony_cioom: 14146cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 14156cd6a6acSopenharmony_ci error = -1; 14166cd6a6acSopenharmony_ci goto cleanup; 14176cd6a6acSopenharmony_ci 14186cd6a6acSopenharmony_cirealpatherr: 14196cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 14206cd6a6acSopenharmony_ci "SELinux: Could not get canonical path for %s restorecon: %m.\n", 14216cd6a6acSopenharmony_ci pathname_orig); 14226cd6a6acSopenharmony_ci error = -1; 14236cd6a6acSopenharmony_ci goto cleanup; 14246cd6a6acSopenharmony_ci 14256cd6a6acSopenharmony_cifts_err: 14266cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 14276cd6a6acSopenharmony_ci "fts error while labeling %s: %m\n", 14286cd6a6acSopenharmony_ci paths[0]); 14296cd6a6acSopenharmony_ci error = -1; 14306cd6a6acSopenharmony_ci goto cleanup; 14316cd6a6acSopenharmony_ci} 14326cd6a6acSopenharmony_ci 14336cd6a6acSopenharmony_ci 14346cd6a6acSopenharmony_ci/* 14356cd6a6acSopenharmony_ci * Public API 14366cd6a6acSopenharmony_ci */ 14376cd6a6acSopenharmony_ci 14386cd6a6acSopenharmony_ci/* selinux_restorecon(3) - Main function that is responsible for labeling */ 14396cd6a6acSopenharmony_ciint selinux_restorecon(const char *pathname_orig, 14406cd6a6acSopenharmony_ci unsigned int restorecon_flags) 14416cd6a6acSopenharmony_ci{ 14426cd6a6acSopenharmony_ci return selinux_restorecon_common(pathname_orig, restorecon_flags, 1); 14436cd6a6acSopenharmony_ci} 14446cd6a6acSopenharmony_ci 14456cd6a6acSopenharmony_ci/* selinux_restorecon_parallel(3) - Parallel version of selinux_restorecon(3) */ 14466cd6a6acSopenharmony_ciint selinux_restorecon_parallel(const char *pathname_orig, 14476cd6a6acSopenharmony_ci unsigned int restorecon_flags, 14486cd6a6acSopenharmony_ci size_t nthreads) 14496cd6a6acSopenharmony_ci{ 14506cd6a6acSopenharmony_ci return selinux_restorecon_common(pathname_orig, restorecon_flags, nthreads); 14516cd6a6acSopenharmony_ci} 14526cd6a6acSopenharmony_ci 14536cd6a6acSopenharmony_ci/* selinux_restorecon_set_sehandle(3) is called to set the global fc handle */ 14546cd6a6acSopenharmony_civoid selinux_restorecon_set_sehandle(struct selabel_handle *hndl) 14556cd6a6acSopenharmony_ci{ 14566cd6a6acSopenharmony_ci char **specfiles; 14576cd6a6acSopenharmony_ci unsigned char *fc_digest; 14586cd6a6acSopenharmony_ci size_t num_specfiles, fc_digest_len; 14596cd6a6acSopenharmony_ci 14606cd6a6acSopenharmony_ci fc_sehandle = hndl; 14616cd6a6acSopenharmony_ci if (!fc_sehandle) 14626cd6a6acSopenharmony_ci return; 14636cd6a6acSopenharmony_ci 14646cd6a6acSopenharmony_ci /* Check if digest requested in selabel_open(3), if so use it. */ 14656cd6a6acSopenharmony_ci if (selabel_digest(fc_sehandle, &fc_digest, &fc_digest_len, 14666cd6a6acSopenharmony_ci &specfiles, &num_specfiles) < 0) 14676cd6a6acSopenharmony_ci selabel_no_digest = true; 14686cd6a6acSopenharmony_ci else 14696cd6a6acSopenharmony_ci selabel_no_digest = false; 14706cd6a6acSopenharmony_ci} 14716cd6a6acSopenharmony_ci 14726cd6a6acSopenharmony_ci 14736cd6a6acSopenharmony_ci/* 14746cd6a6acSopenharmony_ci * selinux_restorecon_default_handle(3) is called to set the global restorecon 14756cd6a6acSopenharmony_ci * handle by a process if the default params are required. 14766cd6a6acSopenharmony_ci */ 14776cd6a6acSopenharmony_cistruct selabel_handle *selinux_restorecon_default_handle(void) 14786cd6a6acSopenharmony_ci{ 14796cd6a6acSopenharmony_ci struct selabel_handle *sehandle; 14806cd6a6acSopenharmony_ci 14816cd6a6acSopenharmony_ci struct selinux_opt fc_opts[] = { 14826cd6a6acSopenharmony_ci { SELABEL_OPT_DIGEST, (char *)1 } 14836cd6a6acSopenharmony_ci }; 14846cd6a6acSopenharmony_ci 14856cd6a6acSopenharmony_ci sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, 1); 14866cd6a6acSopenharmony_ci 14876cd6a6acSopenharmony_ci if (!sehandle) { 14886cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 14896cd6a6acSopenharmony_ci "Error obtaining file context handle: %m\n"); 14906cd6a6acSopenharmony_ci return NULL; 14916cd6a6acSopenharmony_ci } 14926cd6a6acSopenharmony_ci 14936cd6a6acSopenharmony_ci selabel_no_digest = false; 14946cd6a6acSopenharmony_ci return sehandle; 14956cd6a6acSopenharmony_ci} 14966cd6a6acSopenharmony_ci 14976cd6a6acSopenharmony_ci/* 14986cd6a6acSopenharmony_ci * selinux_restorecon_set_exclude_list(3) is called to add additional entries 14996cd6a6acSopenharmony_ci * to be excluded from labeling checks. 15006cd6a6acSopenharmony_ci */ 15016cd6a6acSopenharmony_civoid selinux_restorecon_set_exclude_list(const char **exclude_list) 15026cd6a6acSopenharmony_ci{ 15036cd6a6acSopenharmony_ci int i; 15046cd6a6acSopenharmony_ci struct stat sb; 15056cd6a6acSopenharmony_ci 15066cd6a6acSopenharmony_ci for (i = 0; exclude_list[i]; i++) { 15076cd6a6acSopenharmony_ci if (lstat(exclude_list[i], &sb) < 0 && errno != EACCES) { 15086cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 15096cd6a6acSopenharmony_ci "lstat error on exclude path \"%s\", %m - ignoring.\n", 15106cd6a6acSopenharmony_ci exclude_list[i]); 15116cd6a6acSopenharmony_ci break; 15126cd6a6acSopenharmony_ci } 15136cd6a6acSopenharmony_ci if (add_exclude(exclude_list[i], CALLER_EXCLUDED) && 15146cd6a6acSopenharmony_ci errno == ENOMEM) 15156cd6a6acSopenharmony_ci assert(0); 15166cd6a6acSopenharmony_ci } 15176cd6a6acSopenharmony_ci} 15186cd6a6acSopenharmony_ci 15196cd6a6acSopenharmony_ci/* selinux_restorecon_set_alt_rootpath(3) sets an alternate rootpath. */ 15206cd6a6acSopenharmony_ciint selinux_restorecon_set_alt_rootpath(const char *alt_rootpath) 15216cd6a6acSopenharmony_ci{ 15226cd6a6acSopenharmony_ci size_t len; 15236cd6a6acSopenharmony_ci 15246cd6a6acSopenharmony_ci /* This should be NULL on first use */ 15256cd6a6acSopenharmony_ci if (rootpath) 15266cd6a6acSopenharmony_ci free(rootpath); 15276cd6a6acSopenharmony_ci 15286cd6a6acSopenharmony_ci rootpath = strdup(alt_rootpath); 15296cd6a6acSopenharmony_ci if (!rootpath) { 15306cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__); 15316cd6a6acSopenharmony_ci return -1; 15326cd6a6acSopenharmony_ci } 15336cd6a6acSopenharmony_ci 15346cd6a6acSopenharmony_ci /* trim trailing /, if present */ 15356cd6a6acSopenharmony_ci len = strlen(rootpath); 15366cd6a6acSopenharmony_ci while (len && (rootpath[len - 1] == '/')) 15376cd6a6acSopenharmony_ci rootpath[--len] = '\0'; 15386cd6a6acSopenharmony_ci rootpathlen = len; 15396cd6a6acSopenharmony_ci 15406cd6a6acSopenharmony_ci return 0; 15416cd6a6acSopenharmony_ci} 15426cd6a6acSopenharmony_ci 15436cd6a6acSopenharmony_ci/* selinux_restorecon_xattr(3) 15446cd6a6acSopenharmony_ci * Find RESTORECON_PARTIAL_MATCH_DIGEST entries. 15456cd6a6acSopenharmony_ci */ 15466cd6a6acSopenharmony_ciint selinux_restorecon_xattr(const char *pathname, unsigned int xattr_flags, 15476cd6a6acSopenharmony_ci struct dir_xattr ***xattr_list) 15486cd6a6acSopenharmony_ci{ 15496cd6a6acSopenharmony_ci bool recurse = (xattr_flags & 15506cd6a6acSopenharmony_ci SELINUX_RESTORECON_XATTR_RECURSE) ? true : false; 15516cd6a6acSopenharmony_ci bool delete_nonmatch = (xattr_flags & 15526cd6a6acSopenharmony_ci SELINUX_RESTORECON_XATTR_DELETE_NONMATCH_DIGESTS) ? true : false; 15536cd6a6acSopenharmony_ci bool delete_all = (xattr_flags & 15546cd6a6acSopenharmony_ci SELINUX_RESTORECON_XATTR_DELETE_ALL_DIGESTS) ? true : false; 15556cd6a6acSopenharmony_ci ignore_mounts = (xattr_flags & 15566cd6a6acSopenharmony_ci SELINUX_RESTORECON_XATTR_IGNORE_MOUNTS) ? true : false; 15576cd6a6acSopenharmony_ci 15586cd6a6acSopenharmony_ci int rc, fts_flags; 15596cd6a6acSopenharmony_ci struct stat sb; 15606cd6a6acSopenharmony_ci struct statfs sfsb; 15616cd6a6acSopenharmony_ci struct dir_xattr *current, *next; 15626cd6a6acSopenharmony_ci FTS *fts; 15636cd6a6acSopenharmony_ci FTSENT *ftsent; 15646cd6a6acSopenharmony_ci char *paths[2] = { NULL, NULL }; 15656cd6a6acSopenharmony_ci 15666cd6a6acSopenharmony_ci __selinux_once(fc_once, restorecon_init); 15676cd6a6acSopenharmony_ci 15686cd6a6acSopenharmony_ci if (!fc_sehandle) 15696cd6a6acSopenharmony_ci return -1; 15706cd6a6acSopenharmony_ci 15716cd6a6acSopenharmony_ci if (lstat(pathname, &sb) < 0) { 15726cd6a6acSopenharmony_ci if (errno == ENOENT) 15736cd6a6acSopenharmony_ci return 0; 15746cd6a6acSopenharmony_ci 15756cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 15766cd6a6acSopenharmony_ci "lstat(%s) failed: %m\n", 15776cd6a6acSopenharmony_ci pathname); 15786cd6a6acSopenharmony_ci return -1; 15796cd6a6acSopenharmony_ci } 15806cd6a6acSopenharmony_ci 15816cd6a6acSopenharmony_ci if (!recurse) { 15826cd6a6acSopenharmony_ci if (statfs(pathname, &sfsb) == 0) { 15836cd6a6acSopenharmony_ci if (sfsb.f_type == RAMFS_MAGIC || 15846cd6a6acSopenharmony_ci sfsb.f_type == TMPFS_MAGIC) 15856cd6a6acSopenharmony_ci return 0; 15866cd6a6acSopenharmony_ci } 15876cd6a6acSopenharmony_ci 15886cd6a6acSopenharmony_ci if (check_excluded(pathname)) 15896cd6a6acSopenharmony_ci return 0; 15906cd6a6acSopenharmony_ci 15916cd6a6acSopenharmony_ci rc = add_xattr_entry(pathname, delete_nonmatch, delete_all); 15926cd6a6acSopenharmony_ci 15936cd6a6acSopenharmony_ci if (!rc && dir_xattr_list) 15946cd6a6acSopenharmony_ci *xattr_list = &dir_xattr_list; 15956cd6a6acSopenharmony_ci else if (rc == -1) 15966cd6a6acSopenharmony_ci return rc; 15976cd6a6acSopenharmony_ci 15986cd6a6acSopenharmony_ci return 0; 15996cd6a6acSopenharmony_ci } 16006cd6a6acSopenharmony_ci 16016cd6a6acSopenharmony_ci paths[0] = (char *)pathname; 16026cd6a6acSopenharmony_ci fts_flags = FTS_PHYSICAL | FTS_NOCHDIR; 16036cd6a6acSopenharmony_ci 16046cd6a6acSopenharmony_ci fts = fts_open(paths, fts_flags, NULL); 16056cd6a6acSopenharmony_ci if (!fts) { 16066cd6a6acSopenharmony_ci selinux_log(SELINUX_ERROR, 16076cd6a6acSopenharmony_ci "fts error on %s: %m\n", 16086cd6a6acSopenharmony_ci paths[0]); 16096cd6a6acSopenharmony_ci return -1; 16106cd6a6acSopenharmony_ci } 16116cd6a6acSopenharmony_ci 16126cd6a6acSopenharmony_ci while ((ftsent = fts_read(fts)) != NULL) { 16136cd6a6acSopenharmony_ci switch (ftsent->fts_info) { 16146cd6a6acSopenharmony_ci case FTS_DP: 16156cd6a6acSopenharmony_ci continue; 16166cd6a6acSopenharmony_ci case FTS_D: 16176cd6a6acSopenharmony_ci if (statfs(ftsent->fts_path, &sfsb) == 0) { 16186cd6a6acSopenharmony_ci if (sfsb.f_type == RAMFS_MAGIC || 16196cd6a6acSopenharmony_ci sfsb.f_type == TMPFS_MAGIC) 16206cd6a6acSopenharmony_ci continue; 16216cd6a6acSopenharmony_ci } 16226cd6a6acSopenharmony_ci if (check_excluded(ftsent->fts_path)) { 16236cd6a6acSopenharmony_ci fts_set(fts, ftsent, FTS_SKIP); 16246cd6a6acSopenharmony_ci continue; 16256cd6a6acSopenharmony_ci } 16266cd6a6acSopenharmony_ci 16276cd6a6acSopenharmony_ci rc = add_xattr_entry(ftsent->fts_path, 16286cd6a6acSopenharmony_ci delete_nonmatch, delete_all); 16296cd6a6acSopenharmony_ci if (rc == 1) 16306cd6a6acSopenharmony_ci continue; 16316cd6a6acSopenharmony_ci else if (rc == -1) 16326cd6a6acSopenharmony_ci goto cleanup; 16336cd6a6acSopenharmony_ci break; 16346cd6a6acSopenharmony_ci default: 16356cd6a6acSopenharmony_ci break; 16366cd6a6acSopenharmony_ci } 16376cd6a6acSopenharmony_ci } 16386cd6a6acSopenharmony_ci 16396cd6a6acSopenharmony_ci if (dir_xattr_list) 16406cd6a6acSopenharmony_ci *xattr_list = &dir_xattr_list; 16416cd6a6acSopenharmony_ci 16426cd6a6acSopenharmony_ci (void) fts_close(fts); 16436cd6a6acSopenharmony_ci return 0; 16446cd6a6acSopenharmony_ci 16456cd6a6acSopenharmony_cicleanup: 16466cd6a6acSopenharmony_ci rc = errno; 16476cd6a6acSopenharmony_ci (void) fts_close(fts); 16486cd6a6acSopenharmony_ci errno = rc; 16496cd6a6acSopenharmony_ci 16506cd6a6acSopenharmony_ci if (dir_xattr_list) { 16516cd6a6acSopenharmony_ci /* Free any used memory */ 16526cd6a6acSopenharmony_ci current = dir_xattr_list; 16536cd6a6acSopenharmony_ci while (current) { 16546cd6a6acSopenharmony_ci next = current->next; 16556cd6a6acSopenharmony_ci free(current->directory); 16566cd6a6acSopenharmony_ci free(current->digest); 16576cd6a6acSopenharmony_ci free(current); 16586cd6a6acSopenharmony_ci current = next; 16596cd6a6acSopenharmony_ci } 16606cd6a6acSopenharmony_ci } 16616cd6a6acSopenharmony_ci return -1; 16626cd6a6acSopenharmony_ci} 16636cd6a6acSopenharmony_ci 16646cd6a6acSopenharmony_cilong unsigned selinux_restorecon_get_skipped_errors(void) 16656cd6a6acSopenharmony_ci{ 16666cd6a6acSopenharmony_ci return skipped_errors; 16676cd6a6acSopenharmony_ci} 1668