From 796bf397a49aac31645309a5444b0589cca7be09 Mon Sep 17 00:00:00 2001 From: Thomas Perale Date: Wed, 13 May 2026 12:39:07 +0200 Subject: [PATCH] package/proftpd: patch CVE-2026-42167 - CVE-2026-42167: mod_sql in ProFTPD before 1.3.9a allows remote attackers to execute arbitrary code via a username, in scenarios where there is logging of USER requests with an expansion such as %U, and the SQL backend allows commands (e.g., COPY TO PROGRAM). For more information, see: - https://www.cve.org/CVERecord?id=CVE-2026-42167 - https://github.com/proftpd/proftpd/issues/2052 - https://github.com/proftpd/proftpd/commit/af90843baf7dcb8c6be1e5261be2d0b5b5850673 The backport was provided by the Fedora Community: - https://src.fedoraproject.org/rpms/proftpd/raw/epel9/f/2052.patch (cherry picked from commit 0d5ce9ed84e7dd050624252045caa813c0f05e82) Signed-off-by: Thomas Perale --- package/proftpd/0001-CVE-2026-42167.patch | 199 ++++++++++++++++++++++ package/proftpd/proftpd.mk | 3 + 2 files changed, 202 insertions(+) create mode 100644 package/proftpd/0001-CVE-2026-42167.patch diff --git a/package/proftpd/0001-CVE-2026-42167.patch b/package/proftpd/0001-CVE-2026-42167.patch new file mode 100644 index 0000000000..8ac4803d3d --- /dev/null +++ b/package/proftpd/0001-CVE-2026-42167.patch @@ -0,0 +1,199 @@ +From 415395b795436ae47cc25b2394e80033b80f11be Mon Sep 17 00:00:00 2001 +From: TJ Saunders +Date: Mon, 27 Apr 2026 12:13:09 -0700 +Subject: [PATCH] Issue #2052: When resolving any variable whose value is + supplied by the client, make sure we **always** escape that value text. + +CVE: CVE-2026-42167 +Upstream: https://github.com/proftpd/proftpd/commit/af90843baf7dcb8c6be1e5261be2d0b5b5850673 +Upstream: https://src.fedoraproject.org/rpms/proftpd/raw/epel9/f/2052.patch +Signed-off-by: Thomas Perale +--- + contrib/mod_sql.c | 103 ++++++++++++++++++++++++++++------------------ + 1 file changed, 64 insertions(+), 39 deletions(-) + +diff --git a/contrib/mod_sql.c b/contrib/mod_sql.c +index 4978b38b70..4872f5fa64 100644 +--- a/contrib/mod_sql.c ++++ b/contrib/mod_sql.c +@@ -2,7 +2,7 @@ + * ProFTPD: mod_sql -- SQL frontend + * Copyright (c) 1998-1999 Johnie Ingram. + * Copyright (c) 2001 Andrew Houghton. +- * Copyright (c) 2004-2022 TJ Saunders ++ * Copyright (c) 2004-2026 TJ Saunders + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -760,40 +760,46 @@ static int is_escaped_text(const char *text, size_t text_len) { + } + + static int sql_resolved_append_text(pool *p, struct sql_resolved *resolved, +- const char *text, size_t text_len) { +- char *new_text; +- size_t new_textlen; ++ const char *text, size_t text_len, int already_escaped) { ++ char *new_text = NULL; ++ size_t new_textlen = 0; + + if (text == NULL || + text_len == 0) { + return 0; + } + +- /* For backward compatibility (see Issue #1149), we indulge in a little +- * heuristic here, and only escape the text if it hasn't already been +- * escaped. How to properly tell? If the first and last characters of +- * the given text are `'`, AND there are no other occurrences of that +- * character in the text, assume it has already been quoted. +- */ +- if (is_escaped_text(text, text_len) == FALSE) { +- modret_t *mr; ++ new_text = (char *) text; ++ new_textlen = text_len; + +- mr = sql_dispatch(sql_make_cmd(p, 2, resolved->conn_name, text), +- "sql_escapestring"); +- if (check_response(mr, resolved->conn_flags) < 0) { +- errno = EIO; +- return -1; +- } ++ if (already_escaped == FALSE) { ++ /* For backward compatibility (see Issue #1149), we indulge in a little ++ * heuristic here, and only escape the text if it hasn't already been ++ * escaped. How to properly tell? If the first and last characters of ++ * the given text are `'`, AND there are no other occurrences of that ++ * character in the text, assume it has already been quoted. ++ * ++ * Per Issue #2052, we refine this to use this heuristic only if we do ++ * not already know that the text has been escaped. Some callers may ++ * have already escaped the provided text for us. ++ */ ++ if (is_escaped_text(text, text_len) == FALSE) { ++ modret_t *mr; + +- new_text = (char *) mr->data; +- new_textlen = strlen(new_text); ++ mr = sql_dispatch(sql_make_cmd(p, 2, resolved->conn_name, text), ++ "sql_escapestring"); ++ if (check_response(mr, resolved->conn_flags) < 0) { ++ errno = EIO; ++ return -1; ++ } + +- } else { +- pr_trace_msg(trace_channel, 17, +- "text '%s' is already escaped, skipping escaping it again", text); ++ new_text = (char *) mr->data; ++ new_textlen = strlen(new_text); + +- new_text = (char *) text; +- new_textlen = text_len; ++ } else { ++ pr_trace_msg(trace_channel, 17, ++ "text '%s' is already escaped, skipping escaping it again", text); ++ } + } + + if (new_textlen > resolved->buflen) { +@@ -811,7 +817,7 @@ static int sql_resolved_append_text(pool *p, struct sql_resolved *resolved, + + static int sql_resolve_on_meta(pool *p, pr_jot_ctx_t *jot_ctx, + unsigned char logfmt_id, const char *jot_hint, const void *val) { +- int res = 0; ++ int res = 0, already_escaped = FALSE; + struct sql_resolved *resolved; + + resolved = jot_ctx->log; +@@ -970,35 +976,53 @@ static int sql_resolve_on_meta(pool *p, pr_jot_ctx_t *jot_ctx, + break; + } + ++ /* Per Issue #2052, the following variable values can all be supplied ++ * remotely by the client. As such, they should be escaped preemptively. ++ */ + case LOGFMT_META_ANON_PASS: + case LOGFMT_META_BASENAME: +- case LOGFMT_META_CLASS: + case LOGFMT_META_CMD_PARAMS: + case LOGFMT_META_COMMAND: + case LOGFMT_META_DIR_NAME: + case LOGFMT_META_DIR_PATH: ++ case LOGFMT_META_FILENAME: ++ case LOGFMT_META_IDENT_USER: ++ case LOGFMT_META_METHOD: ++ case LOGFMT_META_ORIGINAL_USER: ++ case LOGFMT_META_RESPONSE_STR: ++ case LOGFMT_META_REMOTE_HOST: ++ case LOGFMT_META_RENAME_FROM: ++ case LOGFMT_META_USER: ++ case LOGFMT_META_XFER_PATH: { ++ modret_t *mr; ++ ++ mr = sql_dispatch(sql_make_cmd(p, 2, resolved->conn_name, ++ (const char *) val), "sql_escapestring"); ++ if (check_response(mr, resolved->conn_flags) < 0) { ++ errno = EIO; ++ return -1; ++ } ++ ++ text = (char *) mr->data; ++ text_len = strlen(text); ++ already_escaped = TRUE; ++ break; ++ } ++ ++ case LOGFMT_META_CLASS: + case LOGFMT_META_ENV_VAR: + case LOGFMT_META_EOS_REASON: +- case LOGFMT_META_FILENAME: + case LOGFMT_META_GROUP: +- case LOGFMT_META_IDENT_USER: + case LOGFMT_META_ISO8601: + case LOGFMT_META_LOCAL_FQDN: + case LOGFMT_META_LOCAL_IP: + case LOGFMT_META_LOCAL_NAME: +- case LOGFMT_META_METHOD: + case LOGFMT_META_NOTE_VAR: +- case LOGFMT_META_ORIGINAL_USER: + case LOGFMT_META_PROTOCOL: +- case LOGFMT_META_REMOTE_HOST: + case LOGFMT_META_REMOTE_IP: +- case LOGFMT_META_RENAME_FROM: +- case LOGFMT_META_RESPONSE_STR: +- case LOGFMT_META_USER: + case LOGFMT_META_VERSION: + case LOGFMT_META_VHOST_IP: + case LOGFMT_META_XFER_FAILURE: +- case LOGFMT_META_XFER_PATH: + case LOGFMT_META_XFER_STATUS: + case LOGFMT_META_XFER_TYPE: + default: +@@ -1011,7 +1035,8 @@ static int sql_resolve_on_meta(pool *p, pr_jot_ctx_t *jot_ctx, + text_len = strlen(text); + } + +- res = sql_resolved_append_text(p, resolved, text, text_len); ++ res = sql_resolved_append_text(p, resolved, text, text_len, ++ already_escaped); + } + + return res; +@@ -1074,7 +1099,7 @@ static int sql_resolve_on_default(pool *p, pr_jot_ctx_t *jot_ctx, + break; + } + +- res = sql_resolved_append_text(p, resolved, text, text_len); ++ res = sql_resolved_append_text(p, resolved, text, text_len, FALSE); + } + + return res; +@@ -3175,7 +3200,7 @@ static int showinfo_on_meta(pool *p, pr_jot_ctx_t *jot_ctx, + } + + text_len = strlen(text); +- res = sql_resolved_append_text(p, resolved, text, text_len); ++ res = sql_resolved_append_text(p, resolved, text, text_len, FALSE); + + } else { + res = sql_resolve_on_meta(p, jot_ctx, logfmt_id, jot_hint, val); diff --git a/package/proftpd/proftpd.mk b/package/proftpd/proftpd.mk index 84a499a5c0..7310cd02fe 100644 --- a/package/proftpd/proftpd.mk +++ b/package/proftpd/proftpd.mk @@ -11,6 +11,9 @@ PROFTPD_LICENSE_FILES = COPYING PROFTPD_CPE_ID_VENDOR = proftpd PROFTPD_SELINUX_MODULES = ftp +# 0001-CVE-2026-42167.patch +PROFTPD_IGNORE_CVES += CVE-2026-42167 + PROFTPD_CONF_ENV = \ ac_cv_func_setpgrp_void=yes \ ac_cv_func_setgrent_void=yes