From d6b8b83aa51616946fd314bc48087312d13c99f8 Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.funk1@gmail.com>
Date: Thu, 26 Mar 2026 22:52:54 -0700
Subject: telnet: don't leak the value of unexported environment variables

Patch based on the following OpenBSD commit:
<https://github.com/openbsd/src/commit/1a11dc7253488a97d6df686dae9230f78682e8df>

* telnet/commands.c (env_getvalue): Add a boolean argument to prevent
prevent unexported variables from being returned.
* telnet/externs.h (env_getvalue): Adjust the function declaration.
* telnet/authenc.c (telnet_getenv): Add the new argument.
* telnet/telnet.c (dooption, gettermname, suboption, env_opt_add)
(telnet): Likewise.

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 <justin.swartz@risingedge.co.za>
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

CVE: CVE-2026-32772
Upstream-Status: Backport [https://cgit.git.savannah.gnu.org/cgit/inetutils.git/patch/?id=d6b8b83aa51616946fd314bc48087312d13c99f8]
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
 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(-)

diff --git a/libtelnet/misc-proto.h b/libtelnet/misc-proto.h
index abf8316..a836a69 100644
--- a/libtelnet/misc-proto.h
+++ b/libtelnet/misc-proto.h
@@ -68,6 +68,8 @@
 #ifndef __MISC_PROTO__
 # define __MISC_PROTO__
 
+#include <stdbool.h>
+
 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
diff --git a/telnet/authenc.c b/telnet/authenc.c
index b019251..dcd19e8 100644
--- 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 *
diff --git a/telnet/commands.c b/telnet/commands.c
index 2a133c9..d8d0864 100644
--- a/telnet/commands.c
+++ b/telnet/commands.c
@@ -66,6 +66,7 @@
 #include <stdarg.h>
 #include <errno.h>
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <limits.h>		/* LLONG_MAX for Solaris. */
 
@@ -2059,10 +2060,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);
 }
diff --git a/telnet/externs.h b/telnet/externs.h
index f79c6ae..e0d9fbc 100644
--- a/telnet/externs.h
+++ b/telnet/externs.h
@@ -67,6 +67,7 @@
 # endif
 #endif
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <setjmp.h>
 #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 *);
diff --git a/telnet/telnet.c b/telnet/telnet.c
index 8884b6e..6a5cf8b 100644
--- 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);
@@ -992,7 +992,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
@@ -1727,7 +1727,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)
     {
@@ -2484,7 +2484,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);
-- 
2.50.1

