From 446fe466539a32403d1c6976d565eb06e409021b Mon Sep 17 00:00:00 2001
From: Frank <111432253+fhofmannCF@users.noreply.github.com>
Date: Fri, 19 Aug 2022 18:08:19 +0100
Subject: [PATCH] Limit KeyctlString() to DESCRIBE / GET_SECURITY

KeyctlString() treats the query data buffer filled by the keyctl(2) syscall as C-Style string with a trailing NULL byte.

This is only true for two cmds - KEYCTL_DESCRIBE and KEYCTL_GET_SECURITY.

Both are guaranteed to return at least an empty (C) string (i.e. a one-byte-sized buffer containing only a NULL byte) if the requested attribute is not set at all.

Other cmds that can be passed to the system call - KEYCTL_READ most prominently - return explicitly-sized binary data; NULL bytes have no special meaning for these queries and are _part_ of the returned data, irrespective of where in the buffer they occur. Returning nothing (zero-length) can also be permitted; example of this is a KEYCTL_READ on an empty keyring.

If KeyctlString() is called with any of the keyctl(2) query commands other than the two that are guaranteed to return C-style strings, it will either:
* panic (attempting to strip trailing null bytes from zero-sized buffers), or
* truncate the returned data in error (for KEYCTL_READ of a key payload).

Therefore, restrict the use.
---
 unix/syscall_linux.go | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/unix/syscall_linux.go b/unix/syscall_linux.go
index ecb0f27fb8..60f203a90b 100644
--- a/unix/syscall_linux.go
+++ b/unix/syscall_linux.go
@@ -1381,6 +1381,13 @@ func SetsockoptTCPRepairOpt(fd, level, opt int, o []TCPRepairOpt) (err error) {
 // KeyctlString calls keyctl commands which return a string.
 // These commands are KEYCTL_DESCRIBE and KEYCTL_GET_SECURITY.
 func KeyctlString(cmd int, id int) (string, error) {
+	// Other queries - particularly KEYCTL_READ - can either return zero-length data,
+	// or explicitly-sized buffers of binary data (which may include NULL bytes).
+	// This function misbehaves then (panics or truncates returns), so explicitly
+	// allow only the two cmds documented to return NUL-terminated (C-type) strings.
+	if cmd != unix.KEYCTL_DESCRIBE || cmd != unix.KEYCTL_GET_SECURITY {
+		return "", EINVAL
+	}
 	// We must loop as the string data may change in between the syscalls.
 	// We could allocate a large buffer here to reduce the chance that the
 	// syscall needs to be called twice; however, this is unnecessary as