Setting environment variable globally without restarting Ubuntu - linux

I know that system wide environment variables can be set by adding entries into
/etc/environment
or
/etc/profile
But that requires system restart or X restart.
Is it possible to set an environment variable in Ubuntu / Linux so that immediately available system wide without restarting OS or logging out the user?

The simple answer is: you cannot do this in general.
Why can there be no general solution?
The "why?" needs a more detailed explanation. In Linux, the environment is process-specific. Each process environment is stored in a special memory area allocated exclusively for this process.
As an aside: To quickly inspect the environment of a process, have a look at /proc/<pid>/env (or try /proc/self/env for the environment of the currently running process, such as your shell).
When a ("parent") process starts another ("child") process (via fork(2)), the environment the environment of the parent is copied to produce the environment of the child. There is no inheritance-style association between those two environments thereafter, they are completely separate. So there is no "global" or "master" environment we could change, to achieve what you want.
Why not simply change the per-process environment of all running processes? The memory area for the environment is in a well-defined location (basically right before the memory allocated for the stack), so you can't easily extend it, without corrupting other critical memory areas of the process.
Possible half-solutions for special cases
That said, one can imagine several special cases where you could indeed achieve what you want.
Most obviously, if you do "size-neutral" changes, you could conceivable patch up all environments of all processes. For example, replace every USER=foo environment variable (if present), with USER=bar. A rather special case, I fear.
If you don't really need to change the environments of all processes, but only of a class of well-known ones, more creative approaches might be possible. Vorsprung's answer is an impressive demonstration of doing exactly this with only Bash processes.
There are probably many other special cases, where there is a possible solution. But as explained above: no solution for the general case.

This perl program uses gdb to change the USER variable in all currently running bash shells to whatever is given as the program arg. To set a new variable the internal bash call "set_if_not" could be used
#!/usr/bin/perl
use strict;
use warnings;
my #pids = qx(ps -C bash -o pid=);
my $foo = $ARGV[0];
print "changing user to $foo";
print #pids;
open( my $gdb, "|gdb" ) || die "$! gdb";
select($gdb);
$|++;
for my $pid ( #pids ) {
print "attach $pid\n";
sleep 1;
print 'call bind_variable("USER","' . $foo . '",0)' . "\n";
sleep 1;
print "detach\n";
}
This only works with bash ( I only tested it with version 4.1 on Ubuntu 10.04 LTS) and does not alter the environment for arbitrary already running programs. Obviously it must be run as root.

I fear the solution here is a frustrating one: Don't use environment variables. Instead use a file.
So, instead of setting up /etc/environment with:
SPECIAL_VAR='some/path/I/want/later/'
And calling it with:
$SPECIAL_VAR
Instead, create a file at ~/.yourvars with the content:
SPECIAL_VAR='some/path/I/want/later/'
And source the thing every time you need the variable:
cd `source ~/.yourvars; echo $SPECIAL_VAR`
A hack? Perhaps. But it works.

Related

Secure Erase of a Bash Environmental Variable

Suppose I have in a Bash shell script an environmental variable that holds a sensitive value (e.g. a password). How may I securely overwrite the memory that holds this variable's value before exiting my script?
If possible, the technique used to do so would not be dependent on the particular implementation of Bash I'm using. I'd like to find a standards-respecting/canonical way to do this that works on all correct Bash implementations.
Please note that the following are not in the scope of the question:
1. How the sensitive value is placed into the environmental variable
2. How the sensitive value stored in the environmental variable is passed to the program that consumes it
7/10/2017 5:03 AM Update to Address Comment by rici
rici, thank you for your comment, copied here:
"Exiting the script is really the only way to reliably delete an
environment variable from the script's resident memory. Why do you
feel the string is less safe after the script terminates than while it
is running?"
My intent here is to follow good practice and actively scrub all cryptographically-sensitive values from memory as soon as I am through using them.
I do not know if Bash actively scrubs the memory used by a script when that script exits. I suspect that it does not. If it indeed does not, the sensitive cryptographic value will remain resident in memory and is subject to capture by an adversary.
In C/C++, one can easily scrub a value's memory location. I am trying to find out of this is possible in Bash. It may be that Bash is simply not the right tool for security-sensitive applications.
First off, we need to distinguish between environment variables and shell variables. Environment variables exist for the lifetime of the process and cannot be overwritten. Not only that, but on many systems they are trivially visible to other processes. For example Linux provides the /proc filesystem which allows for lots of introspection of running processes, including observing their environment variables.
Here's an example of a Bash script that attempts to overwrite an environment variable. Notice that although the value within the script changes, the process' environment is not changed:
$ SECRET=mysecret bash -c \
'strings /proc/$$/environ | grep SECRET
SECRET=overwritten
echo "SECRET=$SECRET"
strings /proc/$$/environ | grep SECRET'
SECRET=mysecret
SECRET=overwritten
SECRET=mysecret
So it is never safe to store secrets in environment variables unless you control all access to the machine.
Holding a secret in a (non-environment) shell variable is much more secure, as an attacker would need to be able to access the memory of the process, which is generally something only the kernel can do. And while you're correct that minimizing the time you hold onto such secrets is a good practice, it's not generally worth jumping through lots of hoops for. It's far more important to secure your system and execution environment, because a motivated attacker who has sufficient access can observe a secret even if it only lives in memory for a brief time. Holding a secret in memory for longer than strictly necessary is only a marginal increase in risk, whereas running a privileged program on an insecure system already means all bets are off.

Sensitive Data in Command Line Interfaces

I know it's frowned upon to use passwords in command line interfaces like in this example:
./commandforsomething -u username -p plaintextpassword
My understanding that the reason for that (in unix systems at least) is because it'll be able to be read in the scrollback as well as the .bash_history file (or whatever flavor shell you use).
HOWEVER, I was wondering if it was safe to use that sort of interface with sensitive data programatically while programming things. For example, in perl, you can execute a command using two ``, the exec command, or system command (I'm not 100% sure on the differences between these apart from the return value from the two backticks being the output of the executed command versus the return value... but that's a question for another post I guess).
So, my question is this: Is it safe to do things LIKE
system("command", "userarg", "passwordarg");
as it essentially does the same thing, just without getting posted in scrollback or history? (note that I only use perl as an example - I don't care about the answer specific to perl but instead the generally accepted principle).
It's not only about shell history.
ps shows all arguments passed to the program. The reason why passing arguments like this is bad is that you could potentially see other users' passwords by just looping around and executing ps. The cited code won't change much, as it essentially does the same.
You can try to pass some secrets via environment, since if the user doesn't have an access to the given process, the environment won't be shown. This is better, but is a pretty bad solution too (e.g.: in case program fails and dumps a core, all passwords will get written to disk).
If you use environment variables, use ps -E which will show you environment variables of the process. Use it as a different users than the one executing the program. Basically simulate the "attacker" and see if you can snoop the password. On a properly configured system you shouldn't be able to do it.

Which shell should be used for Linux/MacOS/UNIX best compatibility?

My question may seem related to SO question "What Linux shell should I use?", but my problem is to know which shell shall be used to write an application start script, knowing that this is a cross-platform Java application (almost all Linux distributions, MacOS, Solaris, ...). So i'm adding compatibility concerns here.
Please note that i'm not asking "which is the best shell to use" in general (which may have no sense in my opinion: subjective, depends on needs), but I'd like to know, which shell has the best chance, today, to be available (and suitable for Java application start) on most operating systems.
Also, may I simply have to use the shebang #!/bin/bash to "use bash"? (or for example #!/bin/ksh for Korn shell). What if this shell is not available on this OS?
We're actually using a ".sh" file with the shebang #!/bin/sh (which is Bourne shell I guess) but some users are complaining about errors on some Linux distributions (we don't know yet which one they use, but we would like to have a more global approach instead of fixing errors one by one). MacOS is currently using bash as the default shell but at this time we don't have any issue on MacOS using /bin/sh...
Note: we'd like to avoid having several start scripts (i.e. using different shells)
For maximum portability, your best bet is /bin/sh using only POSIX sh features (no extensions). Any other shell you pick might not be installed on some system (BSDs rarely have bash, while Linux rarely has ksh).
The problem you can run into is that frequently, /bin/sh is not actually Bourne sh or a strictly POSIX sh -- it's frequently just a link for /bin/bash or /bin/ksh that runs that other shell in sh-compatibility mode. That means that while any POSIX sh script should run fine, there will also be extensions supported that will cause things that are illegal per POSIX to run as well. So you might have a script that you think is fine (runs fine when you test it), but its actually depending on some bash or ksh extension that other shells don't support.
You can try running your script with multiple shells in POSIX compatibility mode (try say, bash, ksh, and dash) and make sure it runs on all of them and you're not accidentally using some extension that only one supports.
You won't find a shell implementation that will be installed on every of these OSes, however, all of them are either POSIX compliant or more or less close to being compliant.
You should then restrict your shell scripts to stick to the POSIX standard as far as possible.
However, there is no simple way to tell a script is to be executed in a POSIX context, and in particular to specify what shebang to set. I would suggest to use a postinstaller script that would insert the correct shebang on the target platform retrieved using this command:
#!/bin/sh
printf "#!%s\n" `PATH=\`getconf PATH\` command -v sh`
You scripts should also include this instruction once and before calling any external command:
export PATH=$(getconf PATH):$PATH
to make sure the utilities called are the POSIX ones. Moreover, beware that some Unix implementations might require an environment variable to be set for them to behave a POSIX way (eg BIN_SH=xpg4 is required on Tru64/OSF1, XPG_SUS_ENV=ON on AIX, ...).
To develop your script, I would recommend to use a shell that has the less extensions to the standard, like dash. That would help to quickly detect errors caused by bashisms (or kshisms or whatever).
PS: beware that despite popular belief, /bin/sh is not guaranteed to be POSIX compliant even on a POSIX compliant OS.

Find out which environment variables used by a command

In a Linux environment (in my case XUbuntu), is there a way to find out which environment variables are accessed by a command which is run from the console?
It must be possible to find out about these variables, as someone has to provide the values to the program. But is there some default method to do this?
The program in question here is xprintidle.
Assuming you want the variables actually used by some process running a command, you could use ltrace and look for calls to getenv:
$ ltrace -e getenv ./your_program
Of course, the argument to getenv can be computed (so you cannot really predict it).
However, some (old or strange) applications might use the environ global, or the third optional argument to main; and some applications even change their environment using putenv, setenv or unsetenv (all 3 being the libc functions, not the shell builtin).
but xprintidle is mostly interacting with the Xorg server. I'm surprised you are expecting it to use many environment variables (except DISPLAY).
Do you mean actually accessed, or the defined environment variables for a process?
For defined environment, you can use:
perl -pe 's,\00,\n,g' /proc/xxxx/environ
where xxxx is the PID of your process.
For accessed variables, you'll have to use something like ltrace and check for calls to getenv().
You can look at the environment variables that are passed to your program by using the printenv command.
One way that programs can look at their environment is by using the getenv call, so you could use a tracing program or the LD_PRELOAD trick to wrap that call and figure out what they are looking for.
However, not all programs use getenv to look at their environment. If the program uses the following (uncommon) format for main, it can look at the environment variables through the envp pointer:
int main(int argc, char *argv[], char *envp[]) {
/* ... */
}
Also, programs can get access to the environment through the environ variable.
You'll probably have luck by tracing on getenv calls, but it is no guarantee---it varies from program to program.
If you want the list of environment variables, then that is done using env
But determining which variables are accessed by the program is impossible, since they are all passed as they are.

Is there a way to change the environment variables of another process in Unix?

On Unix, is there any way that one process can change another's environment variables (assuming they're all being run by the same user)? A general solution would be best, but if not, what about the specific case where one is a child of the other?
Edit: How about via gdb?
Via gdb:
(gdb) attach process_id
(gdb) call putenv ("env_var_name=env_var_value")
(gdb) detach
This is quite a nasty hack and should only be done in the context of a debugging scenario, of course.
You probably can do it technically (see other answers), but it might not help you.
Most programs will expect that env vars cannot be changed from the outside after startup, hence most will probably just read the vars they are interested in at startup and initialize based on that. So changing them afterwards will not make a difference, since the program will never re-read them.
If you posted this as a concrete problem, you should probably take a different approach. If it was just out of curiosity: Nice question :-).
Substantially, no. If you had sufficient privileges (root, or thereabouts) and poked around /dev/kmem (kernel memory), and you made changes to the process's environment, and if the process actually re-referenced the environment variable afterwards (that is, the process had not already taken a copy of the env var and was not using just that copy), then maybe, if you were lucky and clever, and the wind was blowing in the right direction, and the phase of the moon was correct, perhaps, you might achieve something.
Quoting Jerry Peek:
You can't teach an old dog new tricks.
The only thing you can do is to change the environment variable of the child process before starting it: it gets the copy of the parent environment, sorry.
See http://www.unix.com.ua/orelly/unix/upt/ch06_02.htm for details.
Just a comment on the answer about using /proc. Under linux /proc is supported but, it does not work, you cannot change the /proc/${pid}/environ file, even if you are root: it is absolutely read-only.
I could think of the rather contrived way to do that, and it will not work for arbitrary processes.
Suppose that you write your own shared library which implements 'char *getenv'. Then, you set up 'LD_PRELOAD' or 'LD_LIBRARY_PATH' env. vars so that both your processes are run with your shared library preloaded.
This way, you will essentially have a control over the code of the 'getenv' function. Then, you could do all sorts of nasty tricks. Your 'getenv' could consult external config file or SHM segment for alternate values of env vars. Or you could do regexp search/replace on the requested values. Or ...
I can't think of an easy way to do that for arbitrary running processes (even if you are root), short of rewriting dynamic linker (ld-linux.so).
Or get your process to update a config file for the new process and then either:
perform a kill -HUP on the new process to reread the updated config file, or
have the process check the config file for updates every now and then. If changes are found, then reread the config file.
It seems that putenv doesn't work now, but setenv does.
I was testing the accepted answer while trying to set the variable in the current shell with no success
$] sudo gdb -p $$
(gdb) call putenv("TEST=1234")
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x0
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=
and the variant how it works:
$] sudo gdb -p $$
(gdb) call (int) setenv("TEST", "1234", 1)
$1 = 0
(gdb) call (char*) getenv("TEST")
$2 = 0x55f19ff5edc0 "1234"
(gdb) detach
(gdb) quit
$] echo "TEST=$TEST"
TEST=1234
Not as far as I know. Really you're trying to communicate from one process to another which calls for one of the IPC methods (shared memory, semaphores, sockets, etc.). Having received data by one of these methods you could then set environment variables or perform other actions more directly.
If your unix supports the /proc filesystem, then it's trivial to READ the env - you can read the environment, commandline, and many other attributes of any process you own that way. Changing it... Well, I can think of a way, but it's a BAD idea.
The more general case... I don't know, but I doubt there's a portable answer.
(Edited: my original answer assumed the OP wanted to READ the env, not change it)
UNIX is full of Inter-process communication. Check if your target instance has some. Dbus is becoming a standard in "desktop" IPC.
I change environment variables inside of Awesome window manager using awesome-client with is a Dbus "sender" of lua code.
Not a direct answer but... Raymond Chen had a [Windows-based] rationale around this only the other day :-
... Although there are certainly unsupported ways of doing it or ways that work with the assistance of a debugger, there’s nothing that is supported for programmatic access to another process’s command line, at least nothing provided by the kernel. ...
That there isn’t is a consequence of the principle of not keeping track of information which you don’t need. The kernel has no need to obtain the command line of another process. It takes the command line passed to the CreateProcess function and copies it into the address space of the process being launched, in a location where the GetCommandLine function can retrieve it. Once the process can access its own command line, the kernel’s responsibilities are done.
Since the command line is copied into the process’s address space, the process might even write to the memory that holds the command line and modify it. If that happens, then the original command line is lost forever; the only known copy got overwritten.
In other words, any such kernel facilities would be
difficult to implement
potentially a security concern
However the most likely reason is simply that there's limited use cases for such a facility.
Yes, you can, but it doesn't make sense. To do that you should invade into a running process and change its memory.
Environment variables are the part of the running process, that also means env vars are copied and stored to the memory of a process when it started, and only the process itself owns it.
If you change evn vars when the process is trying to read them it could be corrupted, but you still can change them if you attach a debugger to the process, because it has access to the memory of the attached process.

Resources