2

Is it possible to recover the original environment that a shell script was invoked with? I'm not trying to write a program that depends on the ability to access the original environment, I'm wondering if it's guaranteed to be inaccessible.

I have a simple shell script that creates a new environment variable and changes the value of PATH.

#!/bin/sh

PATH=/usr/bin
NEW_VAR=new_var_value; export NEW_VAR

env

Is there a way to recover the original value of PATH or a way to tell that NEW_VAR was not originally present when the shell script was executed?

I know that argv, envp and the path to the executable are all set early on in the execv*(2) system call, but I'm not sure "where" or "how" they're stored. My guess is that the ability to manipulate the argument vector using shift and the environment using export, unexport, and assignments doesn't actually change the what the argv and envp are fundamentally set to. There's just logic inside the shell to make sure that new calls to execv*(2) use the right envp when calling another command (e.g. some-command | LC_ALL=C sort).

1
  • 1
    It depends on the implementation of the shell and/or libc. If the shell doesn't disturb the original environment, as is often the case, then you can do (on Linux) xargs -0 -L 1 < /proc/$$/environ to see your original environment. – Mark Plotnick Feb 20 '17 at 1:11
3

While a command is running, most systems allow you to display its environment with the ps command. It can't be done portably, and not all systems allow reliable parsing, but if all you want is to extract “interesting” parts of the initial environment in a way that works on a specific Unix variant, that's easy. For example, on Linux and *BSD:

ps eww $$

Alternatively, on Solaris and Linux:

cat /proc/$$/environ

Or, on Solaris and AIX:

pargs -e $$

If you need to hide the former contents of the environment, make sure that all the shell processes (including background subshells) have exited. To pass data in a way that doesn't leak in this way, use a pipe instead of an environment variable.

exec sh -c 'unset pw; pw=$PASSWORD; unset PASSWORD; printf %s "$pw" | exec theprogram'

Note that on most (all modern?) Unix variants, the environment of a process is only visible to the same user. Only the arguments are generally visible to any user running ps. So it's ok to pass confidential data in the environment; getting rid of it is only a matter of hardening, not basic security.

3

POSIX itself does not provide any way to do this out of the box. There are a few ways I can think of to come close to this:

  1. Look at the environment present in the parent (for example, with /proc/pid/environ in Linux) and repopulate the environment from scratch based on that. This may or may not work since
    • The parent may not necessarily have the same environment as the child started with
    • The parent may not exist any more
    • The child may not have access to the parent's environment (for example, in the case of dropping privileges)
  2. Save the environment at the beginning of your script (for example, using export -p or /proc/self/environ at the beginning) and restore it later. This works well enough, but you have to write the logic to restore it yourself.
  3. When you set a new environment varable, do it through a function which first stores the old variable value, and later you can restore it through the same function (or do it manually, by storing it in say, $OLD_PATH). This works, but is cumbersome and it's easy to forget to do it. For example:
$OLD_PATH=$PATH
# do something
$PATH=$OLD_PATH
1

If you want to know the state of the environment before any modifs, then why don't you just save it before to do anything. For example,

#!/bin/sh
PATH_orig=$PATH; # save the original PATH
case ${NEW_VAR++} in
   ? ) echo 'Achtung: NEW_VAR is already in the original environment' ;;
   * ) echo 'NEW_VAR not found in the original environment' ;;
esac

OLD_ENV=$(export -p) # will save your original env in a source-able manner

### And now make your changes...
PATH=/usr/bin
NEW_VAR="foo"; export NEW_VAR

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.