diff options
-rw-r--r-- | libudev/libudev-private.h | 3 | ||||
-rw-r--r-- | libudev/libudev-selinux-private.c | 28 | ||||
-rw-r--r-- | udev/udevd.c | 149 |
3 files changed, 156 insertions, 24 deletions
diff --git a/libudev/libudev-private.h b/libudev/libudev-private.h index 3758c5b1b4..fa9722360b 100644 --- a/libudev/libudev-private.h +++ b/libudev/libudev-private.h @@ -227,12 +227,15 @@ static inline void udev_selinux_init(struct udev *udev) {} static inline void udev_selinux_exit(struct udev *udev) {} static inline void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode) {} static inline void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode) {} +static inline void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode) {} static inline void udev_selinux_resetfscreatecon(struct udev *udev) {} #else void udev_selinux_init(struct udev *udev); void udev_selinux_exit(struct udev *udev); void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int mode); void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned int mode); +void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode); void udev_selinux_resetfscreatecon(struct udev *udev); #endif + #endif diff --git a/libudev/libudev-selinux-private.c b/libudev/libudev-selinux-private.c index 84f8b6a63f..2d4463d864 100644 --- a/libudev/libudev-selinux-private.c +++ b/libudev/libudev-selinux-private.c @@ -53,7 +53,7 @@ void udev_selinux_lsetfilecon(struct udev *udev, const char *file, unsigned int if (matchpathcon(file, mode, &scontext) < 0) { err(udev, "matchpathcon(%s) failed\n", file); return; - } + } if (lsetfilecon(file, scontext) < 0) err(udev, "setfilecon %s failed: %m\n", file); freecon(scontext); @@ -65,6 +65,7 @@ void udev_selinux_setfscreatecon(struct udev *udev, const char *file, unsigned i if (!selinux_enabled) return; + if (matchpathcon(file, mode, &scontext) < 0) { err(udev, "matchpathcon(%s) failed\n", file); return; @@ -81,3 +82,28 @@ void udev_selinux_resetfscreatecon(struct udev *udev) if (setfscreatecon(selinux_prev_scontext) < 0) err(udev, "setfscreatecon failed: %m\n"); } + +void udev_selinux_setfscreateconat(struct udev *udev, int dirfd, const char *file, unsigned int mode) +{ + char filename[UTIL_PATH_SIZE]; + + if (!selinux_enabled) + return; + + /* resolve relative filename */ + if (file[0] != '/') { + char procfd[UTIL_PATH_SIZE]; + char target[UTIL_PATH_SIZE]; + ssize_t len; + + snprintf(procfd, sizeof(procfd), "/proc/%u/fd/%u", getpid(), dirfd); + len = readlink(procfd, target, sizeof(target)); + if (len <= 0 || len == sizeof(target)) + return; + target[len] = '\0'; + + util_strscpyl(filename, sizeof(filename), target, "/", file, NULL); + file = filename; + } + udev_selinux_setfscreatecon(udev, file, mode); +} diff --git a/udev/udevd.c b/udev/udevd.c index 77a14df7b0..1bde8f4a74 100644 --- a/udev/udevd.c +++ b/udev/udevd.c @@ -449,29 +449,6 @@ static void worker_kill(struct udev *udev, int retain) } } -static int mem_size_mb(void) -{ - FILE *f; - char buf[4096]; - long int memsize = -1; - - f = fopen("/proc/meminfo", "r"); - if (f == NULL) - return -1; - - while (fgets(buf, sizeof(buf), f) != NULL) { - long int value; - - if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { - memsize = value / 1024; - break; - } - } - - fclose(f); - return memsize; -} - /* lookup event for identical, parent, child device */ static bool is_devpath_busy(struct event *event) { @@ -768,6 +745,130 @@ static void handle_signal(struct udev *udev, int signo) } } +static int copy_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) +{ + struct dirent *dent; + + for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { + struct stat stats; + + if (dent->d_name[0] == '.') + continue; + if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) + continue; + + if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); + if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { + fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); + fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); + } else { + utimensat(dirfd(dir_to), dent->d_name, NULL, 0); + } + udev_selinux_resetfscreatecon(udev); + } else if (S_ISLNK(stats.st_mode)) { + char target[UTIL_PATH_SIZE]; + ssize_t len; + + len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); + if (len <= 0 || len == (ssize_t)sizeof(target)) + continue; + target[len] = '\0'; + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); + if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) + utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); + udev_selinux_resetfscreatecon(udev); + } else if (S_ISDIR(stats.st_mode)) { + DIR *dir2_from, *dir2_to; + + if (maxdepth == 0) + continue; + + udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); + mkdirat(dirfd(dir_to), dent->d_name, 0755); + udev_selinux_resetfscreatecon(udev); + + dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2_to == NULL) + continue; + + dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); + if (dir2_from == NULL) { + closedir(dir2_to); + continue; + } + + copy_dir(udev, dir2_from, dir2_to, maxdepth-1); + + closedir(dir2_to); + closedir(dir2_from); + } + } + + return 0; +} + +static void prepare_dev(struct udev *udev) +{ + struct stdlinks { + const char *link; + const char *target; + }; + static const struct stdlinks stdlinks[] = { + { "core", "/proc/kcore" }, + { "fd", "/proc/fd" }, + { "stdin", "/proc/self/fd/0" }, + { "stdout", "/proc/self/fd/1" }, + { "stderr", "/proc/self/fd/2" }, + }; + unsigned int i; + DIR *dir_from, *dir_to; + + dir_to = opendir(udev_get_dev_path(udev)); + if (dir_to == NULL) + return; + + /* create standard symlinks to /proc */ + for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { + udev_selinux_setfscreateconat(udev, dirfd(dir_to), stdlinks[i].link, S_IFLNK); + if (symlinkat(stdlinks[i].target, dirfd(dir_to), stdlinks[i].link) < 0 && errno == EEXIST) + utimensat(dirfd(dir_to), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); + udev_selinux_resetfscreatecon(udev); + } + + /* copy content from /lib/udev/devices to /dev */ + dir_from = opendir(LIBEXECDIR "/devices"); + if (dir_from != NULL) { + copy_dir(udev, dir_from, dir_to, 8); + closedir(dir_from); + } + + closedir(dir_to); +} + +static int mem_size_mb(void) +{ + FILE *f; + char buf[4096]; + long int memsize = -1; + + f = fopen("/proc/meminfo", "r"); + if (f == NULL) + return -1; + + while (fgets(buf, sizeof(buf), f) != NULL) { + long int value; + + if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { + memsize = value / 1024; + break; + } + } + + fclose(f); + return memsize; +} + int main(int argc, char *argv[]) { struct udev *udev; @@ -858,6 +959,8 @@ int main(int argc, char *argv[]) if (write(STDERR_FILENO, 0, 0) < 0) dup2(fd, STDERR_FILENO); + prepare_dev(udev); + /* init control socket, bind() ensures, that only one udevd instance is running */ udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); if (udev_ctrl == NULL) { |