policycoreutils: introduce unsetfiles
Introduce a helper to remove SELinux file security contexts. Mainly for testing label operations, and only for SELinux disabled systems, since removing file contexts is not supported by SELinux. Signed-off-by: Christian Göttsche <cgzones@googlemail.com> Acked-by: James Carter <jwcart2@gmail.com>
This commit is contained in:
parent
be11f48b7a
commit
0faf3433e8
|
@ -9,4 +9,5 @@ setfiles/restorecon
|
|||
setfiles/restorecon_xattr
|
||||
setfiles/setfiles
|
||||
setsebool/setsebool
|
||||
unsetfiles/unsetfiles
|
||||
hll/pp/pp
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
SUBDIRS = setfiles load_policy newrole run_init secon sestatus semodule setsebool scripts po man hll
|
||||
SUBDIRS = setfiles load_policy newrole run_init secon sestatus semodule setsebool scripts po man hll unsetfiles
|
||||
|
||||
all install relabel clean indent:
|
||||
@for subdir in $(SUBDIRS); do \
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
PREFIX ?= /usr
|
||||
SBINDIR ?= $(PREFIX)/sbin
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
|
||||
override CFLAGS += -D_GNU_SOURCE
|
||||
override LDLIBS += -lselinux
|
||||
|
||||
|
||||
all: unsetfiles
|
||||
|
||||
unsetfiles: unsetfiles.o
|
||||
|
||||
install: all
|
||||
test -d $(DESTDIR)$(SBINDIR) || install -m 755 -d $(DESTDIR)$(SBINDIR)
|
||||
test -d $(DESTDIR)$(MANDIR)/man1 || install -m 755 -d $(DESTDIR)$(MANDIR)/man1
|
||||
install -m 755 unsetfiles $(DESTDIR)$(SBINDIR)
|
||||
install -m 644 unsetfiles.1 $(DESTDIR)$(MANDIR)/man1/
|
||||
|
||||
clean:
|
||||
-rm -f unsetfiles *.o
|
||||
|
||||
indent:
|
||||
../../scripts/Lindent $(wildcard *.[ch])
|
||||
|
||||
relabel: install
|
||||
/sbin/restorecon $(DESTDIR)$(SBINDIR)/unsetfiles
|
|
@ -0,0 +1,46 @@
|
|||
.TH UNSETFILES "1" "December 2023" "Security Enhanced Linux"
|
||||
.SH NAME
|
||||
unsetfiles \- Remove SELinux file security contexts.
|
||||
.SH SYNOPSIS
|
||||
.B unsetfiles
|
||||
.RB [ \-hnrvx ]
|
||||
.IR pathname \ ...
|
||||
|
||||
.SH DESCRIPTION
|
||||
.P
|
||||
This program removes the SELinux file security contexts of files. It can help
|
||||
cleaning extended file attributes after disabling SELinux.
|
||||
.P
|
||||
.B unsetfiles
|
||||
will only work on SELinux disabled systems, since removing file security
|
||||
contexts is not supported by SELinux.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-h
|
||||
Show usage information and exit.
|
||||
.TP
|
||||
.B \-n
|
||||
Do not actually remove any SELinux file security contexts.
|
||||
.TP
|
||||
.B \-r
|
||||
Remove SELinux file security contexts recursive.
|
||||
.TP
|
||||
.B \-v
|
||||
Be verbose about performed actions.
|
||||
.TP
|
||||
.B \-x
|
||||
Do not cross filesystem boundaries.
|
||||
|
||||
.SH ARGUMENTS
|
||||
.TP
|
||||
.IR pathname \ ...
|
||||
One or more path names to operate on.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR restorecon (8),
|
||||
.BR setfiles (8)
|
||||
|
||||
.SH AUTHORS
|
||||
.nf
|
||||
Christian Göttsche (cgzones@googlemail.com)
|
|
@ -0,0 +1,183 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/magic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <selinux/selinux.h>
|
||||
|
||||
|
||||
#define XATTR_NAME_SELINUX "security.selinux"
|
||||
|
||||
|
||||
static void usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [-nrvx] <path>\n\n"
|
||||
"Options:\n"
|
||||
"\t-n\tdon't remove any file labels\n"
|
||||
"\t-r\tremove labels recursive\n"
|
||||
"\t-v\tbe verbose\n"
|
||||
"\t-x\tdo not cross filesystem boundaries\n",
|
||||
progname);
|
||||
}
|
||||
|
||||
static void unset(int atfd, const char *path, const char *fullpath,
|
||||
bool dry_run, bool recursive, bool verbose,
|
||||
dev_t root_dev)
|
||||
{
|
||||
ssize_t ret;
|
||||
int fd, rc;
|
||||
DIR *dir;
|
||||
|
||||
ret = lgetxattr(fullpath, XATTR_NAME_SELINUX, NULL, 0);
|
||||
if (ret <= 0) {
|
||||
if (errno != ENODATA && errno != ENOTSUP)
|
||||
fprintf(stderr, "Failed to get SELinux label of %s: %m\n", fullpath);
|
||||
else if (verbose)
|
||||
printf("Failed to get SELinux label of %s: %m\n", fullpath);
|
||||
} else {
|
||||
if (dry_run) {
|
||||
printf("Would remove SELinux label of %s\n", fullpath);
|
||||
} else {
|
||||
if (verbose)
|
||||
printf("Removing label of %s\n", fullpath);
|
||||
|
||||
rc = lremovexattr(fullpath, XATTR_NAME_SELINUX);
|
||||
if (rc < 0)
|
||||
fprintf(stderr, "Failed to remove SELinux label of %s: %m\n", fullpath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!recursive)
|
||||
return;
|
||||
|
||||
fd = openat(atfd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (errno != ENOTDIR)
|
||||
fprintf(stderr, "Failed to open %s: %m\n", fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root_dev != (dev_t)-1) {
|
||||
struct stat sb;
|
||||
|
||||
rc = fstat(fd, &sb);
|
||||
if (rc == -1) {
|
||||
fprintf(stderr, "Failed to stat directory %s: %m\n", fullpath);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sb.st_dev != root_dev) {
|
||||
if (verbose)
|
||||
printf("Skipping directory %s due to filesystem boundary\n", fullpath);
|
||||
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dir = fdopendir(fd);
|
||||
if (!dir) {
|
||||
fprintf(stderr, "Failed to open directory %s: %m\n", fullpath);
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const struct dirent *entry;
|
||||
char *nextfullpath;
|
||||
|
||||
errno = 0;
|
||||
entry = readdir(dir);
|
||||
if (!entry) {
|
||||
if (errno)
|
||||
fprintf(stderr, "Failed to iterate directory %s: %m\n", fullpath);
|
||||
break;
|
||||
}
|
||||
|
||||
if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' || (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
|
||||
continue;
|
||||
|
||||
rc = asprintf(&nextfullpath, "%s/%s", strcmp(fullpath, "/") == 0 ? "" : fullpath, entry->d_name);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Out of memory!\n");
|
||||
closedir(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
unset(dirfd(dir), entry->d_name, nextfullpath, dry_run, recursive, verbose, root_dev);
|
||||
|
||||
free(nextfullpath);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
bool dry_run = false, recursive = false, verbose = false, same_dev = false;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "hnrvx")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
case 'n':
|
||||
dry_run = true;
|
||||
break;
|
||||
case 'r':
|
||||
recursive = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'x':
|
||||
same_dev = true;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (is_selinux_enabled()) {
|
||||
fprintf(stderr, "Removing SELinux attributes on a SELinux enabled system is not supported!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (int index = optind; index < argc; index++) {
|
||||
dev_t root_dev = (dev_t)-1;
|
||||
|
||||
if (same_dev) {
|
||||
struct stat sb;
|
||||
int rc;
|
||||
|
||||
rc = stat(argv[index], &sb);
|
||||
if (rc == -1) {
|
||||
fprintf(stderr, "Failed to stat %s: %m\n", argv[index]);
|
||||
continue;
|
||||
}
|
||||
|
||||
root_dev = sb.st_dev;
|
||||
}
|
||||
unset(AT_FDCWD, argv[index], argv[index], dry_run, recursive, verbose, root_dev);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue