From 82b8a93f6a1b13d1136aca6fc759b0b0f84913a8 Mon Sep 17 00:00:00 2001 From: David Grieser Date: Mon, 12 Dec 2022 21:45:53 +0100 Subject: [PATCH 1/2] Added environment variable support. --- chansession.h | 4 +++ runopts.h | 2 ++ svr-chansession.c | 62 +++++++++++++++++++++++++++++++++++++++++++---- svr-runopts.c | 5 ++++ 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/chansession.h b/chansession.h index cf4fba36e..56587b9d6 100644 --- a/chansession.h +++ b/chansession.h @@ -75,6 +75,10 @@ struct ChanSess { char * agentfile; char * agentdir; #endif + + unsigned env_capacity; + unsigned env_size; + char **env; }; struct ChildPid { diff --git a/runopts.h b/runopts.h index d44283d6b..725c47d60 100644 --- a/runopts.h +++ b/runopts.h @@ -136,6 +136,8 @@ typedef struct svr_runopts { char *pubkey_plugin_options; #endif + char *acceptenv; /* Comma-separated. */ + int pass_on_env; } svr_runopts; diff --git a/svr-chansession.c b/svr-chansession.c index 656a96825..5c3991d03 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -62,6 +62,7 @@ static void send_msg_chansess_exitstatus(const struct Channel * channel, static void send_msg_chansess_exitsignal(const struct Channel * channel, const struct ChanSess * chansess); static void get_termmodes(const struct ChanSess *chansess); +static char *create_newvar(const char *param, const char *var); const struct ChanType svrchansess = { "session", /* name */ @@ -326,6 +327,12 @@ static void cleanupchansess(const struct Channel *channel) { m_free(chansess->term); m_free(chansess->original_command); + if (chansess->env) { + char **p = chansess->env; + for (; *p; free(*p++)) {} + m_free(chansess->env); + } + if (chansess->tty) { /* write the utmp/wtmp login record */ li = chansess_login_alloc(chansess); @@ -360,6 +367,25 @@ static void cleanupchansess(const struct Channel *channel) { TRACE(("leave closechansess")) } +static char is_env_allowed(const char *name) { + char const *p = svr_opts.acceptenv, *q; + if (!name || name[0] == '\0' || strchr(name, '=')) return 0; + /* Comma-separated, LC_ means prefix, TZ= means 1 var. */ + if (p) for (; *p != '\0'; ) { + for (q = p; *q != '\0' && *q != ','; ++q) {} + if (p != q) { + if (q[-1] == '=') { + const unsigned size = q - p - 1; + if (0 == memcmp(name, p, size) && name[size] == '\0') return 1; + } else { + if (0 == strncmp(name, p, q - p)) return 1; /* Prefix match. */ + } + } + p = q + 1; + } + return 0; +} + /* Handle requests for a channel. These can be execution requests, * or x11/authagent forwarding. These are passed to appropriate handlers */ static void chansessionrequest(struct Channel *channel) { @@ -404,8 +430,25 @@ static void chansessionrequest(struct Channel *channel) { #endif } else if (strcmp(type, "signal") == 0) { ret = sessionsignal(chansess); + } else if (strcmp(type, "env") == 0) { + char *name = buf_getstring(ses.payload, NULL); + char *value = buf_getstring(ses.payload, NULL); + if (name && value && is_env_allowed(name)) { + char *newvar = create_newvar(name, value); + if (chansess->env_size + 1 >= chansess->env_capacity) { + if (chansess->env) { + chansess->env = m_realloc(chansess->env, sizeof(*chansess->env) * (chansess->env_capacity <<= 1)); + } else { + chansess->env = m_malloc(sizeof(*chansess->env) * (chansess->env_capacity = 16)); + } + } + chansess->env[chansess->env_size++] = newvar; /* Takes ownership. */ + chansess->env[chansess->env_size] = NULL; + } + m_free(value); + m_free(name); } else { - /* etc, todo "env", "subsystem" */ + /* etc */ } out: @@ -1007,6 +1050,12 @@ static void execchild(const void *user_data) { } #endif + /* Call putenv here first, so "USER" etc. below will override it. */ + if (chansess->env) { + char **p = chansess->env; + for (; *p; putenv(*p++)) {} + } + /* set env vars */ addnewvar("USER", ses.authstate.pw_name); addnewvar("LOGNAME", ses.authstate.pw_name); @@ -1093,9 +1142,7 @@ void svr_chansessinitialise() { } -/* add a new environment variable, allocating space for the entry */ -void addnewvar(const char* param, const char* var) { - +static char *create_newvar(const char *param, const char *var) { char* newvar = NULL; int plen, vlen; @@ -1107,8 +1154,13 @@ void addnewvar(const char* param, const char* var) { newvar[plen] = '='; memcpy(&newvar[plen+1], var, vlen); newvar[plen+vlen+1] = '\0'; + return newvar; +} + +/* add a new environment variable, allocating space for the entry */ +void addnewvar(const char* param, const char* var) { /* newvar is leaked here, but that's part of putenv()'s semantics */ - if (putenv(newvar) < 0) { + if (putenv(create_newvar(param, var)) < 0) { dropbear_exit("environ error"); } } diff --git a/svr-runopts.c b/svr-runopts.c index cb9259525..5905ba6bd 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -104,6 +104,7 @@ static void printhelp(const char * progname) { "-W (default %d, larger may be faster, max 10MB)\n" "-K (0 is never, default %d, in seconds)\n" "-I (0 is never, default %d, in seconds)\n" + "-A env1=,env2 Environment variable names and prefixes to accept\n" "-z disable QoS\n" #if DROPBEAR_PLUGIN "-A [,]\n" @@ -166,6 +167,7 @@ void svr_getopts(int argc, char ** argv) { svr_opts.inetdmode = 0; svr_opts.portcount = 0; svr_opts.hostkey = NULL; + svr_opts.acceptenv = NULL; svr_opts.delay_hostkey = 0; svr_opts.pidfile = expand_homedir_path(DROPBEAR_PIDFILE); #if DROPBEAR_SVR_LOCALTCPFWD @@ -220,6 +222,9 @@ void svr_getopts(int argc, char ** argv) { case 'r': next = &keyfile; break; + case 'A': + next = &svr_opts.acceptenv; + break; case 'R': svr_opts.delay_hostkey = 1; break; From 24296877c5227628d3343005179662cf4ba137c5 Mon Sep 17 00:00:00 2001 From: David Grieser Date: Tue, 13 Dec 2022 09:49:02 +0100 Subject: [PATCH 2/2] Fixed build. --- svr-chansession.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svr-chansession.c b/svr-chansession.c index 5c3991d03..b921f46f1 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -1054,7 +1054,7 @@ static void execchild(const void *user_data) { if (chansess->env) { char **p = chansess->env; for (; *p; putenv(*p++)) {} - } + } /* set env vars */ addnewvar("USER", ses.authstate.pw_name);