A new command line option, -W or --accept-env, has been added to
allow administrators to extend the default whitelist with additional
environment variable patterns.

This feature is requires opt-in at compile time and must be enabled
using the configure flag: --enable-accept-env

This is useful for systems that require specific variables to be
passed to the login process, which would otherwise be dropped by
the default whitelist. Any variables allowed via this option are
still subject to the global path-traversal sanitization implemented
in the previous patch.

* configure.ac: Add --enable-accept-env argument and define
ENABLE_ACCEPT_ENV macro.
* telnetd/telnetd.h: Add prototype for add_allowed_env_pattern()
guarded by ENABLE_ACCEPT_ENV.
* telnetd/telnetd.c (argp_options): Add the "W"/"accept-env" option
guarded by ENABLE_ACCEPT_ENV.
(parse_opt): Handle the 'W' case by calling add_allowed_env_pattern(),
guarded by ENABLE_ACCEPT_ENV.
* telnetd/utility.c: Declare user_env_vars storage and extend
is_env_var_allowed() to check user patterns first, guarding all
additions with ENABLE_ACCEPT_ENV.
---
 configure.ac      |  8 ++++++++
 telnetd/telnetd.c | 10 ++++++++++
 telnetd/telnetd.h |  4 ++++
 telnetd/utility.c | 42 ++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index ac6e6c59..89f73929 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,14 @@ AC_ARG_ENABLE([authentication],
               [test -z "$KERBEROS_VERSION" && 
AC_MSG_ERROR([--enable-authentication is given, but a Kerberos version is not 
provided])],
              [test -z "$KERBEROS_VERSION" && enable_authentication=no])
 
+AC_ARG_ENABLE([accept-env],
+              AS_HELP_STRING([--enable-accept-env],
+                             [Enable the --accept-env option in telnetd]),
+              [if test "$enableval" = yes; then
+                 AC_DEFINE([ENABLE_ACCEPT_ENV], 1, [Define as 1 to enable the 
--accept-env option in telnetd])
+              fi],
+              [enable_accept_env=no])
+
 if test -n "$KERBEROS_VERSION"; then
   test -n "$enable_authentication" || enable_authentication=yes
   test -n "$enable_encryption" || enable_encryption=yes
diff --git a/telnetd/telnetd.c b/telnetd/telnetd.c
index affa2c96..acb6ce08 100644
--- a/telnetd/telnetd.c
+++ b/telnetd/telnetd.c
@@ -117,6 +117,10 @@ static struct argp_option argp_options[] = {
    "do not print host information before login has been completed", GRID},
   {"linemode", 'l', "MODE", OPTION_ARG_OPTIONAL,
    "set line mode", GRID},
+#ifdef ENABLE_ACCEPT_ENV
+  {"accept-env", 'W', "PATTERN", 0,
+   "specify an additional environment variable pattern to accept", GRID},
+#endif
   {"no-keepalive", 'n', NULL, 0,
    "disable TCP keep-alive", GRID},
   {"reverse-lookup", 'U', NULL, 0,
@@ -180,6 +184,12 @@ parse_opt (int key, char *arg, struct argp_state *state 
MAYBE_UNUSED)
       reverse_lookup = 1;
       break;
 
+#ifdef ENABLE_ACCEPT_ENV
+    case 'W':
+      add_allowed_env_pattern (arg);
+      break;
+#endif
+
 #ifdef AUTHENTICATION
     case 'X':
       auth_disable_name (arg);
diff --git a/telnetd/telnetd.h b/telnetd/telnetd.h
index 5ee59e6e..259b3126 100644
--- a/telnetd/telnetd.h
+++ b/telnetd/telnetd.h
@@ -319,6 +319,10 @@ extern char *expand_line (const char *fmt);
 extern void exorcise_env (void);
 extern int is_env_var_allowed (const char *var, const char *val);
 
+#ifdef ENABLE_ACCEPT_ENV
+extern void add_allowed_env_pattern (const char *pattern);
+#endif
+
 
 /*  FIXME */
 extern void _termstat (void);
diff --git a/telnetd/utility.c b/telnetd/utility.c
index cabdcdfd..c9d38867 100644
--- a/telnetd/utility.c
+++ b/telnetd/utility.c
@@ -75,6 +75,12 @@ static int pcc;
 
 extern int not42;
 
+#ifdef ENABLE_ACCEPT_ENV
+# define MAX_USER_ENV_VARS 16
+static const char *user_env_vars[MAX_USER_ENV_VARS];
+static size_t user_env_var_count = 0;
+#endif
+
 /* A default whitelist for environment variables. */
 static const char *allowed_env_vars[] = {
   "USER",
@@ -90,15 +96,30 @@ is_env_var_allowed (const char *var, const char *val)
 {
   const char **p;
   int allowed = 0;
+#ifdef ENABLE_ACCEPT_ENV
+  size_t i;
 
-  for (p = allowed_env_vars; *p; p++)
+  for (i = 0; i < user_env_var_count; i++)
     {
-      if (fnmatch (*p, var, FNM_NOESCAPE) == 0)
+      if (fnmatch (user_env_vars[i], var, FNM_NOESCAPE) == 0)
         {
           allowed = 1;
           break;
         }
     }
+#endif
+
+  if (!allowed)
+    {
+      for (p = allowed_env_vars; *p; p++)
+        {
+          if (fnmatch (*p, var, FNM_NOESCAPE) == 0)
+            {
+              allowed = 1;
+              break;
+            }
+        }
+    }
 
   if (!allowed)
     return 0;
@@ -118,6 +139,23 @@ is_env_var_allowed (const char *var, const char *val)
   return 1;
 }
 
+#ifdef ENABLE_ACCEPT_ENV
+void
+add_allowed_env_pattern (const char *pattern)
+{
+  if (!pattern || *pattern == 0)
+    return;
+
+  if (user_env_var_count >= MAX_USER_ENV_VARS)
+    {
+      syslog (LOG_NOTICE, "Ignoring --accept-env option: limit reached");
+      return;
+    }
+
+  user_env_vars[user_env_var_count++] = pattern;
+}
+#endif
+
 void
 exorcise_env (void)
 {
-- 


Reply via email to