selinux/libselinux/utils/selinux_restorecon.c

300 lines
7.5 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <stdbool.h>
#include <sepol/sepol.h>
#include <selinux/label.h>
#include <selinux/restorecon.h>
static char *policyfile;
static char **exclude_list;
static int exclude_count;
static int validate_context(char **contextp)
{
char *context = *contextp, *tmpcon;
if (policyfile) {
if (sepol_check_context(context) < 0) {
fprintf(stderr, "Invalid context %s\n", context);
exit(-1);
}
} else if (security_canonicalize_context_raw(context, &tmpcon) == 0) {
free(context);
*contextp = tmpcon;
} else if (errno != ENOENT) {
fprintf(stderr, "Validate context error: %s\n",
strerror(errno));
exit(-1);
}
return 0;
}
static void usage(const char *progname)
{
fprintf(stderr,
"\nusage: %s [-FCnRrdmiIaAsl] [-e dir] [-v|-P]\n"
"[-x alt_rootpath] [-p policy] [-f specfile] pathname ...\n"
"\nWhere:\n\t"
"-F Set the label to that in specfile.\n\t"
" If not set then reset the \"type\" component of the "
"label to that\n\t in the specfile.\n\t"
"-C Check labels even if the stored SHA1 digest matches\n\t"
" the specfiles SHA1 digest.\n\t"
"-n Don't change any file labels (passive check).\n\t"
"-R Recursively change file and directory labels.\n\t"
"-v Show changes in file labels (-v and -P are mutually "
" exclusive).\n\t"
"-P Show progress by printing \"*\" to stdout every 1000 files"
",\n\t unless relabeling entire OS, then show percentage complete.\n\t"
"-r Use realpath(3) to convert pathnames to canonical form.\n\t"
"-d Prevent descending into directories that have a "
"different\n\t device number than the pathname from which "
"the descent began.\n\t"
"-m Do not automatically read /proc/mounts to determine what\n\t"
" non-seclabel mounts to exclude from relabeling.\n\t"
"-e Exclude this directory (add multiple -e entries).\n\t"
"-i Do not set SELABEL_OPT_DIGEST option when calling "
" selabel_open(3).\n\t"
"-I Ignore files that do not exist.\n\t"
"-a Add an association between an inode and a context.\n\t"
" If there is a different context that matched the inode,\n\t"
" then use the first context that matched.\n\t"
"-A Abort on errors during the file tree walk.\n\t"
"-s Log any label changes to syslog(3).\n\t"
"-l Log what specfile context matched each file.\n\t"
"-x Set alternate rootpath.\n\t"
"-p Optional binary policy file (also sets validate context "
"option).\n\t"
"-f Optional file contexts file.\n\t"
"pathname One or more paths to relabel.\n\n",
progname);
exit(-1);
}
static void add_exclude(const char *directory)
{
char **tmp_list;
if (directory == NULL || directory[0] != '/') {
fprintf(stderr, "Full path required for exclude: %s.\n",
directory);
exit(-1);
}
/* Add another two entries, one for directory, and the other to
* terminate the list */
tmp_list = realloc(exclude_list, sizeof(char *) * (exclude_count + 2));
if (!tmp_list) {
fprintf(stderr, "ERROR: realloc failed.\n");
exit(-1);
}
exclude_list = tmp_list;
exclude_list[exclude_count] = strdup(directory);
if (!exclude_list[exclude_count]) {
fprintf(stderr, "ERROR: strdup failed.\n");
exit(-1);
}
exclude_count++;
exclude_list[exclude_count] = NULL;
}
int main(int argc, char **argv)
{
int opt, i;
unsigned int restorecon_flags = 0;
char *path = NULL, *digest = NULL, *validate = NULL;
char *alt_rootpath = NULL;
FILE *policystream;
bool ignore_digest = false, require_selinux = true;
bool verbose = false, progress = false;
struct selabel_handle *hnd = NULL;
struct selinux_opt selabel_option[] = {
{ SELABEL_OPT_PATH, path },
{ SELABEL_OPT_DIGEST, digest },
{ SELABEL_OPT_VALIDATE, validate }
};
if (argc < 2)
usage(argv[0]);
exclude_list = NULL;
exclude_count = 0;
while ((opt = getopt(argc, argv, "iIFCnRvPrdaAslme:f:p:x:")) > 0) {
switch (opt) {
case 'F':
restorecon_flags |=
SELINUX_RESTORECON_SET_SPECFILE_CTX;
break;
case 'C':
restorecon_flags |=
SELINUX_RESTORECON_IGNORE_DIGEST;
break;
case 'n':
restorecon_flags |= SELINUX_RESTORECON_NOCHANGE;
break;
case 'R':
restorecon_flags |= SELINUX_RESTORECON_RECURSE;
break;
case 'v':
if (progress) {
fprintf(stderr,
"Progress and Verbose are mutually exclusive\n");
exit(-1);
}
verbose = true;
restorecon_flags |= SELINUX_RESTORECON_VERBOSE;
break;
case 'P':
if (verbose) {
fprintf(stderr,
"Progress and Verbose are mutually exclusive\n");
exit(-1);
}
progress = true;
restorecon_flags |= SELINUX_RESTORECON_PROGRESS;
break;
case 'r':
restorecon_flags |= SELINUX_RESTORECON_REALPATH;
break;
case 'd':
restorecon_flags |= SELINUX_RESTORECON_XDEV;
break;
case 'm':
restorecon_flags |= SELINUX_RESTORECON_IGNORE_MOUNTS;
break;
case 'e':
add_exclude(optarg);
break;
case 'p':
policyfile = optarg;
policystream = fopen(policyfile, "r");
if (!policystream) {
fprintf(stderr,
"ERROR: opening %s: %s\n",
policyfile, strerror(errno));
exit(-1);
}
if (sepol_set_policydb_from_file(policystream) < 0) {
fprintf(stderr,
"ERROR: reading policy %s: %s\n",
policyfile, strerror(errno));
exit(-1);
}
fclose(policystream);
selinux_set_callback(SELINUX_CB_VALIDATE,
(union selinux_callback)&validate_context);
require_selinux = false;
break;
case 'f':
path = optarg;
break;
case 'i':
ignore_digest = true;
break;
case 'I':
restorecon_flags |= SELINUX_RESTORECON_IGNORE_NOENTRY;
break;
case 'a':
restorecon_flags |= SELINUX_RESTORECON_ADD_ASSOC;
break;
case 'A':
restorecon_flags |= SELINUX_RESTORECON_ABORT_ON_ERROR;
break;
case 's':
restorecon_flags |= SELINUX_RESTORECON_SYSLOG_CHANGES;
break;
case 'l':
restorecon_flags |= SELINUX_RESTORECON_LOG_MATCHES;
break;
case 'x':
alt_rootpath = optarg;
break;
default:
usage(argv[0]);
}
}
if (require_selinux && (is_selinux_enabled() <= 0)) {
fprintf(stderr,
"SELinux must be enabled to perform this operation.\n");
exit(-1);
}
if (optind >= argc) {
fprintf(stderr, "No pathname specified\n");
exit(-1);
}
/* If any of these set then do our own selabel_open and pass
* handle to selinux_restorecon */
if (ignore_digest || path || policyfile) {
if (path)
selabel_option[0].value = path;
else
selabel_option[0].value = NULL;
if (ignore_digest)
selabel_option[1].value = NULL;
else
selabel_option[1].value = (char *)1;
if (policyfile) /* Validate */
selabel_option[2].value = (char *)1;
else
selabel_option[2].value = NULL;
hnd = selabel_open(SELABEL_CTX_FILE, selabel_option, 3);
if (!hnd) {
switch (errno) {
case EOVERFLOW:
fprintf(stderr, "ERROR: Number of specfiles or"
" specfile buffer caused an overflow.\n");
break;
default:
fprintf(stderr, "ERROR: selabel_open: %s\n",
strerror(errno));
}
exit(-1);
}
selinux_restorecon_set_sehandle(hnd);
}
if (exclude_list)
selinux_restorecon_set_exclude_list
((const char **)exclude_list);
if (alt_rootpath)
selinux_restorecon_set_alt_rootpath(alt_rootpath);
/* Call restorecon for each path in list */
for (i = optind; i < argc; i++) {
if (selinux_restorecon(argv[i], restorecon_flags) < 0) {
fprintf(stderr, "ERROR: selinux_restorecon: %s\n",
strerror(errno));
exit(-1);
}
}
if (exclude_list) {
for (i = 0; exclude_list[i]; i++)
free(exclude_list[i]);
free(exclude_list);
}
if (hnd)
selabel_close(hnd);
return 0;
}