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