summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libudev/libudev-private.h3
-rw-r--r--libudev/libudev-selinux-private.c28
-rw-r--r--udev/udevd.c149
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) {