#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XATTR_NAME_SELINUX "security.selinux" static void usage(const char *progname) { fprintf(stderr, "usage: %s [-nrvx] \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; }