Blame SOURCES/0014-Fix-sbd-common-avoid-statting-potential-links.patch

6f8c14
From 5b4c866f7c0b4ef8061e131a1ee0d1c608d35054 Mon Sep 17 00:00:00 2001
6f8c14
From: Klaus Wenninger <klaus.wenninger@aon.at>
6f8c14
Date: Wed, 19 Sep 2018 16:15:27 +0200
6f8c14
Subject: [PATCH] Fix: sbd-common: avoid statting potential links
6f8c14
6f8c14
These potential links might be anything and statting - if just
6f8c14
allowed to stat chr-nodes (e.g. SELinux) - them would lead
6f8c14
to avc-logs in the SELinux case.
6f8c14
---
6f8c14
 src/sbd-common.c | 133 +++++++++++++++++++++++++++++++++++++++----------------
6f8c14
 1 file changed, 96 insertions(+), 37 deletions(-)
6f8c14
6f8c14
diff --git a/src/sbd-common.c b/src/sbd-common.c
6f8c14
index 679f946..cc84cd0 100644
6f8c14
--- a/src/sbd-common.c
6f8c14
+++ b/src/sbd-common.c
6f8c14
@@ -262,6 +262,12 @@ struct watchdog_list_item {
6f8c14
 	struct watchdog_list_item *next;
6f8c14
 };
6f8c14
 
6f8c14
+struct link_list_item {
6f8c14
+	char *dev_node;
6f8c14
+	char *link_name;
6f8c14
+	struct link_list_item *next;
6f8c14
+};
6f8c14
+
6f8c14
 static struct watchdog_list_item *watchdog_list = NULL;
6f8c14
 static int watchdog_list_items = 0;
6f8c14
 
6f8c14
@@ -275,6 +281,7 @@ watchdog_populate_list(void)
6f8c14
 	char entry_name[280];
6f8c14
 	DIR *dp;
6f8c14
 	char buf[280] = "";
6f8c14
+	struct link_list_item *link_list = NULL;
6f8c14
 
6f8c14
 	if (watchdog_list != NULL) {
6f8c14
 		return;
6f8c14
@@ -288,7 +295,7 @@ watchdog_populate_list(void)
6f8c14
 				FILE *file;
6f8c14
 
6f8c14
 				snprintf(entry_name, sizeof(entry_name),
6f8c14
-						SYS_CLASS_WATCHDOG "/%s/dev", entry->d_name);
6f8c14
+				         SYS_CLASS_WATCHDOG "/%s/dev", entry->d_name);
6f8c14
 				file = fopen(entry_name, "r");
6f8c14
 				if (file) {
6f8c14
 					int major, minor;
6f8c14
@@ -309,43 +316,59 @@ watchdog_populate_list(void)
6f8c14
 	/* search for watchdog nodes in /dev */
6f8c14
 	dp = opendir(WATCHDOG_NODEDIR);
6f8c14
 	if (dp) {
6f8c14
+		/* first go for links and memorize them */
6f8c14
 		while ((entry = readdir(dp))) {
6f8c14
-			if ((entry->d_type == DT_CHR) || (entry->d_type == DT_LNK)) {
6f8c14
-				struct stat statbuf;
6f8c14
+			if (entry->d_type == DT_LNK) {
6f8c14
+				int len;
6f8c14
 
6f8c14
 				snprintf(entry_name, sizeof(entry_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
+				         WATCHDOG_NODEDIR "%s", entry->d_name);
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
+					continue;
6f8c14
+				} else {
6f8c14
+					/* just memorize to avoid statting the target - SELinux */
6f8c14
+					struct link_list_item *lli =
6f8c14
+						calloc(1, sizeof(struct link_list_item));
6f8c14
+
6f8c14
+					lli->dev_node = strdup(buf);
6f8c14
+					lli->link_name = strdup(entry_name);
6f8c14
+					lli->next = link_list;
6f8c14
+					link_list = lli;
6f8c14
 				}
6f8c14
+			}
6f8c14
+		}
6f8c14
+
6f8c14
+		rewinddir(dp);
6f8c14
+
6f8c14
+		while ((entry = readdir(dp))) {
6f8c14
+			if (entry->d_type == DT_CHR) {
6f8c14
+				struct stat statbuf;
6f8c14
+
6f8c14
+				snprintf(entry_name, sizeof(entry_name),
6f8c14
+				         WATCHDOG_NODEDIR "%s", entry->d_name);
6f8c14
 				if(!stat(entry_name, &statbuf) && S_ISCHR(statbuf.st_mode)) {
6f8c14
 					int i;
6f8c14
 
6f8c14
@@ -353,8 +376,9 @@ watchdog_populate_list(void)
6f8c14
 						if (statbuf.st_rdev == watchdogs[i]) {
6f8c14
 							int wdfd = watchdog_init_fd(entry_name, -1);
6f8c14
 							struct watchdog_list_item *wdg =
6f8c14
-									calloc(1, sizeof(struct watchdog_list_item));
6f8c14
+								calloc(1, sizeof(struct watchdog_list_item));
6f8c14
 							int len;
6f8c14
+							struct link_list_item *tmp_list = NULL;
6f8c14
 
6f8c14
 							wdg->dev = watchdogs[i];
6f8c14
 							wdg->dev_node = strdup(entry_name);
6f8c14
@@ -374,8 +398,8 @@ watchdog_populate_list(void)
6f8c14
 							}
6f8c14
 
6f8c14
 							snprintf(entry_name, sizeof(entry_name),
6f8c14
-								SYS_CHAR_DEV_DIR "/%d:%d/device/driver",
6f8c14
-								major(watchdogs[i]), minor(watchdogs[i]));
6f8c14
+							         SYS_CHAR_DEV_DIR "/%d:%d/device/driver",
6f8c14
+							         major(watchdogs[i]), minor(watchdogs[i]));
6f8c14
 							len = readlink(entry_name, buf, sizeof(buf) - 1);
6f8c14
 							if (len > 0) {
6f8c14
 								buf[len] = '\0';
6f8c14
@@ -385,14 +409,49 @@ watchdog_populate_list(void)
6f8c14
 							                   "Software Watchdog") == 0)) {
6f8c14
 								wdg->dev_driver = strdup("softdog");
6f8c14
 							}
6f8c14
+
6f8c14
+							/* create dupes if we have memorized links
6f8c14
+							 * to this node
6f8c14
+							 */
6f8c14
+							for (tmp_list = link_list; tmp_list;
6f8c14
+							     tmp_list = tmp_list->next) {
6f8c14
+								if (!strcmp(tmp_list->dev_node,
6f8c14
+								            wdg->dev_node)) {
6f8c14
+									struct watchdog_list_item *dupe_wdg =
6f8c14
+										calloc(1, sizeof(struct watchdog_list_item));
6f8c14
+
6f8c14
+									/* as long as we never purge watchdog_list
6f8c14
+									 * there is no need to dupe strings
6f8c14
+									 */
6f8c14
+									*dupe_wdg = *wdg;
6f8c14
+									dupe_wdg->dev_node = strdup(tmp_list->link_name);
6f8c14
+									dupe_wdg->next = watchdog_list;
6f8c14
+									watchdog_list = dupe_wdg;
6f8c14
+									watchdog_list_items++;
6f8c14
+								}
6f8c14
+								/* for performance reasons we could remove
6f8c14
+								 * the link_list entry
6f8c14
+								 */
6f8c14
+							}
6f8c14
 							break;
6f8c14
 						}
6f8c14
 					}
6f8c14
 				}
6f8c14
 			}
6f8c14
 		}
6f8c14
+
6f8c14
 		closedir(dp);
6f8c14
 	}
6f8c14
+
6f8c14
+	/* cleanup link list */
6f8c14
+	while (link_list) {
6f8c14
+		struct link_list_item *tmp_list = link_list;
6f8c14
+
6f8c14
+		link_list = link_list->next;
6f8c14
+		free(tmp_list->dev_node);
6f8c14
+		free(tmp_list->link_name);
6f8c14
+		free(tmp_list);
6f8c14
+	}
6f8c14
 }
6f8c14
 
6f8c14
 int watchdog_info(void)
6f8c14
-- 
6f8c14
1.8.3.1
6f8c14