From 442b5724788a00829685439101fc27fb48882e8e Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Fri, 14 Nov 2025 16:55:19 +0100 Subject: [PATCH] patches/sysklogd: backport fixes - Allow parenthesis in tag names - Unescape Linux /dev/kmsg messages for unicode characters Signed-off-by: Joachim Wiberg --- ...8-handling-with-8-flag-for-RFC5424-c.patch | 2 +- ...ntheses-handling-in-RFC-3164-tag-par.patch | 78 ++++++++ ...est-verify-parenthetical-tag-parsing.patch | 168 ++++++++++++++++++ ...-Linux-dev-kmsg-messages-before-sani.patch | 142 +++++++++++++++ 4 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 patches/sysklogd/2.7.2/0002-syslogd-fix-parentheses-handling-in-RFC-3164-tag-par.patch create mode 100644 patches/sysklogd/2.7.2/0003-test-verify-parenthetical-tag-parsing.patch create mode 100644 patches/sysklogd/2.7.2/0004-syslogd-unescape-Linux-dev-kmsg-messages-before-sani.patch diff --git a/patches/sysklogd/2.7.2/0001-syslogd-fix-UTF-8-handling-with-8-flag-for-RFC5424-c.patch b/patches/sysklogd/2.7.2/0001-syslogd-fix-UTF-8-handling-with-8-flag-for-RFC5424-c.patch index 25968b5f..e601ade7 100644 --- a/patches/sysklogd/2.7.2/0001-syslogd-fix-UTF-8-handling-with-8-flag-for-RFC5424-c.patch +++ b/patches/sysklogd/2.7.2/0001-syslogd-fix-UTF-8-handling-with-8-flag-for-RFC5424-c.patch @@ -1,7 +1,7 @@ From 31dfe0d16461d2852f3fc56cb82aed3a23db022f Mon Sep 17 00:00:00 2001 From: Joachim Wiberg 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 diff --git a/patches/sysklogd/2.7.2/0002-syslogd-fix-parentheses-handling-in-RFC-3164-tag-par.patch b/patches/sysklogd/2.7.2/0002-syslogd-fix-parentheses-handling-in-RFC-3164-tag-par.patch new file mode 100644 index 00000000..d1d00684 --- /dev/null +++ b/patches/sysklogd/2.7.2/0002-syslogd-fix-parentheses-handling-in-RFC-3164-tag-par.patch @@ -0,0 +1,78 @@ +From f9b6531430c6485af4697a5868a6f43c93d20419 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +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 +--- + 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 + diff --git a/patches/sysklogd/2.7.2/0003-test-verify-parenthetical-tag-parsing.patch b/patches/sysklogd/2.7.2/0003-test-verify-parenthetical-tag-parsing.patch new file mode 100644 index 00000000..6d625b5e --- /dev/null +++ b/patches/sysklogd/2.7.2/0003-test-verify-parenthetical-tag-parsing.patch @@ -0,0 +1,168 @@ +From 10372296093cc70f3d21b46359a4cfbd8312c968 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +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 +--- + 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 + diff --git a/patches/sysklogd/2.7.2/0004-syslogd-unescape-Linux-dev-kmsg-messages-before-sani.patch b/patches/sysklogd/2.7.2/0004-syslogd-unescape-Linux-dev-kmsg-messages-before-sani.patch new file mode 100644 index 00000000..5e875fe9 --- /dev/null +++ b/patches/sysklogd/2.7.2/0004-syslogd-unescape-Linux-dev-kmsg-messages-before-sani.patch @@ -0,0 +1,142 @@ +From c1c3f253a8963c6e9e41fafcf9b888f3f5a34906 Mon Sep 17 00:00:00 2001 +From: Joachim Wiberg +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 +--- + 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 +