sbd/SOURCES/0012-Fix-sbd-common-don-t-follow-symlinks-outside-dev-for.patch
2021-09-10 04:15:58 +00:00

97 lines
3.3 KiB
Diff

From 5d52fa8c3c903df4be0e4e954fbca9b3b15285c6 Mon Sep 17 00:00:00 2001
From: Klaus Wenninger <klaus.wenninger@aon.at>
Date: Fri, 14 Sep 2018 17:51:50 +0200
Subject: [PATCH] Fix: sbd-common: don't follow symlinks outside /dev for
watchdog
This makes it easier to define a SELinux-policy that keeps
avc-log clean on /dev traversal triggered by query-watchdog.
---
src/sbd-common.c | 42 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/src/sbd-common.c b/src/sbd-common.c
index 0ce6478..fcb7a31 100644
--- a/src/sbd-common.c
+++ b/src/sbd-common.c
@@ -251,7 +251,8 @@ watchdog_close(bool disarm)
#define MAX_WATCHDOGS 64
#define SYS_CLASS_WATCHDOG "/sys/class/watchdog"
#define SYS_CHAR_DEV_DIR "/sys/dev/char"
-#define WATCHDOG_NODEDIR "/dev"
+#define WATCHDOG_NODEDIR "/dev/"
+#define WATCHDOG_NODEDIR_LEN 5
struct watchdog_list_item {
dev_t dev;
@@ -273,7 +274,7 @@ watchdog_populate_list(void)
struct dirent *entry;
char entry_name[280];
DIR *dp;
- char buf[256] = "";
+ char buf[280] = "";
if (watchdog_list != NULL) {
return;
@@ -313,7 +314,38 @@ watchdog_populate_list(void)
struct stat statbuf;
snprintf(entry_name, sizeof(entry_name),
- WATCHDOG_NODEDIR "/%s", entry->d_name);
+ WATCHDOG_NODEDIR "%s", entry->d_name);
+ if (entry->d_type == DT_LNK) {
+ int len;
+
+ /* !realpath(entry_name, buf) unfortunately does a stat on
+ * target so we can't really use it to check if links stay
+ * within /dev without triggering e.g. AVC-logs (with
+ * SELinux policy that just allows stat within /dev).
+ * Without canonicalization that doesn't actually touch the
+ * filesystem easily available introduce some limitations
+ * for simplicity:
+ * - just simple path without '..'
+ * - just one level of symlinks (avoid e.g. loop-checking)
+ */
+ len = readlink(entry_name, buf, sizeof(buf) - 1);
+ if ((len < 1) ||
+ (len > sizeof(buf) - WATCHDOG_NODEDIR_LEN - 1)) {
+ continue;
+ }
+ buf[len] = '\0';
+ if (buf[0] != '/') {
+ memmove(&buf[WATCHDOG_NODEDIR_LEN], buf, len+1);
+ memcpy(buf, WATCHDOG_NODEDIR, WATCHDOG_NODEDIR_LEN);
+ len += WATCHDOG_NODEDIR_LEN;
+ }
+ if (strstr(buf, "/../") ||
+ strncmp(WATCHDOG_NODEDIR, buf, WATCHDOG_NODEDIR_LEN) ||
+ lstat(buf, &statbuf) ||
+ !S_ISCHR(statbuf.st_mode)) {
+ continue;
+ }
+ }
if(!stat(entry_name, &statbuf) && S_ISCHR(statbuf.st_mode)) {
int i;
@@ -322,6 +354,7 @@ watchdog_populate_list(void)
int wdfd = watchdog_init_fd(entry_name, -1);
struct watchdog_list_item *wdg =
calloc(1, sizeof(struct watchdog_list_item));
+ int len;
wdg->dev = watchdogs[i];
wdg->dev_node = strdup(entry_name);
@@ -343,7 +376,8 @@ watchdog_populate_list(void)
snprintf(entry_name, sizeof(entry_name),
SYS_CHAR_DEV_DIR "/%d:%d/device/driver",
major(watchdogs[i]), minor(watchdogs[i]));
- if (readlink(entry_name, buf, sizeof(buf)) > 0) {
+ if ((len = readlink(entry_name, buf, sizeof(buf) - 1)) > 0) {
+ buf[len] = '\0';
wdg->dev_driver = strdup(basename(buf));
} else if ((wdg->dev_ident) &&
(strcmp(wdg->dev_ident,
--
1.8.3.1