Version in base suite: 2.4-2+deb12u1 Version in overlay suite: 2.4-2+deb12u2 Base version: inetutils_2.4-2+deb12u2 Target version: inetutils_2.4-2+deb12u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/i/inetutils/inetutils_2.4-2+deb12u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/i/inetutils/inetutils_2.4-2+deb12u3.dsc changelog | 41 local/man/telnetd.8 | 10 patches/local/0007-gnulib-update.patch | 1118 ++++++++++ patches/local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch | 149 + patches/local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch | 165 + patches/series | 6 patches/upstream/0001-Fix-injection-bug-with-bogus-user-names.patch | 9 patches/upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch | 54 patches/upstream/0002-telnetd-Sanitize-all-variable-expansions.patch | 13 patches/upstream/0004-telnetd-add-the-new-accept-env-option.patch | 220 + patches/upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch | 35 11 files changed, 1798 insertions(+), 22 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp5gsn3j31/inetutils_2.4-2+deb12u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp5gsn3j31/inetutils_2.4-2+deb12u3.dsc: no acceptable signature found diff -Nru inetutils-2.4/debian/changelog inetutils-2.4/debian/changelog --- inetutils-2.4/debian/changelog 2026-01-21 16:42:52.000000000 +0000 +++ inetutils-2.4/debian/changelog 2026-03-30 14:52:10.000000000 +0000 @@ -1,9 +1,42 @@ +inetutils (2:2.4-2+deb12u3) bookworm-security; urgency=high + + * Add patch from upstream: + - Prevent privilege escalation via telnetd abusing systemd service + credentials support added to the login(1) implementation of util-linux in + release 2.40. Reported by Ron Ben Yizhak . + Fixes CVE-2026-28372. + - Ignore all environment options from clients unless the variable was + listed in the new --accept-env telnetd option. This mitigates privilege + escalation using environment variables. + This is the complete fix for CVE-2026-24061, with its own CVE pending. + - Fix stack buffer overflow processing SLC suboption triplets. + Reported by Adiel Sol, Arad Inbar, Erez Cohen, Nir Somech, Ben Grinberg, + Daniel Lubel at DREAM Security Research Team. + Fixes CVE-2026-32746. (Closes: #1130742) + * Add the hashcode-string1 module from forky/sid gnulib adapted to bookworm + required by the --accept-env patch, and the gl_hash_set, gl_set, gl_xset + and gl_anyhash bookworm gnulib modules required by hashcode-string1. + Inject new gnulib modules in lib/Makefile.am. + * Adapt netkit-telnet patch to not leak unexported environment variables to + telnetd. Reported by Justin Swartz . + Fixes CVE-2026-32772. (Closes: #1130741) + * Prevent user local privilege escalation using --debug, which was + susceptible to symlink attacks, or leaking on-wire credentials to a + user that had pre-created the file and kept it open. Fix by switching + from /tmp/telnet.debug to /run/telnet/debug., and making the + setup error checks fatal. + Partially reported by Justin Swartz . + * Update local telnetd man page to match new --debug behavior. + + -- Guillem Jover Mon, 30 Mar 2026 16:52:10 +0200 + inetutils (2:2.4-2+deb12u2) bookworm-security; urgency=high - * Fix remote authentication bypass in telnetd. - GNU InetUtils Security Advisory: - - Fixes CVE-2026-24061. (Closes: #1126047) + * Add patch from upstream: + - Fix remote authentication bypass in telnetd. + GNU InetUtils Security Advisory: + + Fixes CVE-2026-24061. (Closes: #1126047) -- Guillem Jover Wed, 21 Jan 2026 17:42:52 +0100 diff -Nru inetutils-2.4/debian/local/man/telnetd.8 inetutils-2.4/debian/local/man/telnetd.8 --- inetutils-2.4/debian/local/man/telnetd.8 2026-01-21 16:42:52.000000000 +0000 +++ inetutils-2.4/debian/local/man/telnetd.8 2026-03-30 14:52:10.000000000 +0000 @@ -124,10 +124,16 @@ This option may be used for debugging purposes. This allows .Nm telnetd -to print out debugging information -to the connection, allowing the user to see what +to write out debugging information to a +.Pa /run/telnet/debug.PID +file, allowing the user to see what .Nm telnetd is doing. +The exact file will be printed by +.Nm telnetd +on the +.Nm telnet +session. There are several possible values for .Ar debugmode : .Bl -tag -width exercise diff -Nru inetutils-2.4/debian/patches/local/0007-gnulib-update.patch inetutils-2.4/debian/patches/local/0007-gnulib-update.patch --- inetutils-2.4/debian/patches/local/0007-gnulib-update.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/local/0007-gnulib-update.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,1118 @@ +Description: Add the required gnulib code from forky/sid or bookworm gnulib + The 0004-telnetd-add-the-new-accept-env-option.patch patch, requires these + modules which are not available in trixie gnulib and ealier, or not included + in the existing upstream package, and we do not automatically refresh the + gnulib modules. +Origin: vendor, Debian +Forwarded: not-needed + +--- + lib/Makefile.am | 8 + + lib/gl_anyhash1.h | 31 ++++ + lib/gl_anyhash2.h | 82 ++++++++++++ + lib/gl_anyhash_primes.h | 87 +++++++++++++ + lib/gl_hash_set.c | 313 ++++++++++++++++++++++++++++++++++++++++++++++++ + lib/gl_hash_set.h | 34 +++++ + lib/gl_set.c | 21 +++ + lib/gl_set.h | 280 ++++++++++++++++++++++++++++++++++++++++++ + lib/gl_xset.c | 21 +++ + lib/gl_xset.h | 81 ++++++++++++ + lib/hashcode-string1.c | 62 +++++++++ + lib/hashcode-string1.h | 33 +++++ + 12 files changed, 1053 insertions(+) + +--- /dev/null ++++ b/lib/hashcode-string1.c +@@ -0,0 +1,62 @@ ++/* hashcode-string1.c -- compute a hash value from a NUL-terminated string. ++ ++ Copyright (C) 1998-2004, 2006-2007, 2009-2026 Free Software Foundation, Inc. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++/* Specification. */ ++#include "hashcode-string1.h" ++ ++#if USE_DIFF_HASH ++ ++# include "bitrotate.h" ++ ++/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see ++ B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm, ++ Software--practice & experience 20, 2 (Feb 1990), 209-224. Good hash ++ algorithms tend to be domain-specific, so what's good for [diffutils'] io.c ++ may not be good for your application." */ ++ ++size_t ++hash_string (const char *string, size_t tablesize) ++{ ++ size_t value = 0; ++ unsigned char ch; ++ ++ for (; (ch = *string); string++) ++ value = ch + rotl_sz (value, 7); ++ return value % tablesize; ++} ++ ++#else /* not USE_DIFF_HASH */ ++ ++/* This one comes from 'recode', and performs a bit better than the above as ++ per a few experiments. It is inspired from a hashing routine found in the ++ very old Cyber 'snoop', itself written in typical Greg Mansfield style. ++ (By the way, what happened to this excellent man? Is he still alive?) */ ++ ++size_t ++hash_string (const char *string, size_t tablesize) ++{ ++ size_t value = 0; ++ unsigned char ch; ++ ++ for (; (ch = *string); string++) ++ value = (value * 31 + ch) % tablesize; ++ return value; ++} ++ ++#endif /* not USE_DIFF_HASH */ +--- /dev/null ++++ b/lib/hashcode-string1.h +@@ -0,0 +1,33 @@ ++/* hashcode-string1.h -- declaration for a simple hash function ++ Copyright (C) 1998-2004, 2006-2007, 2009-2026 Free Software Foundation, Inc. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* Compute a hash code for a NUL-terminated string S, ++ and return the hash code modulo TABLESIZE. ++ The result is platform dependent: it depends on the size of the 'size_t' ++ type. */ ++extern size_t hash_string (char const *s, size_t tablesize) _GL_ATTRIBUTE_PURE; ++ ++ ++#ifdef __cplusplus ++} ++#endif +--- /dev/null ++++ b/lib/gl_hash_set.c +@@ -0,0 +1,313 @@ ++/* Set data type implemented by a hash table. ++ Copyright (C) 2006, 2008-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2018. ++ ++ 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 ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++/* Specification. */ ++#include "gl_hash_set.h" ++ ++#include /* for uintptr_t, SIZE_MAX */ ++#include ++ ++#include "xsize.h" ++ ++/* --------------------------- gl_set_t Data Type --------------------------- */ ++ ++#include "gl_anyhash1.h" ++ ++/* Concrete list node implementation, valid for this file only. */ ++struct gl_list_node_impl ++{ ++ struct gl_hash_entry h; /* hash table entry fields; must be first */ ++ const void *value; ++}; ++typedef struct gl_list_node_impl * gl_list_node_t; ++ ++/* Concrete gl_set_impl type, valid for this file only. */ ++struct gl_set_impl ++{ ++ struct gl_set_impl_base base; ++ gl_setelement_hashcode_fn hashcode_fn; ++ /* A hash table: managed as an array of collision lists. */ ++ struct gl_hash_entry **table; ++ size_t table_size; ++ /* Number of hash table entries. */ ++ size_t count; ++}; ++ ++#define CONTAINER_T gl_set_t ++#define CONTAINER_COUNT(set) (set)->count ++#include "gl_anyhash2.h" ++ ++/* --------------------------- gl_set_t Data Type --------------------------- */ ++ ++static gl_set_t ++gl_hash_nx_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++{ ++ struct gl_set_impl *set = ++ (struct gl_set_impl *) malloc (sizeof (struct gl_set_impl)); ++ ++ if (set == NULL) ++ return NULL; ++ ++ set->base.vtable = implementation; ++ set->base.equals_fn = equals_fn; ++ set->base.dispose_fn = dispose_fn; ++ set->hashcode_fn = hashcode_fn; ++ set->table_size = 11; ++ set->table = ++ (gl_hash_entry_t *) calloc (set->table_size, sizeof (gl_hash_entry_t)); ++ if (set->table == NULL) ++ goto fail; ++ set->count = 0; ++ ++ return set; ++ ++ fail: ++ free (set); ++ return NULL; ++} ++ ++static size_t _GL_ATTRIBUTE_PURE ++gl_hash_size (gl_set_t set) ++{ ++ return set->count; ++} ++ ++static bool _GL_ATTRIBUTE_PURE ++gl_hash_search (gl_set_t set, const void *elt) ++{ ++ size_t hashcode = ++ (set->hashcode_fn != NULL ++ ? set->hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ size_t bucket = hashcode % set->table_size; ++ gl_setelement_equals_fn equals = set->base.equals_fn; ++ ++ /* Look for a match in the hash bucket. */ ++ gl_list_node_t node; ++ ++ for (node = (gl_list_node_t) set->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ return true; ++ return false; ++} ++ ++static int ++gl_hash_nx_add (gl_set_t set, const void *elt) ++{ ++ size_t hashcode = ++ (set->hashcode_fn != NULL ++ ? set->hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ size_t bucket = hashcode % set->table_size; ++ gl_setelement_equals_fn equals = set->base.equals_fn; ++ ++ /* Look for a match in the hash bucket. */ ++ { ++ gl_list_node_t node; ++ ++ for (node = (gl_list_node_t) set->table[bucket]; ++ node != NULL; ++ node = (gl_list_node_t) node->h.hash_next) ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ return 0; ++ } ++ ++ /* Allocate a new node. */ ++ gl_list_node_t node = ++ (struct gl_list_node_impl *) malloc (sizeof (struct gl_list_node_impl)); ++ ++ if (node == NULL) ++ return -1; ++ ++ node->value = elt; ++ node->h.hashcode = hashcode; ++ ++ /* Add node to the hash table. */ ++ node->h.hash_next = set->table[bucket]; ++ set->table[bucket] = &node->h; ++ ++ /* Add node to the set. */ ++ set->count++; ++ ++ hash_resize_after_add (set); ++ ++ return 1; ++} ++ ++static bool ++gl_hash_remove (gl_set_t set, const void *elt) ++{ ++ size_t hashcode = ++ (set->hashcode_fn != NULL ++ ? set->hashcode_fn (elt) ++ : (size_t)(uintptr_t) elt); ++ size_t bucket = hashcode % set->table_size; ++ gl_setelement_equals_fn equals = set->base.equals_fn; ++ ++ /* Look for the first match in the hash bucket. */ ++ gl_list_node_t *nodep; ++ ++ for (nodep = (gl_list_node_t *) &set->table[bucket]; ++ *nodep != NULL; ++ nodep = (gl_list_node_t *) &(*nodep)->h.hash_next) ++ { ++ gl_list_node_t node = *nodep; ++ if (node->h.hashcode == hashcode ++ && (equals != NULL ++ ? equals (elt, node->value) ++ : elt == node->value)) ++ { ++ /* Remove node from the hash table. */ ++ *nodep = (gl_list_node_t) node->h.hash_next; ++ ++ /* Remove node from the set. */ ++ set->count--; ++ ++ if (set->base.dispose_fn != NULL) ++ set->base.dispose_fn (node->value); ++ free (node); ++ return true; ++ } ++ } ++ ++ return false; ++} ++ ++static void ++gl_hash_free (gl_set_t set) ++{ ++ if (set->count > 0) ++ { ++ gl_setelement_dispose_fn dispose = set->base.dispose_fn; ++ struct gl_hash_entry **table = set->table; ++ size_t i; ++ ++ for (i = set->table_size; i > 0; ) ++ { ++ gl_hash_entry_t node = table[--i]; ++ ++ while (node != NULL) ++ { ++ gl_hash_entry_t next = node->hash_next; ++ ++ /* Free the entry. */ ++ if (dispose != NULL) ++ dispose (((gl_list_node_t) node)->value); ++ free (node); ++ ++ node = next; ++ } ++ } ++ } ++ ++ free (set->table); ++ free (set); ++} ++ ++/* ---------------------- gl_set_iterator_t Data Type ---------------------- */ ++ ++/* Here we iterate through the hash buckets. Therefore the order in which the ++ elements are returned is unpredictable. */ ++ ++static gl_set_iterator_t ++gl_hash_iterator (gl_set_t set) ++{ ++ gl_set_iterator_t result; ++ ++ result.vtable = set->base.vtable; ++ result.set = set; ++ result.p = NULL; /* runs through the nodes of a bucket */ ++ result.i = 0; /* index of the bucket that p points into + 1 */ ++ result.j = set->table_size; ++#if defined GCC_LINT || defined lint ++ result.q = NULL; ++ result.count = 0; ++#endif ++ ++ return result; ++} ++ ++static bool ++gl_hash_iterator_next (gl_set_iterator_t *iterator, const void **eltp) ++{ ++ if (iterator->p != NULL) ++ { ++ /* We're traversing a bucket. */ ++ gl_list_node_t node = (gl_list_node_t) iterator->p; ++ *eltp = node->value; ++ iterator->p = (gl_list_node_t) node->h.hash_next; ++ return true; ++ } ++ else ++ { ++ /* Find the next bucket that is not empty. */ ++ size_t j = iterator->j; ++ size_t i = iterator->i; ++ ++ if (i < j) ++ { ++ gl_hash_entry_t *table = iterator->set->table; ++ do ++ { ++ gl_list_node_t node = (gl_list_node_t) table[i++]; ++ if (node != NULL) ++ { ++ *eltp = node->value; ++ iterator->p = (gl_list_node_t) node->h.hash_next; ++ iterator->i = i; ++ return true; ++ } ++ } ++ while (i < j); ++ } ++ /* Here iterator->p is still NULL, and i == j. */ ++ iterator->i = j; ++ return false; ++ } ++} ++ ++static void ++gl_hash_iterator_free (gl_set_iterator_t *iterator) ++{ ++} ++ ++ ++const struct gl_set_implementation gl_hash_set_implementation = ++ { ++ gl_hash_nx_create_empty, ++ gl_hash_size, ++ gl_hash_search, ++ gl_hash_nx_add, ++ gl_hash_remove, ++ gl_hash_free, ++ gl_hash_iterator, ++ gl_hash_iterator_next, ++ gl_hash_iterator_free ++ }; +--- /dev/null ++++ b/lib/gl_hash_set.h +@@ -0,0 +1,34 @@ ++/* Set data type implemented by a hash table. ++ Copyright (C) 2006, 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2018. ++ ++ 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 ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_HASH_SET_H ++#define _GL_HASH_SET_H ++ ++#include "gl_set.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern const struct gl_set_implementation gl_hash_set_implementation; ++#define GL_HASH_SET &gl_hash_set_implementation ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* _GL_HASH_SET_H */ +--- /dev/null ++++ b/lib/gl_set.c +@@ -0,0 +1,21 @@ ++/* Abstract set data type. ++ ++ Copyright (C) 2018-2023 Free Software Foundation, Inc. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published ++ by the Free Software Foundation, either version 3 of the License, ++ or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++#define GL_SET_INLINE _GL_EXTERN_INLINE ++#include "gl_set.h" +--- /dev/null ++++ b/lib/gl_set.h +@@ -0,0 +1,280 @@ ++/* Abstract set data type. ++ Copyright (C) 2006-2007, 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2018. ++ ++ 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 ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_SET_H ++#define _GL_SET_H ++ ++#include ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++#ifndef GL_SET_INLINE ++# define GL_SET_INLINE _GL_INLINE ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++ ++/* gl_set is an abstract set data type. It can contain any number of objects ++ ('void *' or 'const void *' pointers); the order does not matter. ++ Duplicates (in the sense of the comparator) are forbidden. ++ ++ There are several implementations of this set datatype, optimized for ++ different operations or for memory. You can start using the simplest set ++ implementation, GL_ARRAY_SET, and switch to a different implementation ++ later, when you realize which operations are performed the most frequently. ++ The API of the different implementations is exactly the same; when switching ++ to a different implementation, you only have to change the gl_set_create ++ call. ++ ++ The implementations are: ++ GL_ARRAY_SET a growable array ++ GL_LINKEDHASH_SET a hash table with a linked list ++ GL_HASH_SET a hash table ++ ++ The memory consumption is asymptotically the same: O(1) for every object ++ in the set. When looking more closely at the average memory consumed ++ for an object, GL_ARRAY_SET is the most compact representation, then comes ++ GL_HASH_SET, and GL_LINKEDHASH_SET needs the most memory. ++ ++ The guaranteed average performance of the operations is, for a set of ++ n elements: ++ ++ Operation ARRAY LINKEDHASH ++ HASH ++ ++ gl_set_size O(1) O(1) ++ gl_set_add O(n) O(1) ++ gl_set_remove O(n) O(1) ++ gl_set_search O(n) O(1) ++ gl_set_iterator O(1) O(1) ++ gl_set_iterator_next O(1) O(1) ++ */ ++ ++/* --------------------------- gl_set_t Data Type --------------------------- */ ++ ++/* Type of function used to compare two elements. ++ NULL denotes pointer comparison. */ ++typedef bool (*gl_setelement_equals_fn) (const void *elt1, const void *elt2); ++ ++/* Type of function used to compute a hash code. ++ NULL denotes a function that depends only on the pointer itself. */ ++typedef size_t (*gl_setelement_hashcode_fn) (const void *elt); ++ ++#ifndef _GL_SETELEMENT_DISPOSE_FN_DEFINED ++/* Type of function used to dispose an element once it's removed from a set. ++ NULL denotes a no-op. */ ++typedef void (*gl_setelement_dispose_fn) (const void *elt); ++# define _GL_SETELEMENT_DISPOSE_FN_DEFINED 1 ++#endif ++ ++struct gl_set_impl; ++/* Type representing an entire set. */ ++typedef struct gl_set_impl * gl_set_t; ++ ++struct gl_set_implementation; ++/* Type representing a set datatype implementation. */ ++typedef const struct gl_set_implementation * gl_set_implementation_t; ++ ++#if 0 /* Unless otherwise specified, these are defined inline below. */ ++ ++/* Creates an empty set. ++ IMPLEMENTATION is one of GL_ARRAY_SET, GL_LINKEDHASH_SET, GL_HASH_SET. ++ EQUALS_FN is an element comparison function or NULL. ++ HASHCODE_FN is an element hash code function or NULL. ++ DISPOSE_FN is an element disposal function or NULL. */ ++/* declared in gl_xset.h */ ++extern gl_set_t gl_set_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++ /*_GL_ATTRIBUTE_DEALLOC (gl_set_free, 1)*/ ++ _GL_ATTRIBUTE_RETURNS_NONNULL; ++/* Likewise. Returns NULL upon out-of-memory. */ ++extern gl_set_t gl_set_nx_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++ /*_GL_ATTRIBUTE_DEALLOC (gl_set_free, 1)*/; ++ ++/* Returns the current number of elements in a set. */ ++extern size_t gl_set_size (gl_set_t set); ++ ++/* Searches whether an element is already in the set. ++ Returns true if found, or false if not present in the set. */ ++extern bool gl_set_search (gl_set_t set, const void *elt); ++ ++/* Adds an element to a set. ++ Returns true if it was not already in the set and added, false otherwise. */ ++/* declared in gl_xset.h */ ++extern bool gl_set_add (gl_set_t set, const void *elt); ++ ++/* Likewise. Returns -1 upon out-of-memory. */ ++_GL_ATTRIBUTE_NODISCARD ++extern int gl_set_nx_add (gl_set_t set, const void *elt); ++ ++/* Removes an element from a set. ++ Returns true if it was found and removed. */ ++extern bool gl_set_remove (gl_set_t set, const void *elt); ++ ++/* Frees an entire set. ++ (But this call does not free the elements of the set. It only invokes ++ the DISPOSE_FN on each of the elements of the set.) */ ++extern void gl_set_free (gl_set_t set); ++ ++#endif /* End of inline and gl_xset.h-defined functions. */ ++ ++/* ---------------------- gl_set_iterator_t Data Type ---------------------- */ ++ ++/* Functions for iterating through a set. ++ Note: Iterating through a set of type GL_HASH_SET returns the elements in an ++ unpredictable order. If you need a predictable order, use GL_LINKEDHASH_SET ++ instead of GL_HASH_SET. */ ++ ++/* Type of an iterator that traverses a set. ++ This is a fixed-size struct, so that creation of an iterator doesn't need ++ memory allocation on the heap. */ ++typedef struct ++{ ++ /* For fast dispatch of gl_set_iterator_next. */ ++ const struct gl_set_implementation *vtable; ++ /* For detecting whether the last returned element was removed. */ ++ gl_set_t set; ++ size_t count; ++ /* Other, implementation-private fields. */ ++ void *p; void *q; ++ size_t i; size_t j; ++} gl_set_iterator_t; ++ ++#if 0 /* These are defined inline below. */ ++ ++/* Creates an iterator traversing a set. ++ The set's contents must not be modified while the iterator is in use, ++ except for removing the last returned element. */ ++extern gl_set_iterator_t gl_set_iterator (gl_set_t set); ++ ++/* If there is a next element, stores the next element in *ELTP, advances the ++ iterator and returns true. Otherwise, returns false. */ ++extern bool gl_set_iterator_next (gl_set_iterator_t *iterator, ++ const void **eltp); ++ ++/* Frees an iterator. */ ++extern void gl_set_iterator_free (gl_set_iterator_t *iterator); ++ ++#endif /* End of inline functions. */ ++ ++/* ------------------------ Implementation Details ------------------------ */ ++ ++struct gl_set_implementation ++{ ++ /* gl_set_t functions. */ ++ gl_set_t (*nx_create_empty) (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn); ++ size_t (*size) (gl_set_t set); ++ bool (*search) (gl_set_t set, const void *elt); ++ int (*nx_add) (gl_set_t set, const void *elt); ++ bool (*remove_elt) (gl_set_t set, const void *elt); ++ void (*set_free) (gl_set_t set); ++ /* gl_set_iterator_t functions. */ ++ gl_set_iterator_t (*iterator) (gl_set_t set); ++ bool (*iterator_next) (gl_set_iterator_t *iterator, const void **eltp); ++ void (*iterator_free) (gl_set_iterator_t *iterator); ++}; ++ ++struct gl_set_impl_base ++{ ++ const struct gl_set_implementation *vtable; ++ gl_setelement_equals_fn equals_fn; ++ gl_setelement_dispose_fn dispose_fn; ++}; ++ ++/* Define all functions of this file as accesses to the ++ struct gl_set_implementation. */ ++ ++GL_SET_INLINE ++/*_GL_ATTRIBUTE_DEALLOC (gl_set_free, 1)*/ ++gl_set_t ++gl_set_nx_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++{ ++ return implementation->nx_create_empty (implementation, equals_fn, ++ hashcode_fn, dispose_fn); ++} ++ ++GL_SET_INLINE size_t ++gl_set_size (gl_set_t set) ++{ ++ return ((const struct gl_set_impl_base *) set)->vtable->size (set); ++} ++ ++GL_SET_INLINE bool ++gl_set_search (gl_set_t set, const void *elt) ++{ ++ return ((const struct gl_set_impl_base *) set)->vtable->search (set, elt); ++} ++ ++_GL_ATTRIBUTE_NODISCARD GL_SET_INLINE int ++gl_set_nx_add (gl_set_t set, const void *elt) ++{ ++ return ((const struct gl_set_impl_base *) set)->vtable->nx_add (set, elt); ++} ++ ++GL_SET_INLINE bool ++gl_set_remove (gl_set_t set, const void *elt) ++{ ++ return ((const struct gl_set_impl_base *) set)->vtable->remove_elt (set, elt); ++} ++ ++GL_SET_INLINE void ++gl_set_free (gl_set_t set) ++{ ++ ((const struct gl_set_impl_base *) set)->vtable->set_free (set); ++} ++ ++GL_SET_INLINE gl_set_iterator_t ++gl_set_iterator (gl_set_t set) ++{ ++ return ((const struct gl_set_impl_base *) set)->vtable->iterator (set); ++} ++ ++GL_SET_INLINE bool ++gl_set_iterator_next (gl_set_iterator_t *iterator, const void **eltp) ++{ ++ return iterator->vtable->iterator_next (iterator, eltp); ++} ++ ++GL_SET_INLINE void ++gl_set_iterator_free (gl_set_iterator_t *iterator) ++{ ++ iterator->vtable->iterator_free (iterator); ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++_GL_INLINE_HEADER_END ++ ++#endif /* _GL_SET_H */ +--- /dev/null ++++ b/lib/gl_xset.c +@@ -0,0 +1,21 @@ ++/* Abstract set data type, with out-of-memory checking. ++ ++ Copyright (C) 2018-2023 Free Software Foundation, Inc. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published ++ by the Free Software Foundation, either version 3 of the License, ++ or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#include ++ ++#define GL_XSET_INLINE _GL_EXTERN_INLINE ++#include "gl_xset.h" +--- /dev/null ++++ b/lib/gl_xset.h +@@ -0,0 +1,81 @@ ++/* Abstract set data type, with out-of-memory checking. ++ Copyright (C) 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2009. ++ ++ 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 ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . */ ++ ++#ifndef _GL_XSET_H ++#define _GL_XSET_H ++ ++#include "gl_set.h" ++#include "xalloc.h" ++ ++#ifndef _GL_INLINE_HEADER_BEGIN ++ #error "Please include config.h first." ++#endif ++_GL_INLINE_HEADER_BEGIN ++#ifndef GL_XSET_INLINE ++# define GL_XSET_INLINE _GL_INLINE ++#endif ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* These functions are thin wrappers around the corresponding functions with ++ _nx_ infix from gl_set.h. Upon out-of-memory, they invoke xalloc_die (), ++ instead of returning an error indicator. */ ++#if 0 /* These are defined inline below. */ ++extern gl_set_t gl_set_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++ /*_GL_ATTRIBUTE_DEALLOC (gl_set_free, 1)*/ ++ _GL_ATTRIBUTE_RETURNS_NONNULL; ++extern bool gl_set_add (gl_set_t set, const void *elt); ++#endif ++ ++GL_XSET_INLINE ++/*_GL_ATTRIBUTE_DEALLOC (gl_set_free, 1)*/ ++_GL_ATTRIBUTE_RETURNS_NONNULL ++gl_set_t ++gl_set_create_empty (gl_set_implementation_t implementation, ++ gl_setelement_equals_fn equals_fn, ++ gl_setelement_hashcode_fn hashcode_fn, ++ gl_setelement_dispose_fn dispose_fn) ++{ ++ gl_set_t result = ++ gl_set_nx_create_empty (implementation, equals_fn, hashcode_fn, dispose_fn); ++ if (result == NULL) ++ xalloc_die (); ++ return result; ++} ++ ++GL_XSET_INLINE bool ++gl_set_add (gl_set_t set, const void *elt) ++{ ++ int result = gl_set_nx_add (set, elt); ++ if (result < 0) ++ xalloc_die (); ++ return result; ++} ++ ++#ifdef __cplusplus ++} ++#endif ++ ++_GL_INLINE_HEADER_END ++ ++#endif /* _GL_XSET_H */ +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -1106,6 +1106,14 @@ EXTRA_DIST += $(top_srcdir)/build-aux/gi + + ## end gnulib module gitlog-to-changelog + ++## Locally added gnulib modules ++ ++libgnu_a_SOURCES += \ ++ gl_hash_set.c \ ++ gl_set.c \ ++ gl_xset.c \ ++ # EOL ++ + ## begin gnulib module glob + + if GL_COND_OBJ_GLOB +--- /dev/null ++++ b/lib/gl_anyhash1.h +@@ -0,0 +1,31 @@ ++/* Hash table for sequential list, set, and map data type. ++ Copyright (C) 2006, 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of ++ gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, ++ gl_linkedhash_set.c, gl_hash_set.c, ++ gl_linkedhash_map.c, gl_hash_map.c. */ ++ ++/* Hash table entry. */ ++struct gl_hash_entry ++{ ++ struct gl_hash_entry *hash_next; /* chain of entries in same bucket */ ++ size_t hashcode; /* cache of the hash code of ++ - the key (for map data type) or ++ - the value (for list, set data types) */ ++}; ++typedef struct gl_hash_entry * gl_hash_entry_t; +--- /dev/null ++++ b/lib/gl_anyhash2.h +@@ -0,0 +1,82 @@ ++/* Hash table for sequential list, set, and map data type. ++ Copyright (C) 2006, 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++/* Common code of ++ gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c, ++ gl_linkedhash_set.c, gl_hash_set.c, ++ gl_linkedhash_map.c, gl_hash_map.c. */ ++ ++#include "gl_anyhash_primes.h" ++ ++/* Resizes the hash table with a new estimated size. */ ++static void ++hash_resize (CONTAINER_T container, size_t estimate) ++{ ++ size_t new_size = next_prime (estimate); ++ ++ if (new_size > container->table_size) ++ { ++ gl_hash_entry_t *old_table = container->table; ++ /* Allocate the new table. */ ++ gl_hash_entry_t *new_table; ++ size_t i; ++ ++ if (size_overflow_p (xtimes (new_size, sizeof (gl_hash_entry_t)))) ++ goto fail; ++ new_table = ++ (gl_hash_entry_t *) calloc (new_size, sizeof (gl_hash_entry_t)); ++ if (new_table == NULL) ++ goto fail; ++ ++ /* Iterate through the entries of the old table. */ ++ for (i = container->table_size; i > 0; ) ++ { ++ gl_hash_entry_t node = old_table[--i]; ++ ++ while (node != NULL) ++ { ++ gl_hash_entry_t next = node->hash_next; ++ /* Add the entry to the new table. */ ++ size_t bucket = node->hashcode % new_size; ++ node->hash_next = new_table[bucket]; ++ new_table[bucket] = node; ++ ++ node = next; ++ } ++ } ++ ++ container->table = new_table; ++ container->table_size = new_size; ++ free (old_table); ++ } ++ return; ++ ++ fail: ++ /* Just continue without resizing the table. */ ++ return; ++} ++ ++/* Resizes the hash table if needed, after CONTAINER_COUNT (container) was ++ incremented. */ ++static void ++hash_resize_after_add (CONTAINER_T container) ++{ ++ size_t count = CONTAINER_COUNT (container); ++ size_t estimate = xsum (count, count / 2); /* 1.5 * count */ ++ if (estimate > container->table_size) ++ hash_resize (container, estimate); ++} +--- /dev/null ++++ b/lib/gl_anyhash_primes.h +@@ -0,0 +1,87 @@ ++/* Table of primes, for use by hash tables. ++ Copyright (C) 2006, 2009-2023 Free Software Foundation, Inc. ++ Written by Bruno Haible , 2006. ++ ++ This file is free software: you can redistribute it and/or modify ++ it under the terms of the GNU Lesser General Public License as ++ published by the Free Software Foundation; either version 2.1 of the ++ License, or (at your option) any later version. ++ ++ This file is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with this program. If not, see . */ ++ ++/* Array of primes, approximately in steps of factor 1.2. ++ This table was computed by executing the Common Lisp expression ++ (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i)))) ++ and feeding the result to PARI/gp. */ ++static const size_t primes[] = ++ { ++ 11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199, ++ 239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543, ++ 3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899, ++ 22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849, ++ 140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887, ++ 723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277, ++ 3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307, ++ 13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233, ++ 47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469, ++ 171731387, 206077643, 247293161, 296751781, 356102141, 427322587, ++ 512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331, ++ 1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL, ++ 3810050851UL, ++#if SIZE_MAX > 4294967295UL ++ 4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL, ++ 11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL, ++ 28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL, ++ 70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL, ++ 146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL, ++ 302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL, ++ 628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL, ++ 1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL, ++ 2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL, ++ 5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL, ++ 11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL, ++ 24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL, ++ 49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL, ++ 103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL, ++ 214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL, ++ 445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL, ++ 923114351670013UL, 1107737222003791UL, 1329284666404567UL, ++ 1595141599685509UL, 1914169919622551UL, 2297003903547091UL, ++ 2756404684256459UL, 3307685621107757UL, 3969222745329323UL, ++ 4763067294395177UL, 5715680753274209UL, 6858816903929113UL, ++ 8230580284714831UL, 9876696341657791UL, 11852035609989371UL, ++ 14222442731987227UL, 17066931278384657UL, 20480317534061597UL, ++ 24576381040873903UL, 29491657249048679UL, 35389988698858471UL, ++ 42467986438630267UL, 50961583726356109UL, 61153900471627387UL, ++ 73384680565952851UL, 88061616679143347UL, 105673940014972061UL, ++ 126808728017966413UL, 152170473621559703UL, 182604568345871671UL, ++ 219125482015045997UL, 262950578418055169UL, 315540694101666193UL, ++ 378648832921999397UL, 454378599506399233UL, 545254319407679131UL, ++ 654305183289214771UL, 785166219947057701UL, 942199463936469157UL, ++ 1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL, ++ 1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL, ++ 3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL, ++ 5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL, ++ 10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL, ++ 17419784962119465179UL, ++#endif ++ SIZE_MAX /* sentinel, to ensure the search terminates */ ++ }; ++ ++/* Returns a suitable prime >= ESTIMATE. */ ++static size_t ++next_prime (size_t estimate) ++{ ++ size_t i; ++ ++ for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++) ++ if (primes[i] >= estimate) ++ return primes[i]; ++ return SIZE_MAX; /* not a prime, but better than nothing */ ++} diff -Nru inetutils-2.4/debian/patches/local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch inetutils-2.4/debian/patches/local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch --- inetutils-2.4/debian/patches/local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,149 @@ +From 920740d13e8d07b51c95c61e8a6c111027cfdd2f Mon Sep 17 00:00:00 2001 +From: Guillem Jover +Date: Mon, 23 Mar 2026 23:36:56 +0100 +Subject: [PATCH] telnet: Do not leak environment variables not marked for + export to telnetd + +A telnet server can read a client's environment variables with the +NEW-ENVIRON option and the SEND ENV_USERVAR command. + +This had previously been reported as CVE-2005-0488, but inetutils never +got a fix for it. + +Reported-by: Justin Swartz +Based-on-patch: https://gitlab.com/redhat/centos-stream/rpms/telnet/-/blob/c9s/telnet-0.17-env.patch +Link: https://www.openwall.com/lists/oss-security/2026/03/13/1 +Fixes: CVE-2026-32772 +Origin: vendor, Debian +Forwarded: no +--- + libtelnet/misc-proto.h | 4 +++- + telnet/authenc.c | 4 ++-- + telnet/commands.c | 5 +++-- + telnet/externs.h | 4 +++- + telnet/telnet.c | 10 +++++----- + 5 files changed, 16 insertions(+), 11 deletions(-) + +--- a/libtelnet/misc-proto.h ++++ b/libtelnet/misc-proto.h +@@ -68,6 +68,8 @@ + #ifndef __MISC_PROTO__ + # define __MISC_PROTO__ + ++#include ++ + void auth_encrypt_init (char *, char *, char *, char *, int); + void auth_encrypt_user (char *); + void auth_encrypt_connect (int); +@@ -79,6 +81,6 @@ void printd (unsigned char *, int); + int net_write (unsigned char *, int); + void net_encrypt (void); + int telnet_spin (void); +-char *telnet_getenv (char *); ++char *telnet_getenv (char *, bool); + char *telnet_gets (char *, char *, int, int); + #endif +--- a/telnet/authenc.c ++++ b/telnet/authenc.c +@@ -91,9 +91,9 @@ telnet_spin () + } + + char * +-telnet_getenv (char *val) ++telnet_getenv (char *val, bool exported_only) + { +- return ((char *) env_getvalue (val)); ++ return ((char *) env_getvalue (val, exported_only)); + } + + char * +--- a/telnet/commands.c ++++ b/telnet/commands.c +@@ -66,6 +66,7 @@ + #include + #include + ++#include + #include + #include /* LLONG_MAX for Solaris. */ + +@@ -2060,10 +2061,10 @@ env_default (int init, int welldefined) + } + + unsigned char * +-env_getvalue (const char *var) ++env_getvalue (const char *var, bool exported_only) + { + register struct env_lst *ep = env_find (var); +- if (ep) ++ if (ep && (!exported_only || ep->export)) + return (ep->value); + return (NULL); + } +--- a/telnet/externs.h ++++ b/telnet/externs.h +@@ -67,6 +67,7 @@ + # endif + #endif + ++#include + #include + #include + #if defined CRAY && !defined NO_BSD_SETJMP +@@ -331,7 +332,8 @@ env_opt (unsigned char *, int), + env_opt_start (void), + env_opt_start_info (void), env_opt_add (unsigned char *), env_opt_end (int); + +-extern unsigned char *env_default (int, int), *env_getvalue (const char *); ++extern unsigned char *env_default (int, int); ++extern unsigned char *env_getvalue (const char *, bool); + + int dosynch(const char *); + int get_status(const char *); +--- a/telnet/telnet.c ++++ b/telnet/telnet.c +@@ -496,7 +496,7 @@ dooption (int option) + #endif + + case TELOPT_XDISPLOC: /* X Display location */ +- if (env_getvalue ("DISPLAY")) ++ if (env_getvalue ("DISPLAY", false)) + new_state_ok = 1; + break; + +@@ -793,7 +793,7 @@ gettermname (void) + resettermname = 0; + if (tnamep && tnamep != unknown) + free (tnamep); +- if ((tname = (char *) env_getvalue ("TERM")) && ++ if ((tname = (char *) env_getvalue ("TERM", false)) && + (init_term (tname, &err) == 0)) + { + tnamep = mklist (termbuf, tname); +@@ -995,7 +995,7 @@ suboption (void) + unsigned char temp[50], *dp; + int len; + +- if ((dp = env_getvalue ("DISPLAY")) == NULL) ++ if ((dp = env_getvalue ("DISPLAY", false)) == NULL) + { + /* + * Something happened, we no longer have a DISPLAY +@@ -1731,7 +1731,7 @@ env_opt_add (register unsigned char *ep) + env_opt_add (ep); + return; + } +- vp = env_getvalue ((char *)ep); ++ vp = env_getvalue ((char *)ep, true); + if (opt_replyp + (vp ? strlen ((char *) vp) : 0) + + strlen ((char *) ep) + 6 > opt_replyend) + { +@@ -2488,7 +2488,7 @@ telnet (char *user) + send_will (TELOPT_LINEMODE, 1); + send_will (TELOPT_NEW_ENVIRON, 1); + send_do (TELOPT_STATUS, 1); +- if (env_getvalue ("DISPLAY")) ++ if (env_getvalue ("DISPLAY", false)) + send_will (TELOPT_XDISPLOC, 1); + if (eight) + tel_enter_binary (eight); diff -Nru inetutils-2.4/debian/patches/local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch inetutils-2.4/debian/patches/local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch --- inetutils-2.4/debian/patches/local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,165 @@ +From 2fbe8789599fb46844b03f45ce28af743528e767 Mon Sep 17 00:00:00 2001 +From: Guillem Jover +Date: Mon, 23 Mar 2026 23:38:13 +0100 +Subject: [PATCH] telnetd: Prevent user local privilege escalation using + --debug + +Do not try to open an existing hardcoded /tmp/telnet.debug file for +appending debug logging, as that is going to be fraught with security +issues. + +This would require at least making sure we do not follow symlinks, and +that the permissions and ownership are safe. But that would not prevent +a user pre-creating a file and then keeping it open, which means any +authentication logged would get snooped. + +Simply aborting on file existence during open, is not an option either +because then we can only serve a single client. And switching to log +all the debugging output, which amounts to the telnet protocol stream, +into syslog would leak user credentials, might be too verbose for +syslog, and would need to be sanitized somehow anyway. + +Instead, we switch to use a subdirectory under /run, where we write one +debug output file per server process, designated by its pid, in the form +of /run/telnet/debug., and make any error when setting this up +fatal. + +Partially-reported-by: Justin Swartz +Link: https://lists.gnu.org/r/bug-inetutils/2026-03/msg00040.html +Origin: vendor, Debian +Forwarded: no +--- + paths | 1 + + telnetd/Makefile.am | 3 ++- + telnetd/telnetd.c | 1 + + telnetd/telnetd.h | 2 ++ + telnetd/utility.c | 42 +++++++++++++++++++++++++++--------------- + 5 files changed, 33 insertions(+), 16 deletions(-) + +--- a/paths ++++ b/paths +@@ -78,6 +78,7 @@ PATH_FTPUSERS $(sysconfdir)/ftpusers + PATH_FTPCHROOT $(sysconfdir)/ftpchroot + PATH_FTPWELCOME $(sysconfdir)/ftpwelcome + PATH_FTPDPID $(runstatedir)/ftpd.pid ++PATH_TELNETDBGD $(runstatedir)/telnet + PATH_INETDCONF $(sysconfdir)/inetd.conf + PATH_INETDDIR $(sysconfdir)/inetd.d + PATH_INETDPID $(runstatedir)/inetd.pid +--- a/telnetd/Makefile.am ++++ b/telnetd/Makefile.am +@@ -22,7 +22,8 @@ AM_CPPFLAGS = \ + $(iu_INCLUDES) \ + -I$(top_srcdir) \ + $(INCAUTH) $(NCURSES_INCLUDE) \ +- $(PATHDEF_DEV) $(PATHDEF_TTY) $(PATHDEF_TTY_PFX) $(PATHDEF_LOGIN) ++ $(PATHDEF_DEV) $(PATHDEF_TTY) $(PATHDEF_TTY_PFX) $(PATHDEF_LOGIN) \ ++ $(PATHDEF_TELNETDBGD) + + LDADD = \ + $(top_builddir)/libtelnet/libtelnet.a \ +--- a/telnetd/telnetd.c ++++ b/telnetd/telnetd.c +@@ -185,6 +185,7 @@ parse_opt (int key, char *arg, struct ar + + case 'D': + parse_debug_level (arg); ++ debug_open (); + break; + + case 'E': +--- a/telnetd/telnetd.h ++++ b/telnetd/telnetd.h +@@ -288,6 +288,8 @@ extern void printdata (char *, char *, i + extern void printsub (int, unsigned char *, int); + extern void debug_output_datalen (const char *data, size_t len); + extern void debug_output_data (const char *fmt, ...); ++extern void debug_open (void); ++extern void debug_close (void); + + /* TTY functions */ + extern void init_termbuf (void); +--- a/telnetd/utility.c ++++ b/telnetd/utility.c +@@ -27,6 +27,7 @@ + # include + #endif + #include ++#include + + #if defined AUTHENTICATION || defined ENCRYPTION + # include +@@ -892,47 +893,58 @@ terminaltypeok (char *s) + /* Debugging support */ + + static FILE *debug_fp = NULL; ++static char *debug_file = NULL; + +-static int ++void + debug_open (void) + { +- int um = umask (077); ++ int fd; ++ ++ if (debug_fp) ++ return; ++ ++ if (mkdir (PATH_TELNETDBGD, 0700) < 0 && errno != EEXIST) ++ fatalperror (pty, "cannot create debug output directory"); ++ ++ if (asprintf (&debug_file, "%s/debug.%d", PATH_TELNETDBGD, getpid()) < 0) ++ fatalperror (pty, "cannot allocate debug output filename"); ++ ++ fd = open (debug_file, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0600); ++ if (fd < 0) ++ fatalperror (pty, "cannot open debug output file"); ++ ++ debug_fp = fdopen (fd, "a"); + if (!debug_fp) +- debug_fp = fopen ("/tmp/telnet.debug", "a"); +- umask (um); +- return debug_fp == NULL; ++ fatalperror (pty, "cannot associate stream to debug file descriptor"); ++ ++ setlinebuf (debug_fp); ++ ++ dprintf (pty, "Debug output log file is '%s'.\n", debug_file); + } + +-static int ++void + debug_close (void) + { + if (debug_fp) + fclose (debug_fp); ++ free (debug_file); + debug_fp = NULL; +- +- return 0; + } + + void + debug_output_datalen (const char *data, size_t len) + { +- if (debug_open ()) +- return; +- + fwrite (data, 1, len, debug_fp); +- debug_close (); + } + + void + debug_output_data (const char *fmt, ...) + { + va_list ap; +- if (debug_open ()) +- return; ++ + va_start (ap, fmt); + vfprintf (debug_fp, fmt, ap); + va_end (ap); +- debug_close (); + } + + /* diff -Nru inetutils-2.4/debian/patches/series inetutils-2.4/debian/patches/series --- inetutils-2.4/debian/patches/series 2026-01-21 16:42:52.000000000 +0000 +++ inetutils-2.4/debian/patches/series 2026-03-30 14:52:10.000000000 +0000 @@ -3,8 +3,14 @@ 0002-ftpd-rcp-rlogin-rsh-rshd-uucpd-fix-check-set-id-retu.patch upstream/0001-Fix-injection-bug-with-bogus-user-names.patch upstream/0002-telnetd-Sanitize-all-variable-expansions.patch +upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch +upstream/0004-telnetd-add-the-new-accept-env-option.patch +upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch # Local patches 0001-build-Disable-GFDL-info-files-and-useless-man-pages.patch 0002-build-Use-runstatedir-for-run-directory.patch 0003-inetd-Change-protocol-semantics-in-inetd.conf.patch 0004-Use-krb5_auth_con_getsendsubkey-instead-of-krb5_auth.patch +local/0007-gnulib-update.patch +local/0008-telnet-Do-not-leak-environment-variables-not-marked-.patch +local/0009-telnetd-Prevent-user-local-privilege-escalation-usin.patch diff -Nru inetutils-2.4/debian/patches/upstream/0001-Fix-injection-bug-with-bogus-user-names.patch inetutils-2.4/debian/patches/upstream/0001-Fix-injection-bug-with-bogus-user-names.patch --- inetutils-2.4/debian/patches/upstream/0001-Fix-injection-bug-with-bogus-user-names.patch 2026-01-21 16:42:52.000000000 +0000 +++ inetutils-2.4/debian/patches/upstream/0001-Fix-injection-bug-with-bogus-user-names.patch 2026-03-30 14:52:10.000000000 +0000 @@ -9,14 +9,12 @@ Signed-off-by: Simon Josefsson --- - telnetd/utility.c | 9 ++++++++- + telnetd/utility.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) -diff --git a/telnetd/utility.c b/telnetd/utility.c -index 534d683a..4a56622d 100644 --- a/telnetd/utility.c +++ b/telnetd/utility.c -@@ -1733,7 +1733,14 @@ _var_short_name (struct line_expander *exp) +@@ -1734,7 +1734,14 @@ _var_short_name (struct line_expander *e return user_name ? xstrdup (user_name) : NULL; case 'U': @@ -32,6 +30,3 @@ default: exp->state = EXP_STATE_ERROR; --- -2.51.0 - diff -Nru inetutils-2.4/debian/patches/upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch inetutils-2.4/debian/patches/upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch --- inetutils-2.4/debian/patches/upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/upstream/0001-telnetd-don-t-allow-systemd-service-credentials.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,54 @@ +From 4db2f19f4caac03c7f4da6363c140bd70df31386 Mon Sep 17 00:00:00 2001 +From: Erik Auerswald +Date: Sun, 15 Feb 2026 15:38:50 +0100 +Subject: [PATCH] telnetd: don't allow systemd service credentials + +The login(1) implementation of util-linux added support for +systemd service credentials in release 2.40. This allows to +bypass authentication by specifying a directory name in the +environment variable CREDENTIALS_DIRECTORY. If this directory +contains a file named 'login.noauth' with the content of 'yes', +login(1) skips authentication. + +GNU Inetutils telnetd supports to set arbitrary environment +variables using the 'Environment' and 'New Environment' +Telnet options. This allows specifying a directory containing +'login.noauth'. A local user can create such a directory +and file, and, e.g., specify the user name 'root' to escalate +privileges. + +This problem was reported by Ron Ben Yizhak in +. + +This commit clears CREDENTIALS_DIRECTORY from the environment +before executing login(1) to implement a simple fix that can +be backported easily. + +* telnetd/pty.c: Clear CREDENTIALS_DIRECTORY from the environment +before executing 'login'. + +Reported-by: Ron Ben Yizhak +Fixes: CVE-2026-28372 +Origin: upstream, commit:4db2f19f4caac03c7f4da6363c140bd70df31386 +Forwarded: not-needed +--- + telnetd/pty.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +--- a/telnetd/pty.c ++++ b/telnetd/pty.c +@@ -130,6 +130,14 @@ start_login (char *host, int autologin, + if (!cmd) + fatal (net, "can't expand login command line"); + argcv_get (cmd, "", &argc, &argv); ++ ++ /* util-linux's "login" introduced an authentication bypass method ++ * via environment variable "CREDENTIALS_DIRECTORY" in version 2.40. ++ * Clear it from the environment before executing "login" to prevent ++ * abuse via Telnet. ++ */ ++ unsetenv ("CREDENTIALS_DIRECTORY"); ++ + execv (argv[0], argv); + syslog (LOG_ERR, "%s: %m\n", cmd); + fatalperror (net, cmd); diff -Nru inetutils-2.4/debian/patches/upstream/0002-telnetd-Sanitize-all-variable-expansions.patch inetutils-2.4/debian/patches/upstream/0002-telnetd-Sanitize-all-variable-expansions.patch --- inetutils-2.4/debian/patches/upstream/0002-telnetd-Sanitize-all-variable-expansions.patch 2026-01-21 16:42:52.000000000 +0000 +++ inetutils-2.4/debian/patches/upstream/0002-telnetd-Sanitize-all-variable-expansions.patch 2026-03-30 14:52:10.000000000 +0000 @@ -6,14 +6,12 @@ * telnetd/utility.c (sanitize): New function. (_var_short_name): Use it for all variables. --- - telnetd/utility.c | 32 ++++++++++++++++++-------------- + telnetd/utility.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) -diff --git a/telnetd/utility.c b/telnetd/utility.c -index 4a56622d..1e7adb08 100644 --- a/telnetd/utility.c +++ b/telnetd/utility.c -@@ -1684,6 +1684,17 @@ static void _expand_cond (struct line_expander *exp); +@@ -1685,6 +1685,17 @@ static void _expand_cond (struct line_ex static void _skip_block (struct line_expander *exp); static void _expand_block (struct line_expander *exp); @@ -31,7 +29,7 @@ /* Expand a variable referenced by its short one-symbol name. Input: exp->cp points to the variable name. FIXME: not implemented */ -@@ -1710,13 +1721,13 @@ _var_short_name (struct line_expander *exp) +@@ -1711,13 +1722,13 @@ _var_short_name (struct line_expander *e return xstrdup (timebuf); case 'h': @@ -48,7 +46,7 @@ case 't': q = strchr (line + 1, '/'); -@@ -1724,23 +1735,16 @@ _var_short_name (struct line_expander *exp) +@@ -1725,23 +1736,16 @@ _var_short_name (struct line_expander *e q++; else q = line; @@ -76,6 +74,3 @@ default: exp->state = EXP_STATE_ERROR; --- -2.51.0 - diff -Nru inetutils-2.4/debian/patches/upstream/0004-telnetd-add-the-new-accept-env-option.patch inetutils-2.4/debian/patches/upstream/0004-telnetd-add-the-new-accept-env-option.patch --- inetutils-2.4/debian/patches/upstream/0004-telnetd-add-the-new-accept-env-option.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/upstream/0004-telnetd-add-the-new-accept-env-option.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,220 @@ +From d46bd3ccd013e5f0d251862cadab71ba4d4c2caa Mon Sep 17 00:00:00 2001 +From: Collin Funk +Date: Thu, 5 Mar 2026 21:35:22 -0800 +Subject: [PATCH 4/5] telnetd: add the new --accept-env option + +This changes telnetd to ignore all environment options from clients +unless the variable was listed by an --accept-env option. This +mitigates the many ways to escalate privileges using environment +variables. + +* NEWS.md: Mention the change. +* bootstrap.conf (gnulib_modules): Add hashcode-string1, hash-set, and +xset. +* doc/inetutils.texi (telnetd invocation): Mention the new option. +* telnetd/pty.c (scrub_env): Remove function. +(start_login): Remove call to scrub_env. Remove unsetenv call that is +no longer needed. +* telnetd/state.c (suboption): Check for the environment variable in +accept_env_set before making changes to the environment. +* telnetd/telnetd.c (accept_env_set): New variable. +(string_hashcode, string_equals): New function needed for +gl_set_create_empty. +(ACCEPT_ENV_OPTION): New definition. +(argp_options): Add the --accept-env option. +(parse_opt): Process the new option. +(telnetd_setup): Clear the environment before processing options. +* telnetd/telnetd.h: Include gl_hash_set.h, gl_xset.h, and +hashcode-string1.h. +(accept_env_set): New declaration. + +Origin: upstream, commit:81d436d26d5497423e28841af91756e373446cf4 +Forwarded: not-needed +--- + telnetd/pty.c | 31 ------------------------------- + telnetd/state.c | 22 ++++++++++++++-------- + telnetd/telnetd.c | 43 ++++++++++++++++++++++++++++++++++++------- + telnetd/telnetd.h | 4 ++++ + 4 files changed, 54 insertions(+), 46 deletions(-) + +--- a/telnetd/pty.c ++++ b/telnetd/pty.c +@@ -84,28 +84,6 @@ startslave (char *host, int autologin, c + } + + extern char **environ; +-/* +- * scrub_env() +- * +- * Remove a few things from the environment that +- * don't need to be there. +- * +- * Security fix included in telnet-95.10.23.NE of David Borman . +- */ +-static void +-scrub_env (void) +-{ +- register char **cpp, **cpp2; +- +- for (cpp2 = cpp = environ; *cpp; cpp++) +- { +- if (strncmp (*cpp, "LD_", 3) +- && strncmp (*cpp, "_RLD_", 5) +- && strncmp (*cpp, "LIBPATH=", 8) && strncmp (*cpp, "IFS=", 4)) +- *cpp2++ = *cpp; +- } +- *cpp2 = 0; +-} + + void + start_login (char *host, int autologin, char *name) +@@ -118,8 +96,6 @@ start_login (char *host, int autologin, + (void) autologin; + (void) name; + +- scrub_env (); +- + /* Set the environment variable "LINEMODE" to indicate our linemode */ + if (lmodetype == REAL_LINEMODE) + setenv ("LINEMODE", "real", 1); +@@ -131,13 +107,6 @@ start_login (char *host, int autologin, + fatal (net, "can't expand login command line"); + argcv_get (cmd, "", &argc, &argv); + +- /* util-linux's "login" introduced an authentication bypass method +- * via environment variable "CREDENTIALS_DIRECTORY" in version 2.40. +- * Clear it from the environment before executing "login" to prevent +- * abuse via Telnet. +- */ +- unsetenv ("CREDENTIALS_DIRECTORY"); +- + execv (argv[0], argv); + syslog (LOG_ERR, "%s: %m\n", cmd); + fatalperror (net, cmd); +--- a/telnetd/state.c ++++ b/telnetd/state.c +@@ -1494,10 +1494,13 @@ suboption (void) + case NEW_ENV_VAR: + case ENV_USERVAR: + *cp = '\0'; +- if (valp) +- setenv (varp, valp, 1); +- else +- unsetenv (varp); ++ if (accept_env_set && gl_set_search (accept_env_set, varp)) ++ { ++ if (valp) ++ setenv (varp, valp, 1); ++ else ++ unsetenv (varp); ++ } + cp = varp = (char *) subpointer; + valp = 0; + break; +@@ -1513,10 +1516,13 @@ suboption (void) + } + } + *cp = '\0'; +- if (valp) +- setenv (varp, valp, 1); +- else +- unsetenv (varp); ++ if (accept_env_set && gl_set_search (accept_env_set, varp)) ++ { ++ if (valp) ++ setenv (varp, valp, 1); ++ else ++ unsetenv (varp); ++ } + break; + } /* end of case TELOPT_NEW_ENVIRON */ + #if defined AUTHENTICATION +--- a/telnetd/telnetd.c ++++ b/telnetd/telnetd.c +@@ -108,9 +108,32 @@ char *terminaltype; + int SYNCHing; /* we are in TELNET SYNCH mode */ + struct telnetd_clocks clocks; + ++/* Set of environment variables that we do not remove from clients. */ ++gl_set_t accept_env_set = NULL; ++ ++static size_t ++string_hashcode (const void *s) ++{ ++ return hash_string (s, strlen (s)); ++} ++ ++static bool ++string_equals (const void *a, const void *b) ++{ ++ return strcmp (a, b) == 0; ++} ++ ++/* List of long options without short option counterparts. */ ++enum ++{ ++ ACCEPT_ENV_OPTION = UCHAR_MAX + 1 ++}; ++ + + static struct argp_option argp_options[] = { + #define GRID 10 ++ {"accept-env", ACCEPT_ENV_OPTION, "NAME", 0, ++ "accept the environment variable from clients", GRID}, + { "debug", 'D', "LEVEL", OPTION_ARG_OPTIONAL, + "set debugging level", GRID }, + { "exec-login", 'E', "STRING", 0, +@@ -146,6 +169,14 @@ parse_opt (int key, char *arg, struct ar + { + switch (key) + { ++ ++ case ACCEPT_ENV_OPTION: ++ if (!accept_env_set) ++ accept_env_set = gl_set_create_empty (GL_HASH_SET, string_equals, ++ string_hashcode, NULL); ++ gl_set_add (accept_env_set, arg); ++ break; ++ + #ifdef AUTHENTICATION + case 'a': + parse_authmode (arg); +@@ -502,13 +533,11 @@ telnetd_setup (int fd) + + io_setup (); + +- /* Before doing anything related to the identity of the client, +- * scrub the environment variable USER, since it may be set with +- * an irrelevant user name at this point. OpenBSD has been known +- * to offend at this point with their own inetd. Any demand for +- * autologin will get attention in getterminaltype(). +- */ +- unsetenv ("USER"); ++ /* Clear the environment of all variables before doing anything. This avoids ++ many ways of escalating privileges. Environment variable options sent by ++ the client will be checked against ACCEPT_ENV_SET. */ ++ static char *dummy_environ[] = { NULL }; ++ environ = dummy_environ; + + /* get terminal type. */ + uname[0] = 0; +--- a/telnetd/telnetd.h ++++ b/telnetd/telnetd.h +@@ -58,6 +58,9 @@ + #define obstack_chunk_free free + #include + ++#include "gl_hash_set.h" ++#include "gl_xset.h" ++#include "hashcode-string1.h" + #include "xalloc.h" + + #ifndef HAVE_CC_T +@@ -252,6 +255,7 @@ extern char *user_name; + extern int pty, net; + extern int SYNCHing; /* we are in TELNET SYNCH mode */ + extern struct telnetd_clocks clocks; ++extern gl_set_t accept_env_set; + extern char line[]; + + extern char *xstrdup (const char *); diff -Nru inetutils-2.4/debian/patches/upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch inetutils-2.4/debian/patches/upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch --- inetutils-2.4/debian/patches/upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch 1970-01-01 00:00:00.000000000 +0000 +++ inetutils-2.4/debian/patches/upstream/0005-telnetd-fix-stack-buffer-overflow-processing-SLC-sub.patch 2026-03-30 14:52:10.000000000 +0000 @@ -0,0 +1,35 @@ +From 6a2c3aa5c4ca5f09bdb6fdb28d9d369432506f3f Mon Sep 17 00:00:00 2001 +From: Collin Funk +Date: Wed, 11 Mar 2026 23:06:46 -0700 +Subject: [PATCH 5/5] telnetd: fix stack buffer overflow processing SLC + suboption triplets + +Previously a client could write past the end of an internal buffer using +an SLC suboption with many triplets using function octets greater than +18, possibly leading to remote code execution. Reported by Adiel Sol, +Arad Inbar, Erez Cohen, Nir Somech, Ben Grinberg, Daniel Lubel at DREAM +Security Research Team at: +. + +* telnetd/slc.c (add_slc): Return early if writing the tuple would lead +us to writing past the end of the buffer. + +Fixes: CVE-2026-32746 +Origin: upstream, commit:95751794e3da2eebd605238ddbff2232b68edb5f +Forwarded: not-needed +--- + telnetd/slc.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/telnetd/slc.c ++++ b/telnetd/slc.c +@@ -162,6 +162,9 @@ get_slc_defaults (void) + void + add_slc (register char func, register char flag, register cc_t val) + { ++ /* Do nothing if the entire triplet cannot fit in the buffer. */ ++ if (slcbuf + sizeof slcbuf - slcptr <= 6) ++ return; + + if ((*slcptr++ = (unsigned char) func) == 0xff) + *slcptr++ = 0xff;