1#include <stdio.h> 2#include <stdlib.h> 3#include <string.h> 4#include <getopt.h> 5#include <errno.h> 6#include <stdbool.h> 7#include <sepol/sepol.h> 8#include <selinux/label.h> 9#include <selinux/restorecon.h> 10 11static char *policyfile; 12 13static char **exclude_list; 14static int exclude_count; 15 16static int validate_context(char **contextp) 17{ 18 char *context = *contextp, *tmpcon; 19 20 if (policyfile) { 21 if (sepol_check_context(context) < 0) { 22 fprintf(stderr, "Invalid context %s\n", context); 23 exit(-1); 24 } 25 } else if (security_canonicalize_context_raw(context, &tmpcon) == 0) { 26 free(context); 27 *contextp = tmpcon; 28 } else if (errno != ENOENT) { 29 fprintf(stderr, "Validate context error: %s\n", 30 strerror(errno)); 31 exit(-1); 32 } 33 34 return 0; 35} 36 37static __attribute__ ((__noreturn__)) void usage(const char *progname) 38{ 39 fprintf(stderr, 40 "\nusage: %s [-FCnRrdmiIaAsl] [-e dir] [-v|-P]\n" 41 "[-x alt_rootpath] [-p policy] [-f specfile] pathname ...\n" 42 "\nWhere:\n\t" 43 "-F Set the label to that in specfile.\n\t" 44 " If not set then reset the \"type\" component of the " 45 "label to that\n\t in the specfile.\n\t" 46 "-C Check labels even if the stored SHA1 digest matches\n\t" 47 " the specfiles SHA1 digest.\n\t" 48 "-n Don't change any file labels (passive check).\n\t" 49 "-R Recursively change file and directory labels.\n\t" 50 "-v Show changes in file labels (-v and -P are mutually " 51 " exclusive).\n\t" 52 "-P Show progress by printing \"*\" to stdout every 1000 files" 53 ",\n\t unless relabeling entire OS, then show percentage complete.\n\t" 54 "-r Use realpath(3) to convert pathnames to canonical form.\n\t" 55 "-d Prevent descending into directories that have a " 56 "different\n\t device number than the pathname from which " 57 "the descent began.\n\t" 58 "-m Do not automatically read /proc/mounts to determine what\n\t" 59 " non-seclabel mounts to exclude from relabeling.\n\t" 60 "-e Exclude this directory (add multiple -e entries).\n\t" 61 "-i Do not set SELABEL_OPT_DIGEST option when calling " 62 " selabel_open(3).\n\t" 63 "-I Ignore files that do not exist.\n\t" 64 "-a Add an association between an inode and a context.\n\t" 65 " If there is a different context that matched the inode,\n\t" 66 " then use the first context that matched.\n\t" 67 "-A Abort on errors during the file tree walk.\n\t" 68 "-s Log any label changes to syslog(3).\n\t" 69 "-l Log what specfile context matched each file.\n\t" 70 "-x Set alternate rootpath.\n\t" 71 "-p Optional binary policy file (also sets validate context " 72 "option).\n\t" 73 "-f Optional file contexts file.\n\t" 74 "pathname One or more paths to relabel.\n\n", 75 progname); 76 exit(-1); 77} 78 79static void add_exclude(const char *directory) 80{ 81 char **tmp_list; 82 83 if (directory == NULL || directory[0] != '/') { 84 fprintf(stderr, "Full path required for exclude: %s.\n", 85 directory); 86 exit(-1); 87 } 88 89 /* Add another two entries, one for directory, and the other to 90 * terminate the list */ 91 tmp_list = realloc(exclude_list, sizeof(char *) * (exclude_count + 2)); 92 if (!tmp_list) { 93 fprintf(stderr, "ERROR: realloc failed.\n"); 94 exit(-1); 95 } 96 exclude_list = tmp_list; 97 98 exclude_list[exclude_count] = strdup(directory); 99 if (!exclude_list[exclude_count]) { 100 fprintf(stderr, "ERROR: strdup failed.\n"); 101 exit(-1); 102 } 103 exclude_count++; 104 exclude_list[exclude_count] = NULL; 105} 106 107int main(int argc, char **argv) 108{ 109 int opt, i; 110 unsigned int restorecon_flags = 0; 111 char *path = NULL, *digest = NULL, *validate = NULL; 112 char *alt_rootpath = NULL; 113 FILE *policystream; 114 bool ignore_digest = false, require_selinux = true; 115 bool verbose = false, progress = false; 116 117 struct selabel_handle *hnd = NULL; 118 struct selinux_opt selabel_option[] = { 119 { SELABEL_OPT_PATH, path }, 120 { SELABEL_OPT_DIGEST, digest }, 121 { SELABEL_OPT_VALIDATE, validate } 122 }; 123 124 if (argc < 2) 125 usage(argv[0]); 126 127 exclude_list = NULL; 128 exclude_count = 0; 129 130 while ((opt = getopt(argc, argv, "iIFCnRvPrdaAslme:f:p:x:")) > 0) { 131 switch (opt) { 132 case 'F': 133 restorecon_flags |= 134 SELINUX_RESTORECON_SET_SPECFILE_CTX; 135 break; 136 case 'C': 137 restorecon_flags |= 138 SELINUX_RESTORECON_IGNORE_DIGEST; 139 break; 140 case 'n': 141 restorecon_flags |= SELINUX_RESTORECON_NOCHANGE; 142 break; 143 case 'R': 144 restorecon_flags |= SELINUX_RESTORECON_RECURSE; 145 break; 146 case 'v': 147 if (progress) { 148 fprintf(stderr, 149 "Progress and Verbose are mutually exclusive\n"); 150 exit(-1); 151 } 152 verbose = true; 153 restorecon_flags |= SELINUX_RESTORECON_VERBOSE; 154 break; 155 case 'P': 156 if (verbose) { 157 fprintf(stderr, 158 "Progress and Verbose are mutually exclusive\n"); 159 exit(-1); 160 } 161 progress = true; 162 restorecon_flags |= SELINUX_RESTORECON_PROGRESS; 163 break; 164 case 'r': 165 restorecon_flags |= SELINUX_RESTORECON_REALPATH; 166 break; 167 case 'd': 168 restorecon_flags |= SELINUX_RESTORECON_XDEV; 169 break; 170 case 'm': 171 restorecon_flags |= SELINUX_RESTORECON_IGNORE_MOUNTS; 172 break; 173 case 'e': 174 add_exclude(optarg); 175 break; 176 case 'p': 177 policyfile = optarg; 178 179 policystream = fopen(policyfile, "r"); 180 if (!policystream) { 181 fprintf(stderr, 182 "ERROR: opening %s: %s\n", 183 policyfile, strerror(errno)); 184 exit(-1); 185 } 186 187 if (sepol_set_policydb_from_file(policystream) < 0) { 188 fprintf(stderr, 189 "ERROR: reading policy %s: %s\n", 190 policyfile, strerror(errno)); 191 exit(-1); 192 } 193 fclose(policystream); 194 195 selinux_set_callback(SELINUX_CB_VALIDATE, 196 (union selinux_callback)&validate_context); 197 require_selinux = false; 198 break; 199 case 'f': 200 path = optarg; 201 break; 202 case 'i': 203 ignore_digest = true; 204 break; 205 case 'I': 206 restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY; 207 break; 208 case 'a': 209 restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC; 210 break; 211 case 'A': 212 restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR; 213 break; 214 case 's': 215 restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES; 216 break; 217 case 'l': 218 restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES; 219 break; 220 case 'x': 221 alt_rootpath = optarg; 222 break; 223 default: 224 usage(argv[0]); 225 } 226 } 227 228 if (require_selinux && (is_selinux_enabled() <= 0)) { 229 fprintf(stderr, 230 "SELinux must be enabled to perform this operation.\n"); 231 exit(-1); 232 } 233 234 if (optind >= argc) { 235 fprintf(stderr, "No pathname specified\n"); 236 exit(-1); 237 } 238 239 /* If any of these set then do our own selabel_open and pass 240 * handle to selinux_restorecon */ 241 if (ignore_digest || path || policyfile) { 242 if (path) 243 selabel_option[0].value = path; 244 else 245 selabel_option[0].value = NULL; 246 247 if (ignore_digest) 248 selabel_option[1].value = NULL; 249 else 250 selabel_option[1].value = (char *)1; 251 252 if (policyfile) /* Validate */ 253 selabel_option[2].value = (char *)1; 254 else 255 selabel_option[2].value = NULL; 256 257 hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 3); 258 if (!hnd) { 259 switch (errno) { 260 case EOVERFLOW: 261 fprintf(stderr, "ERROR: Number of specfiles or" 262 " specfile buffer caused an overflow.\n"); 263 break; 264 default: 265 fprintf(stderr, "ERROR: selabel_open: %s\n", 266 strerror(errno)); 267 } 268 exit(-1); 269 } 270 selinux_restorecon_set_sehandle(hnd); 271 } 272 273 if (exclude_list) 274 selinux_restorecon_set_exclude_list 275 ((const char **)exclude_list); 276 277 if (alt_rootpath) 278 selinux_restorecon_set_alt_rootpath(alt_rootpath); 279 280 /* Call restorecon for each path in list */ 281 for (i = optind; i < argc; i++) { 282 if (selinux_restorecon(argv[i], restorecon_flags) < 0) { 283 fprintf(stderr, "ERROR: selinux_restorecon: %s\n", 284 strerror(errno)); 285 exit(-1); 286 } 287 } 288 289 if (exclude_list) { 290 for (i = 0; exclude_list[i]; i++) 291 free(exclude_list[i]); 292 free(exclude_list); 293 } 294 295 if (hnd) 296 selabel_close(hnd); 297 298 return 0; 299} 300