[apparmor] Fwd: environment variables

John Johansen john.johansen at canonical.com
Sat Nov 12 09:31:34 UTC 2011


Tetsuo from the TOMOYO security project has graciously provided some input on
environment variables, and allowed me to forward is input to the list.  I have
included my reply inline.

Once again thanks Tetsuo the input is greatly appreciated

On 11/11/2011 09:04 PM, Tetsuo Handa wrote:
> I received "[apparmor] environment variables" via Toshi.
> Below is suggestion from TOMOYO, for TOMOYO has long hostory of managing string
> arguments such as basename, unprintable characters, argv[]/envp[] etc.
> (You may forward this message to AppArmor ML. (I'm not subscribed.))
understandable.  I have made sure your email address is white listed in case you
want to reply on list.

> ----------------------------------------
> John Johansen wrote:
>> So we have been looking into handling the environment variable problem, and
>> it was discussed at our UDS planning session.  To summarize several
>> applications, and frameworks are leveraging environment variables to
>> control execution behavior.
> 
> All inputs (not only executable file and shared libaries but also argv[],
> envp[], stdin, configuration files etc.) might be used as code for attacking.
> 
yep

>> For glibc this behavior is controlled by secure exec which filters out,
>> certain environment variables, eg. LD_PRELOAD.  However secure exec only
>> defines a small black list, and currently only removes the specified
>> variables from the environment.  A more flexible and generic solution is
>> needed, preferably one we can tie to policy.
> 
> Well, let me speak of TOMOYO 2.5 in Linux 3.2. ;-)
> 
> TOMOYO 2.5 provides access control over environment variables. For example,
> 
>   misc env PATH
>   misc env TERM
>   misc env HOME
> 
> will allow passing only PATH, TERM, HOME to execve()'s environment variables.
> Also, optionally TOMOYO allows checking its values. For example,
> 
>   misc env PATH exec.envp["PATH"]="/sbin:/bin:/usr/bin"
>   misc env PATH exec.envp["PATH"]="/bin:/usr/bin"
> 
> will allow passing PATH only if the value is either /sbin:/bin:/usr/bin or
> /bin:/usr/bin (i.e. OR logic). Value part accepts wildcards.
> 
> (Until Ubuntu 12.04 moves to Linux 3.2, you can try Ubuntu 10.04 Live CD
>  with TOMOYO 1.8 for experiencing how TOMOYO deals environment variables
>  http://tomoyo.sourceforge.jp/download.html ).
> 
yep, I saw the patches go in and have been looking at your implementation.
Your rules are similar to what we have been discussing

>> A dumb extension of secure exec does not seem to be a viable solution
>> either, as some environment variables need to be set for applications
>> to work, eg. the dbus session bus address, and yet we would like to be
>> able to control the value or range of values a variable could have.
> 
> Although TOMOYO provides ability to check combination of environment variables
> (i.e. AND logic) like
> 
>   file execute /path/to/program exec.envp["PATH"]="/sbin:/bin:/usr/bin" exec.envp["TERM"]="linux"
> 
I assume by and logic, here you me both the execute path and the environment
variable TERM="linux" correct (sorry I need to reread the tomoyo documentation).

> , number of environment variables is many and there will be dozens of possible
> combinations.
> 
yeah, this would get ugly if there were too many conditions

> The complexity of these combination may exceed what in-kernel logic can handle.
> Therefore, TOMOYO 1.8 also provides execute handler (in short, EH).
> 
I am curious on this, is it because its more rules than you want to load into
the kernel or is there a different limitation.  Not that using an EH might not
be the better solution when doing complicated comparisons.

> EH is a mechanism that replaces programs passed to execve() request with some
> other program specified by the policy configuration.
> This mechanism has two usecases.
> 
> One is for providing a preprocessor. For example we can validate/log/sanitize
> argv[] and envp[] passed to execve() request. An example C program that logs
> argv[] and envp[] looks like
> http://sourceforge.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-tools/ccstools/usr_lib_ccs/audit-exec-param.c?view=markup&revision=5630&root=tomoyo .
> 
make sense

> The other is for gracefully terminating current process when execve() request
> was rejected by TOMOYO. This is helpful for protecting the system from CPU
> consumption when execve() was called from an infinite loop.
> http://www.youtube.com/?v=I8fF5mueWTw demonstrates CPU consumption problem and
> its countermeasure. The countermeasure used in the movie is to carry sleep
> penalty. But gracefully terminating the current thread by replacing with some
> other program allows more flexible handling than simply carrying sleep penalty.
> For example, we can replace the current thread with a fake shell (which acts as
> a honey pot program).
> 
interesting

> All environment variables are moved from envp[] to argv[] so that the EH
> program shall not be affected by dangerous environment variables like
> LD_PRELOAD. The EH program checks environment variables and passes back from
> argv[] to envp[].
> 
> When using EH for providing a preprocessor, current process will fail to run
> the requested program if "the EH program decided not to call execve()" or "the
> EH program decided to call execve() but execve() was rejected by TOMOYO" or
> "the EH program decided to call execve() but execve() was failed by binary
> loader (e.g. load_elf_binary())". In this case, the control cannot be returned
> back to the current process because these situations occur after "point of no
> return of execve() operation that replaced with the EH program".
> 
right

> But even without EH mechanism, there is no guarantee that the current process
> transfers the control to the program passed to execve() operation when the
> current process reached point of no return of execve() operation. Current
> process can be terminated by SIGKILL if something bad happened before
> completing the execve() operation. Current process can be terminated by exit()
> before reaching to the main() function of a C program if something bad happened
> after execve() operation has completed. The downside of EH mechanism is merely
> one of possibilities that makes the current process fail to get back the
> control when something bad happened after point of no return.
yep

> 
> If you can't give up ability to report the current process that the requested
> program could not be started successfully, you can inject EH program using
> call_usermodehelper(UMH_WAIT) from do_execve() and let the EH program decide
> whether to execute or not (at the cost of giving up ability to modify envp[]
> that will be passed to the originally requested program). Though, I wonder how
> valuable not to give up ability to report the failure of execve().
> 
>> There are a few potential solutions and implementations, we could extend
>> filtering, extend exec matching, or create a hybrid solution.  Each
>> solution has its own issues, and design decisions.
>>
>>
>> 1. Matching
>>
>> The idea here is to extend the exec matching rules, with a conditional
>> match.
>>
>> Eg. something like
>>
>>   env={LD_PRELOAD=/usr/bin/*,FOO=bar} /bin/foo Ux,
>>
> 
> As I noted above, complexity of combination of environment variables will
> exceed single-lined condition like env={LD_PRELOAD=/usr/bin/*,FOO=bar} .
> You will want multi-lined conditions or execute handler.
> 
yeah, we iterated to that as well

> I'm not familiar with AppArmor's escape sequences. I have a few questions that
> should be cleared when handling environment variables.
> 
> (1) In TOMOYO, only ASCII printable characters are used in the policy
>     configuration.
>     http://tomoyo.sourceforge.jp/1.8/policy-specification/expression-rules.html#word
>     Not only pathnames but also environment variables may contain non-printable
>     characters (e.g. bell character).
> 
>     How does AppArmor represent non-printable characters in the policy
>     configuration? I guess AppArmor does something like "\ooo for representing
>     non-printable characters".
> 
yeah we provide for \x00 and \000 escaping

> (2) In TOMOYO, all wildcards are prefixed with \ character.
>     http://tomoyo.sourceforge.jp/1.8/policy-specification/expression-rules.html#wildcard
>     In AppArmor, * in LD_PRELOAD=/usr/bin/* acts as wildcard character, doesn't
>     it? Not only pathnames but also environment variables may contain
>     characters reserved for wildcards.
> 
>     How does AppArmor represent literal * character in the policy configuration?
>     I guess AppArmor does something like "use \\ for representing literal \
>     character" and "use \* for representing literal * character".
> 
yes

>> 1b. Should failing to match result in a failed exec or should it fall back,
>>   and try continuing the match against a more generic, or alternate target.
>>
>>   This would allow things like
>>     env={VAR_FOO=BAR} /bin/foo  Ux,
>>     /bin/* ix,
>>
>>   The fall back approach seems appealing at first glance but it might
>>   provide an attacker control over which profile is applied by setting
>>   the environment variables.
> 
> I guess environment variables are too complicated to deal with single-lined
> syntax.
>
right this was just a first pass to get the discussion going, when I wrote that
I knew it wouldn't be sufficient, though I thought that maybe using variables
might have allowed a single line implementation.

ie.
  @{somevar}=lots of values
  env={@{somevar}} /bin/foo Ux,

Basically hide the multi line values within a variable (apparmor variables can be
multi-value similar to perl lists)

>> 1c. What should the match expression look like, should it be a white list,
>>   or a black list.  Should it use aare syntax, or perhaps pcre syntax?
>>
>>   I will note that we do want to be able to make positive assertions
>>   like (ie. that is the value it should have).
>>     DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-iOIxOeHCR6,guid=435b295c9d1fc4337e6f1ac100000271
>>
>>   while at the same time, also allow many generic environment variables
>>   match.  This is needed because environment variables carry a lot of
>>   variables that change from system to system, application to application.
>>   Policy will be hard to write unless there is some generic env var matching.
> 
> I guess you want execute handler.
> 
hrmm, I am going to poke at the kernel implementation first and see what falls
out.  It is possible we will end up with an EH in the end

>> 4. Implementation
>>
>> The likely implementation seems to be in the kernel in the bprm hook called
>> during exec.  Environment filtering could be achieved by extending secure
>> exec in the libc loader.  However this solution is problematic in that it
>> relies on all binaries to be linked against the revised loader which
>> in many cases is not an assumption that can be made.
>>
>>
>>
>> Another point is that with kernel variables we could support better matching
>> of values that change per session but should be static during the session.
>> For example the dbus session bus, shouldn't change for the duration of the
>> session, so a kernel var could be set when the session is setup, and then
>> that value would be matched against for all processes in the session.
>>
>> This is of course assuming certain unimplemented features exist.  For the
>> moment we would be limited to asserting static values with an eye towards,
>> dynamic values in the future.
> 
> Sometimes some of environment variables must be cleared. For example,
> I guess DBUS_SESSION_BUS_ADDRESS should not be automatically inherited when
> restarting (e.g) SSH daemon from terminal session.

Right, we where kicking around the idea of just getting away with matching env
values, and failing the exec if there where values that didn't match but it
became pretty obvious that filtering is needed.

thanks Tetsuo



More information about the AppArmor mailing list