Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libselinux: rework selabel_file(5) database #406

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Next Next commit
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 <[email protected]>
---
v2:
   move from libselinux/utils to policycoreutils and rename
  • Loading branch information
cgzones committed Mar 11, 2024
commit f50988a968b1fac1c6b6a02a559740a723dc6af1
1 change: 1 addition & 0 deletions policycoreutils/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ setfiles/restorecon
setfiles/restorecon_xattr
setfiles/setfiles
setsebool/setsebool
unsetfiles/unsetfiles
hll/pp/pp
2 changes: 1 addition & 1 deletion policycoreutils/Makefile
Original file line number Diff line number Diff line change
@@ -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 \
Expand Down
26 changes: 26 additions & 0 deletions policycoreutils/unsetfiles/Makefile
Original file line number Diff line number Diff line change
@@ -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
46 changes: 46 additions & 0 deletions policycoreutils/unsetfiles/unsetfiles.1
Original file line number Diff line number Diff line change
@@ -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 ([email protected])
183 changes: 183 additions & 0 deletions policycoreutils/unsetfiles/unsetfiles.c
Original file line number Diff line number Diff line change
@@ -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_ | 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;
}