diff mbox series

[RFC,v2,10/27] libselinux: introduce selabel_nuke

Message ID 20230814132025.45364-11-cgzones@googlemail.com (mailing list archive)
State New, archived
Delegated to: Petr Lautrbach
Headers show
Series libselinux: rework selabel_file(5) database | expand

Commit Message

Christian Göttsche Aug. 14, 2023, 1:20 p.m. UTC
Introduce a helper to remove SELinux file 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>
---
 libselinux/utils/.gitignore     |   1 +
 libselinux/utils/selabel_nuke.c | 134 ++++++++++++++++++++++++++++++++
 2 files changed, 135 insertions(+)
 create mode 100644 libselinux/utils/selabel_nuke.c

Comments

James Carter Oct. 4, 2023, 9:01 p.m. UTC | #1
On Mon, Aug 14, 2023 at 9:41 AM Christian Göttsche
<cgzones@googlemail.com> wrote:
>
> Introduce a helper to remove SELinux file 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>
> ---
>  libselinux/utils/.gitignore     |   1 +
>  libselinux/utils/selabel_nuke.c | 134 ++++++++++++++++++++++++++++++++
>  2 files changed, 135 insertions(+)
>  create mode 100644 libselinux/utils/selabel_nuke.c
>
> diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
> index b3311360..a92e1e94 100644
> --- a/libselinux/utils/.gitignore
> +++ b/libselinux/utils/.gitignore
> @@ -20,6 +20,7 @@ selabel_digest
>  selabel_get_digests_all_partial_matches
>  selabel_lookup
>  selabel_lookup_best_match
> +selabel_nuke

This is not a good name and I am not sure that it should have a
"selabel" prefix. It doesn't use any selabel stuff.

It seems like this should be in policycoreutils maybe with the name
"remove_filecons".

Jim


>  selabel_partial_match
>  selinux_check_securetty_context
>  selinuxenabled
> diff --git a/libselinux/utils/selabel_nuke.c b/libselinux/utils/selabel_nuke.c
> new file mode 100644
> index 00000000..b6a2df66
> --- /dev/null
> +++ b/libselinux/utils/selabel_nuke.c
> @@ -0,0 +1,134 @@
> +#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/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 [-nrv] <path>\n", progname);
> +}
> +
> +static void nuke(int atfd, const char *path, const char *fullpath, bool dry_run, bool recursive, bool verbose)
> +{
> +       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;
> +       }
> +
> +       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;
> +               }
> +
> +               nuke(dirfd(dir), entry->d_name, nextfullpath, dry_run, recursive, verbose);
> +
> +               free(nextfullpath);
> +       }
> +
> +       closedir(dir);
> +}
> +
> +
> +int main(int argc, char *argv[])
> +{
> +       bool dry_run = false, recursive = false, verbose = false;
> +       int c;
> +
> +       while ((c = getopt(argc, argv, "nrv")) != -1) {
> +               switch (c) {
> +               case 'n':
> +                       dry_run = true;
> +                       break;
> +               case 'r':
> +                       recursive = true;
> +                       break;
> +               case 'v':
> +                       verbose = 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++)
> +               nuke(AT_FDCWD, argv[index], argv[index], dry_run, recursive, verbose);
> +
> +       return EXIT_SUCCESS;
> +}
> --
> 2.40.1
>
diff mbox series

Patch

diff --git a/libselinux/utils/.gitignore b/libselinux/utils/.gitignore
index b3311360..a92e1e94 100644
--- a/libselinux/utils/.gitignore
+++ b/libselinux/utils/.gitignore
@@ -20,6 +20,7 @@  selabel_digest
 selabel_get_digests_all_partial_matches
 selabel_lookup
 selabel_lookup_best_match
+selabel_nuke
 selabel_partial_match
 selinux_check_securetty_context
 selinuxenabled
diff --git a/libselinux/utils/selabel_nuke.c b/libselinux/utils/selabel_nuke.c
new file mode 100644
index 00000000..b6a2df66
--- /dev/null
+++ b/libselinux/utils/selabel_nuke.c
@@ -0,0 +1,134 @@ 
+#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/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 [-nrv] <path>\n", progname);
+}
+
+static void nuke(int atfd, const char *path, const char *fullpath, bool dry_run, bool recursive, bool verbose)
+{
+	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;
+	}
+
+	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;
+		}
+
+		nuke(dirfd(dir), entry->d_name, nextfullpath, dry_run, recursive, verbose);
+
+		free(nextfullpath);
+	}
+
+	closedir(dir);
+}
+
+
+int main(int argc, char *argv[])
+{
+	bool dry_run = false, recursive = false, verbose = false;
+	int c;
+
+	while ((c = getopt(argc, argv, "nrv")) != -1) {
+		switch (c) {
+		case 'n':
+			dry_run = true;
+			break;
+		case 'r':
+			recursive = true;
+			break;
+		case 'v':
+			verbose = 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++)
+		nuke(AT_FDCWD, argv[index], argv[index], dry_run, recursive, verbose);
+
+	return EXIT_SUCCESS;
+}