From baeb17eb76772a2dbbcc4d52a22ddfc405276db1 Mon Sep 17 00:00:00 2001
From: Alex Wilson <alex.wilson@joyent.com>
Date: Fri, 4 Sep 2015 11:04:30 -0700
Subject: [PATCH 29/34] Accept LANG and LC_* environment variables from clients
 by default

This preserves most of the old SunSSH locale negotiation
behaviour (at least the parts that are most commonly used).

diff -wpruN --no-dereference '--exclude=*.orig' a~/servconf.c a/servconf.c
--- a~/servconf.c	1970-01-01 00:00:00
+++ a/servconf.c	1970-01-01 00:00:00
@@ -201,6 +201,7 @@ initialize_server_options(ServerOptions
 	options->client_alive_count_max = -1;
 	options->num_authkeys_files = 0;
 	options->num_accept_env = 0;
+	options->default_accept_env = 1;
 	options->num_setenv = 0;
 	options->permit_tun = -1;
 	options->permitted_opens = NULL;
@@ -536,6 +537,32 @@ fill_default_server_options(ServerOption
 		options->max_sessions = DEFAULT_SESSIONS_MAX;
 	if (options->use_dns == -1)
 		options->use_dns = 0;
+	if (options->default_accept_env == 1) {
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LANG");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_ALL");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_CTYPE");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_COLLATE");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_TIME");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_NUMERIC");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_MONETARY");
+		opt_array_append("[default]", 0, "AcceptEnv",
+		    &options->accept_env, &options->num_accept_env,
+		    "LC_MESSAGES");
+	}
 	if (options->client_alive_interval == -1)
 		options->client_alive_interval = 0;
 	if (options->client_alive_count_max == -1)
@@ -2329,9 +2356,12 @@ process_server_config_line_depth(ServerO
 			if (*arg == '\0' || strchr(arg, '=') != NULL)
 				fatal("%s line %d: Invalid environment name.",
 				    filename, linenum);
+			options->default_accept_env = 0;
 			found = 1;
 			if (!*activep)
 				continue;
+			if (strcmp(arg, "none") == 0)
+				continue;
 			opt_array_append(filename, linenum, keyword,
 			    &options->accept_env, &options->num_accept_env,
 			    arg);
diff -wpruN --no-dereference '--exclude=*.orig' a~/servconf.h a/servconf.h
--- a~/servconf.h	1970-01-01 00:00:00
+++ a/servconf.h	1970-01-01 00:00:00
@@ -179,6 +179,7 @@ typedef struct {
 	char   **subsystem_args;
 
 	u_int num_accept_env;
+	int    default_accept_env;
 	char   **accept_env;
 	u_int num_setenv;
 	char   **setenv;
diff -wpruN --no-dereference '--exclude=*.orig' a~/session.c a/session.c
--- a~/session.c	1970-01-01 00:00:00
+++ a/session.c	1970-01-01 00:00:00
@@ -831,6 +831,18 @@ check_quietlogin(Session *s, const char
 }
 
 /*
+ * If the given environment variable is set in the daemon's environment,
+ * push it into the new child as well. If it is unset, do nothing.
+ */
+static void
+child_inherit_env(char ***envp, u_int *envsizep, const char *name)
+{
+	char *value;
+	if ((value = getenv(name)) != NULL)
+		child_set_env(envp, envsizep, name, value);
+}
+
+/*
  * Reads environment variables from the given file and adds/overrides them
  * into the environment.  If the file does not exist, this does nothing.
  * Otherwise, it must consist of empty lines, comments (line starts with '#')
@@ -1014,6 +1026,16 @@ do_setup_env(struct ssh *ssh, Session *s
 	ssh_gssapi_do_child(&env, &envsize);
 #endif
 
+	/* Default to the system-wide locale/language settings. */
+	child_inherit_env(&env, &envsize, "LANG");
+	child_inherit_env(&env, &envsize, "LC_ALL");
+	child_inherit_env(&env, &envsize, "LC_CTYPE");
+	child_inherit_env(&env, &envsize, "LC_COLLATE");
+	child_inherit_env(&env, &envsize, "LC_TIME");
+	child_inherit_env(&env, &envsize, "LC_NUMERIC");
+	child_inherit_env(&env, &envsize, "LC_MONETARY");
+	child_inherit_env(&env, &envsize, "LC_MESSAGES");
+
 	/* Set basic environment. */
 	for (i = 0; i < s->num_env; i++)
 		child_set_env(&env, &envsize, s->env[i].name, s->env[i].val);
@@ -1057,8 +1079,7 @@ do_setup_env(struct ssh *ssh, Session *s
 	/* Normal systems set SHELL by default. */
 	child_set_env(&env, &envsize, "SHELL", shell);
 
-	if (getenv("TZ"))
-		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+	child_inherit_env(&env, &envsize, "TZ");
 	if (s->term)
 		child_set_env(&env, &envsize, "TERM", s->term);
 	if (s->display)
diff -wpruN --no-dereference '--exclude=*.orig' a~/sshd_config a/sshd_config
--- a~/sshd_config	1970-01-01 00:00:00
+++ a/sshd_config	1970-01-01 00:00:00
@@ -26,6 +26,10 @@
 #SyslogFacility AUTH
 #LogLevel INFO
 
+# Use the client's locale/language settings
+#AcceptEnv LANG LC_ALL LC_CTYPE LC_COLLATE LC_TIME LC_NUMERIC
+#AcceptEnv LC_MONETARY LC_MESSAGES
+
 # Authentication:
 
 #LoginGraceTime 2m
diff -wpruN --no-dereference '--exclude=*.orig' a~/sshd_config.5 a/sshd_config.5
--- a~/sshd_config.5	1970-01-01 00:00:00
+++ a/sshd_config.5	1970-01-01 00:00:00
@@ -86,7 +86,20 @@ directives.
 Be warned that some environment variables could be used to bypass restricted
 user environments.
 For this reason, care should be taken in the use of this directive.
-The default is not to accept any environment variables.
+The default is to accept only
+.Ev LANG
+and the
+.Ev LC_*
+family of environment variables. If any
+.Cm AcceptEnv
+directives are present in your config file, they will replace this default
+.Po
+ie, only the variables you list will be passed into the session's
+.Xr environ 7
+.Pc .
+You can also use an argument of
+.Dq none
+to specify that no environment variables should be passed.
 .It Cm AddressFamily
 Specifies which address family should be used by
 .Xr sshd 8 .
