patches/sysklogd: backport fixes

- Allow parenthesis in tag names
- Unescape Linux /dev/kmsg messages for unicode characters

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-11-20 11:59:59 +01:00
parent f88ebc9cde
commit 442b572478
4 changed files with 389 additions and 1 deletions
@@ -1,7 +1,7 @@
From 31dfe0d16461d2852f3fc56cb82aed3a23db022f Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 25 Sep 2025 16:48:31 +0200
Subject: [PATCH] syslogd: fix UTF-8 handling with -8 flag, for RFC5424
Subject: [PATCH 1/4] syslogd: fix UTF-8 handling with -8 flag, for RFC5424
compliance
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
@@ -0,0 +1,78 @@
From f9b6531430c6485af4697a5868a6f43c93d20419 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Fri, 26 Sep 2025 08:42:57 +0200
Subject: [PATCH 2/4] syslogd: fix parentheses handling in RFC 3164 tag parsing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Some applications use tag names like "(polkit-agent)" which sysklogd do
not handle well. This patch aims to address this and also add a bit of
logic to normalize such tags:
- Allow parentheses in tag character set for broader compatibility
- Smart parsing: strip surrounding parentheses from complete tags like
"(polkit-agent):" → "polkit-agent", while preserving partial parentheses
like "app(version):" unchanged
- Maintain RFC compliance: both RFC3164 and RFC5424 allow parentheses
- Preserve all existing functionality and edge case handling
Fixes #104
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
src/syslogd.c | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/src/syslogd.c b/src/syslogd.c
index fa82d98..37e1920 100644
--- a/src/syslogd.c
+++ b/src/syslogd.c
@@ -1309,15 +1309,36 @@ parsemsg_rfc3164_app_name_procid(char **msg, char **app_name, char **procid)
m = *msg;
/* Application name. */
- app_name_begin = m;
- app_name_length = strspn(m,
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "._-/");
+ /* Check if tag is surrounded by parentheses like (polkit-agent) */
+ if (*m == '(') {
+ char *closing = strchr(m + 1, ')');
+ if (closing && (closing[1] == ':' || closing[1] == '[' || isblank(closing[1]) || closing[1] == '\0')) {
+ /* Found complete parenthetical tag, strip parentheses */
+ app_name_begin = m + 1;
+ app_name_length = closing - (m + 1);
+ m = closing + 1;
+ } else {
+ /* Incomplete or malformed, treat normally */
+ app_name_begin = m;
+ app_name_length = strspn(m,
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "._-/()");
+ m += app_name_length;
+ }
+ } else {
+ app_name_begin = m;
+ app_name_length = strspn(m,
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "._-/()");
+ m += app_name_length;
+ }
+
if (app_name_length == 0)
goto bad;
- m += app_name_length;
/* Process identifier (optional). */
if (*m == '[') {
--
2.43.0
@@ -0,0 +1,168 @@
From 10372296093cc70f3d21b46359a4cfbd8312c968 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Fri, 26 Sep 2025 10:07:45 +0200
Subject: [PATCH 3/4] test: verify parenthetical tag parsing
Organization: Wires
Regression test for issue #104.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
test/Makefile.am | 5 +-
test/parens.sh | 119 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 122 insertions(+), 2 deletions(-)
create mode 100755 test/parens.sh
diff --git a/test/Makefile.am b/test/Makefile.am
index 7162e6c..59a38cf 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -3,10 +3,10 @@ EXTRA_DIST += api.sh local.sh unicode.sh remote.sh fwd.sh mark.sh \
memleak.sh facility.sh notify.sh rotate_all.sh secure.sh \
logger.sh listen.sh sighup.sh tag.sh hostname.sh \
property.sh raw.sh regression.sh multicast.sh \
- mcast-fwd.sh mcast-iface.sh
+ mcast-fwd.sh mcast-iface.sh parens.sh
CLEANFILES = *~ *.trs *.log
TEST_EXTENSIONS = .sh
-TESTS_ENVIRONMENT= unshare -mrun
+TESTS_ENVIRONMENT= unshare -mrun --map-auto
check_PROGRAMS = api
api_SOURCES = api.c
@@ -30,6 +30,7 @@ TESTS += rotate_all.sh
TESTS += secure.sh
TESTS += sighup.sh
TESTS += tag.sh
+TESTS += parens.sh
TESTS += hostname.sh
TESTS += property.sh
TESTS += raw.sh
diff --git a/test/parens.sh b/test/parens.sh
new file mode 100755
index 0000000..43b8744
--- /dev/null
+++ b/test/parens.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+# Verify parentheses handling in log tags for RFC3164 messages.
+# Regression test for issue #104.
+#
+. "${srcdir:-.}/lib.sh"
+
+
+MSG1="Failed to execute /usr/bin/pkttyagent: No such file or directory"
+MSG2="Normal application message"
+MSG3="Version specific message"
+MSG4="Service startup message"
+
+LOGDIR="$DIR/log"
+SYSLOG="${LOGDIR}/syslog"
+
+setup_syslogd()
+{
+ mkdir -p "$LOGDIR"
+ cat <<-EOF >"${CONF}"
+ # Log everything for testing
+ *.* -$SYSLOG
+ EOF
+ setup -m0
+}
+
+
+extract_tag()
+{
+ msg="$1"
+ actual_line=$(grep "$msg" "$SYSLOG" | tail -1)
+ if [ -n "$actual_line" ]; then
+ echo "$actual_line" | sed -n 's/.*[0-9][0-9] [^ ]* \([^:]*\):.*/\1/p'
+ fi
+}
+
+show_result()
+{
+ input="$1"
+ expected="$2"
+ got="$3"
+
+ echo "Input: '$input'"
+ echo "Expected: '$expected'"
+ echo "Got: '$got'"
+}
+
+verify()
+{
+ input_tag="$1"
+ expected_tag="$2"
+ msg="$3"
+ expected_pattern="${4:-$expected_tag}"
+
+ actual_tag=$(extract_tag "$msg")
+ if [ -n "$actual_tag" ]; then
+ show_result "$input_tag" "$expected_tag" "$actual_tag"
+ if grep -q "$expected_pattern.*$msg" "$SYSLOG"; then
+ return 0
+ else
+ echo "Log contents:"
+ cat "$SYSLOG"
+ return 1
+ fi
+ else
+ echo "Message not found in log"
+ echo "Log contents:"
+ cat "$SYSLOG"
+ return 1
+ fi
+}
+
+test_normal_tag()
+{
+ logger -b -ip user.info -t "normal-app" "$MSG2"
+ verify "normal-app" "normal-app" "$MSG2"
+}
+
+test_paren_stripping()
+{
+ logger -b -ip user.info -t "(polkit-agent)" "$MSG1"
+ verify "(polkit-agent)" "polkit-agent" "$MSG1"
+}
+
+test_service_stripping()
+{
+ logger -b -ip user.info -t "(service-name)" "$MSG4"
+ verify "(service-name)" "service-name" "$MSG4"
+}
+
+test_partial_parens()
+{
+ logger -b -ip user.info -t "app(version)" "$MSG3"
+ verify "app(version)" "app(version)" "$MSG3"
+}
+
+test_normal_tag_with_pid()
+{
+ pid="$$"
+
+ logger -b -ip user.info -t "normal-app[${pid}]" "$MSG2"
+ verify "normal-app[${pid}]" "normal-app[${pid}]" "$MSG2" "normal-app\[${pid}\]"
+}
+
+test_paren_with_pid()
+{
+ pid="$$"
+
+ logger -b -ip user.info -t "(polkit-agent)[$pid]" "$MSG1"
+ verify "(polkit-agent)[$pid]" "polkit-agent[$pid]" "$MSG1" "polkit-agent\[$pid\]"
+}
+
+
+run_step "Set up syslogd for parentheses testing" setup_syslogd
+run_step "Test parenthetical tag stripping" test_paren_stripping
+run_step "Test normal tag preservation" test_normal_tag
+run_step "Test partial parentheses preservation" test_partial_parens
+run_step "Test service parenthetical tag" test_service_stripping
+run_step "Test parenthetical tag with PID" test_paren_with_pid
+run_step "Test normal tag with PID" test_normal_tag_with_pid
--
2.43.0
@@ -0,0 +1,142 @@
From c1c3f253a8963c6e9e41fafcf9b888f3f5a34906 Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Mon, 3 Nov 2025 13:11:56 +0100
Subject: [PATCH 4/4] syslogd: unescape Linux /dev/kmsg messages before
sanitization
Organization: Wires
Linux's /dev/kmsg interface escapes all non-printable characters and
backslashes using C-style hex encoding (\xHH format). Preventing the
recent UTF-8 sanitization improvements from working correctly, since
UTF-8 sequences arrive as escaped text like "\xe2\x80\x94" rather than
as actual bytes, see [1] for details.
This commit implements "unescaping" of the kernel's C-style format
before applying the UTF-8-aware sanitization. Ensuring that UTF-8
content also in kernel messages is properly preserved when using the
-8 flag.
[1]: https://www.kernel.org/doc/Documentation/ABI/testing/dev-kmsg
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
---
src/syslogd.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 77 insertions(+), 4 deletions(-)
diff --git a/src/syslogd.c b/src/syslogd.c
index 37e1920..0b6bc30 100644
--- a/src/syslogd.c
+++ b/src/syslogd.c
@@ -155,6 +155,7 @@ static int KernLog = 1; /* Track kernel logs by default */
static int KeepKernFac; /* Keep remotely logged kernel facility */
static int KeepKernTime; /* Keep kernel timestamp, evern after initial read */
static int KeepKernConsole; /* Keep kernel logging to console */
+static int IsLinuxKmsg; /* Set if reading from Linux /dev/kmsg */
static int rotate_opt; /* Set if command line option has been given (wins) */
static off_t RotateSz = 0; /* Max file size (bytes) before rotating, disabled by default */
@@ -651,8 +652,10 @@ int main(int argc, char *argv[])
_PATH_KLOG);
else
kern_console_off();
- } else
+ } else {
+ IsLinuxKmsg = 1;
kern_console_off();
+ }
}
no_klogd:
consfile.f_type = F_CONSOLE;
@@ -1062,6 +1065,61 @@ utf8_valid(const unsigned char *in, size_t len)
return 1;
}
+/*
+ * Unescapes Linux /dev/kmsg messages that use C-style hex encoding.
+ * Converts "\xHH" sequences back to bytes and "\\" back to "\".
+ * Returns the new length of the unescaped string.
+ */
+static size_t kmsg_unescape(char *msg)
+{
+ char *src, *dst;
+ int hi, lo;
+
+ src = dst = msg;
+ while (*src) {
+ if (*src == '\\' && src[1]) {
+ if (src[1] == 'x' && src[2] && src[3]) {
+ /* Decode \xHH */
+ hi = src[2];
+ lo = src[3];
+
+ /* Convert hex digits to values */
+ if (hi >= '0' && hi <= '9')
+ hi = hi - '0';
+ else if (hi >= 'a' && hi <= 'f')
+ hi = hi - 'a' + 10;
+ else if (hi >= 'A' && hi <= 'F')
+ hi = hi - 'A' + 10;
+ else
+ goto copy_literal;
+
+ if (lo >= '0' && lo <= '9')
+ lo = lo - '0';
+ else if (lo >= 'a' && lo <= 'f')
+ lo = lo - 'a' + 10;
+ else if (lo >= 'A' && lo <= 'F')
+ lo = lo - 'A' + 10;
+ else
+ goto copy_literal;
+
+ *dst++ = (char)((hi << 4) | lo);
+ src += 4;
+ continue;
+ } else if (src[1] == '\\') {
+ /* Decode \\ to \ */
+ *dst++ = '\\';
+ src += 2;
+ continue;
+ }
+ }
+copy_literal:
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+
+ return dst - msg;
+}
+
/*
* Removes characters from log messages that are unsafe to display.
* Preserves valid UTF-8 sequences, including BOM, with -8 flag.
@@ -1773,9 +1831,24 @@ void printsys(char *msg)
parsemsg_rfc3164_app_name_procid(&p, &buffer.app_name, &buffer.proc_id);
q = lp;
- while (*p != '\0' && (c = *p++) != '\n' && q < &line[MAXLINE])
- *q++ = c;
- *q = '\0';
+ if (IsLinuxKmsg) {
+ char tmp[MAXLINE + 1];
+ char *t = tmp;
+
+ while (*p != '\0' && *p != '\n' && t < &tmp[MAXLINE])
+ *t++ = *p++;
+ *t = '\0';
+
+ /* Unescape \xHH sequences and \\ */
+ kmsg_unescape(tmp);
+
+ /* Sanitize the unescaped message with UTF-8 support */
+ parsemsg_remove_unsafe_characters(tmp, lp, MAXLINE);
+ } else {
+ while (*p != '\0' && (c = *p++) != '\n' && q < &line[MAXLINE])
+ *q++ = c;
+ *q = '\0';
+ }
logmsg(&buffer);
}
--
2.43.0