mirror of
https://github.com/sheumann/hush.git
synced 2025-01-11 08:29:54 +00:00
Fixes for environment variable handling on GNO:
* Push/pop environment to make sure it is isolated from our parents and children. * Make all environment vars (and shell vars) case-insensitive, consistent with GNO's internal handling of environment vars. * Wrap putenv and unsetenv to make sure they are called with lower-case variable names, which is necessary to maintain consistency between the environ array and the kernel's internal representation of variables.
This commit is contained in:
parent
97eb1defae
commit
4d60cd1043
@ -481,6 +481,13 @@ off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC;
|
|||||||
int xmkstemp(char *template) FAST_FUNC;
|
int xmkstemp(char *template) FAST_FUNC;
|
||||||
off_t fdlength(int fd) FAST_FUNC;
|
off_t fdlength(int fd) FAST_FUNC;
|
||||||
|
|
||||||
|
#ifdef __GNO__
|
||||||
|
void unsetenv_wrapper(const char *var);
|
||||||
|
int putenv_wrapper(char *string);
|
||||||
|
# define unsetenv unsetenv_wrapper
|
||||||
|
# define putenv putenv_wrapper
|
||||||
|
#endif
|
||||||
|
|
||||||
uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
|
uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
|
||||||
const char *override,
|
const char *override,
|
||||||
unsigned override_units,
|
unsigned override_units,
|
||||||
|
@ -63,6 +63,7 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
long oldmask;
|
long oldmask;
|
||||||
|
bool environPushed;
|
||||||
sig_t prev_alarm_sig;
|
sig_t prev_alarm_sig;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
kvmt *kvm_context;
|
kvmt *kvm_context;
|
||||||
@ -72,6 +73,9 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
|
|||||||
/* Block all signals for now */
|
/* Block all signals for now */
|
||||||
oldmask = sigblock(-1);
|
oldmask = sigblock(-1);
|
||||||
|
|
||||||
|
/* Isolate child process's environment from parent */
|
||||||
|
environPushed = !environPush();
|
||||||
|
|
||||||
pid = fork2(fork_thunk, 1024, 0, forked_child_name,
|
pid = fork2(fork_thunk, 1024, 0, forked_child_name,
|
||||||
(sizeof(fn) + sizeof(arg) + sizeof(oldmask) + 1) / 2,
|
(sizeof(fn) + sizeof(arg) + sizeof(oldmask) + 1) / 2,
|
||||||
fn, arg, oldmask);
|
fn, arg, oldmask);
|
||||||
@ -106,6 +110,8 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
|
|||||||
|
|
||||||
ret:
|
ret:
|
||||||
sigsetmask(oldmask);
|
sigsetmask(oldmask);
|
||||||
|
if (environPushed)
|
||||||
|
environPop();
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,12 +335,6 @@ char* FAST_FUNC xasprintf(const char *format, ...)
|
|||||||
return string_ptr;
|
return string_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAST_FUNC xsetenv(const char *key, const char *value)
|
|
||||||
{
|
|
||||||
if (setenv(key, value, 1))
|
|
||||||
bb_error_msg_and_die(bb_msg_memory_exhausted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handles "VAR=VAL" strings, even those which are part of environ
|
/* Handles "VAR=VAL" strings, even those which are part of environ
|
||||||
* _right now_
|
* _right now_
|
||||||
*/
|
*/
|
||||||
@ -363,12 +357,50 @@ void FAST_FUNC bb_unsetenv(const char *var)
|
|||||||
free(tp);
|
free(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FAST_FUNC bb_unsetenv_and_free(char *var)
|
#ifdef __GNO__
|
||||||
|
/* We wrap putenv and unsetenv functions to make them fully case-insensitive.
|
||||||
|
* The libc versions are case-insensitive with respect to the internal
|
||||||
|
* variable representation maintained by the kernel, but case-sensitive with
|
||||||
|
* respect to the environ array, with the result that the two representations
|
||||||
|
* may get out of sync. We avoid this by always converting variable names to
|
||||||
|
* lower case ourselves. We'd also need to wrap setenv if it were used.
|
||||||
|
*/
|
||||||
|
# undef putenv
|
||||||
|
int putenv_wrapper(char *string)
|
||||||
{
|
{
|
||||||
bb_unsetenv(var);
|
int name_len, i;
|
||||||
free(var);
|
int retval;
|
||||||
|
char *string_lowercase = xstrdup(string);
|
||||||
|
|
||||||
|
name_len = strcspn(string, "=");
|
||||||
|
for (i = 0; i < name_len; i++) {
|
||||||
|
string_lowercase[i] = tolower(string_lowercase[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We rely on putenv to copy its input string and not store the original,
|
||||||
|
* which GNO does but POSIX-standard systems don't. */
|
||||||
|
retval = putenv(string_lowercase);
|
||||||
|
free(string_lowercase);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
# define putenv putenv_wrapper
|
||||||
|
|
||||||
|
# undef unsetenv
|
||||||
|
void unsetenv_wrapper(const char *var)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
char *var_lowercase = xstrdup(var);
|
||||||
|
|
||||||
|
for (c = var_lowercase; *c != 0; c++) {
|
||||||
|
*c = tolower(*c);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsetenv(var_lowercase);
|
||||||
|
free(var_lowercase);
|
||||||
|
}
|
||||||
|
# define unsetenv unsetenv_wrapper
|
||||||
|
#endif
|
||||||
|
|
||||||
// Die with an error message if we can't set gid. (Because resource limits may
|
// Die with an error message if we can't set gid. (Because resource limits may
|
||||||
// limit this user to a given number of processes, and if that fills up the
|
// limit this user to a given number of processes, and if that fills up the
|
||||||
// setgid() will fail and we'll _still_be_root_, which is bad.)
|
// setgid() will fail and we'll _still_be_root_, which is bad.)
|
||||||
|
32
shell/hush.c
32
shell/hush.c
@ -375,6 +375,17 @@
|
|||||||
|
|
||||||
#define SPECIAL_VAR_SYMBOL 3
|
#define SPECIAL_VAR_SYMBOL 3
|
||||||
|
|
||||||
|
/* Use case-insensitive variable name comparisons on GNO, consistent with
|
||||||
|
* GNO's internal handling of environment variables.
|
||||||
|
*/
|
||||||
|
#ifdef __GNO__
|
||||||
|
# define varnamecmp strcasecmp
|
||||||
|
# define varnamencmp strncasecmp
|
||||||
|
#else
|
||||||
|
# define varnamecmp strcmp
|
||||||
|
# define varnamencmp strncmp
|
||||||
|
#endif
|
||||||
|
|
||||||
struct variable;
|
struct variable;
|
||||||
|
|
||||||
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
|
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
|
||||||
@ -1860,7 +1871,7 @@ static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
|
|||||||
|
|
||||||
pp = &G.top_var;
|
pp = &G.top_var;
|
||||||
while ((cur = *pp) != NULL) {
|
while ((cur = *pp) != NULL) {
|
||||||
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
|
if (varnamencmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
|
||||||
return pp;
|
return pp;
|
||||||
pp = &cur->next;
|
pp = &cur->next;
|
||||||
}
|
}
|
||||||
@ -1876,7 +1887,7 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
|
|||||||
char **cpp = G.expanded_assignments;
|
char **cpp = G.expanded_assignments;
|
||||||
while (*cpp) {
|
while (*cpp) {
|
||||||
char *cp = *cpp;
|
char *cp = *cpp;
|
||||||
if (strncmp(cp, name, len) == 0 && cp[len] == '=')
|
if (varnamencmp(cp, name, len) == 0 && cp[len] == '=')
|
||||||
return cp + len + 1;
|
return cp + len + 1;
|
||||||
cpp++;
|
cpp++;
|
||||||
}
|
}
|
||||||
@ -1886,11 +1897,11 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
|
|||||||
if (vpp)
|
if (vpp)
|
||||||
return (*vpp)->varstr + len + 1;
|
return (*vpp)->varstr + len + 1;
|
||||||
|
|
||||||
if (strcmp(name, "PPID") == 0)
|
if (varnamecmp(name, "PPID") == 0)
|
||||||
return utoa(G.root_ppid);
|
return utoa(G.root_ppid);
|
||||||
// bash compat: UID? EUID?
|
// bash compat: UID? EUID?
|
||||||
#if ENABLE_HUSH_RANDOM_SUPPORT
|
#if ENABLE_HUSH_RANDOM_SUPPORT
|
||||||
if (strcmp(name, "RANDOM") == 0)
|
if (varnamecmp(name, "RANDOM") == 0)
|
||||||
return utoa(next_random(&G.random_gen));
|
return utoa(next_random(&G.random_gen));
|
||||||
#endif
|
#endif
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1933,7 +1944,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
|
|||||||
name_len = eq_sign - str + 1; /* including '=' */
|
name_len = eq_sign - str + 1; /* including '=' */
|
||||||
var_pp = &G.top_var;
|
var_pp = &G.top_var;
|
||||||
while ((cur = *var_pp) != NULL) {
|
while ((cur = *var_pp) != NULL) {
|
||||||
if (strncmp(cur->varstr, str, name_len) != 0) {
|
if (varnamencmp(cur->varstr, str, name_len) != 0) {
|
||||||
var_pp = &cur->next;
|
var_pp = &cur->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2010,7 +2021,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
|
|||||||
exp:
|
exp:
|
||||||
if (flg_export == 1)
|
if (flg_export == 1)
|
||||||
cur->flg_export = 1;
|
cur->flg_export = 1;
|
||||||
if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
|
if (name_len == 4 && varnamencmp(cur->varstr, "PS", 2) == 0)
|
||||||
cmdedit_update_prompt();
|
cmdedit_update_prompt();
|
||||||
if (cur->flg_export) {
|
if (cur->flg_export) {
|
||||||
if (flg_export == -1) {
|
if (flg_export == -1) {
|
||||||
@ -2040,7 +2051,7 @@ static int unset_local_var_len(const char *name, int name_len)
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
var_pp = &G.top_var;
|
var_pp = &G.top_var;
|
||||||
while ((cur = *var_pp) != NULL) {
|
while ((cur = *var_pp) != NULL) {
|
||||||
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
|
if (varnamencmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
|
||||||
if (cur->flg_read_only) {
|
if (cur->flg_read_only) {
|
||||||
bb_error_msg("%s: readonly variable", name);
|
bb_error_msg("%s: readonly variable", name);
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -2048,7 +2059,7 @@ static int unset_local_var_len(const char *name, int name_len)
|
|||||||
*var_pp = cur->next;
|
*var_pp = cur->next;
|
||||||
debug_printf_env(("%s: unsetenv '%s'\n", "unset_local_var_len", cur->varstr));
|
debug_printf_env(("%s: unsetenv '%s'\n", "unset_local_var_len", cur->varstr));
|
||||||
bb_unsetenv(cur->varstr);
|
bb_unsetenv(cur->varstr);
|
||||||
if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
|
if (name_len == 3 && varnamencmp(cur->varstr, "PS", 2) == 0)
|
||||||
cmdedit_update_prompt();
|
cmdedit_update_prompt();
|
||||||
if (!cur->max_len)
|
if (!cur->max_len)
|
||||||
free(cur->varstr);
|
free(cur->varstr);
|
||||||
@ -5869,7 +5880,7 @@ static void re_execute_shell(char ***to_free, const char *s,
|
|||||||
*pp++ = (char *) G.argv0_for_re_execing;
|
*pp++ = (char *) G.argv0_for_re_execing;
|
||||||
*pp++ = param_buf;
|
*pp++ = param_buf;
|
||||||
for (cur = G.top_var; cur; cur = cur->next) {
|
for (cur = G.top_var; cur; cur = cur->next) {
|
||||||
if (strcmp(cur->varstr, hush_version_str) == 0)
|
if (varnamecmp(cur->varstr, hush_version_str) == 0)
|
||||||
continue;
|
continue;
|
||||||
if (cur->flg_read_only) {
|
if (cur->flg_read_only) {
|
||||||
*pp++ = (char *) "-R";
|
*pp++ = (char *) "-R";
|
||||||
@ -8081,6 +8092,9 @@ int hush_main(int argc, char **argv)
|
|||||||
INIT_G();
|
INIT_G();
|
||||||
hush_exec_path = get_exec_path();
|
hush_exec_path = get_exec_path();
|
||||||
#ifdef __GNO__
|
#ifdef __GNO__
|
||||||
|
/* Make sure our environment is isolated from the parent process's */
|
||||||
|
if (environPush() == 0)
|
||||||
|
atexit(environPop);
|
||||||
environInit();
|
environInit();
|
||||||
#endif
|
#endif
|
||||||
if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
|
if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user