package/ser2net: backport security patch for GHSA-cgh5-39mg-vhfr

- GHSA-cgh5-39mg-vhfr: Path traversal in authentication via unsanitized
  username allows auth bypass
  https://github.com/cminyard/ser2net/security/advisories/GHSA-cgh5-39mg-vhfr
  No CVE ID yet.

Signed-off-by: Mattia Narducci <mattianarducci1@gmail.com>
Signed-off-by: Julien Olivain <ju.o@free.fr>
(cherry picked from commit 9809b30290)
Signed-off-by: Titouan Christophe <titouan.christophe@mind.be>
This commit is contained in:
Mattia Narducci
2026-08-12 13:33:04 +02:00
committed by Titouan Christophe
parent f185ca044f
commit 34b3608160

View File

@@ -0,0 +1,89 @@
From fa6c2a8840cbc8d7622e46ce12ba15ecc0fb51b7 Mon Sep 17 00:00:00 2001
From: Corey Minyard <corey@minyard.net>
Date: Thu, 23 Jul 2026 10:51:25 -0500
Subject: [PATCH] Fix authorization path handling
The username is received from the remote end and thus untrusted. Make
sure it doesn't have any characters that can cause it to escape the
directory it is supposed to be in when constructing a path.
Reported-by: TristanInSec
Signed-off-by: Corey Minyard <corey@minyard.net>
Upstream: https://github.com/cminyard/ser2net/commit/fa6c2a8840cbc8d7622e46ce12ba15ecc0fb51b7
Signed-off-by: Mattia Narducci <mattianarducci1@gmail.com>
---
auth.c | 42 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 38 insertions(+), 4 deletions(-)
diff --git a/auth.c b/auth.c
index 95c80b4..53c3186 100644
--- a/auth.c
+++ b/auth.c
@@ -185,6 +185,40 @@ handle_auth_begin(struct gensio *net, const char *authdir, const char *pamauth,
return GE_NOTSUP;
}
+/*
+ * Construct a secure authorization path.
+ *
+ * filename must be at least MAX_PATH.
+ *
+ * "username" is untrusted, the rest of the data is trusted.
+ */
+static bool
+construct_auth_path(char *filename, const char *authdir, const char *username,
+ const char *format, ...)
+{
+ size_t baselen;
+ va_list ap;
+
+ /*
+ * '/', '.', and '\' are all parts of things that can modify the base
+ * path. Don't allow them in usernames.
+ */
+ if (strchr(username, '.') || strchr(username, '/')
+ || strchr(username, '\\'))
+ return false;
+
+ /* Get a good base path ending in / */
+ baselen = snprintf(filename, PATH_MAX, "%s/%s/",
+ authdir, username);
+
+ /* Now append the rest of the path. */
+ va_start(ap, format);
+ vsnprintf(filename + baselen, PATH_MAX - baselen, format, ap);
+ va_end(ap);
+
+ return true;
+}
+
static int
handle_precert(struct gensio *net, const char *authdir)
{
@@ -228,8 +262,8 @@ handle_precert(struct gensio *net, const char *authdir)
}
}
- snprintf(filename, sizeof(filename), "%s/%s/allowed_certs/",
- authdir, s);
+ if (!construct_auth_path(filename, authdir, s, "allowed_certs/"))
+ return GE_AUTHREJECT;
err = gensio_control(net, 0, false, GENSIO_CONTROL_CERT_AUTH,
filename, &len);
if (err && err != GE_CERTNOTFOUND) {
@@ -258,8 +292,8 @@ handle_password(struct gensio *net, const char *authdir, const char *password)
return GE_AUTHREJECT;
}
- snprintf(filename, sizeof(filename), "%s/%s/password",
- authdir, username);
+ if (!construct_auth_path(filename, authdir, username, "password"))
+ return GE_AUTHREJECT;
pwfile = fopen(filename, "r");
if (!pwfile) {
syslog(LOG_ERR, "Can't open password file %s: %s", filename,
--
2.55.0