Blame SOURCES/0012-Fix-sbd-common-don-t-follow-symlinks-outside-dev-for.patch

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