The Gentoo packaging software stack is composed of various scripts and pieces of code that makes it very easy to build software from sources in a controlled manner, which I think is the main attractive for most users, it’s at least my main attractive. I have the same control over the software I’d have when building everything from source but I don’t have to waste time to figure out all the dependencies and the options. At least when I’m not the maintainer of the ebuild of course.
Some of the things that Gentoo makes available to developers (and users) are also security checks that stops people from shooting straight to their feet. One of these features is the sandbox software that tries to ensure that the software being built and installed does not try to write outside the control of Portage. It provides a sort-of sandbox around the processes and makes sure it only writes in that limited area. Similar approaches are also implemented by a few different projects.
Unfortunately I’ve noticed that some users and a few developers too mistakes the sandbox tool for a security measure, which is absolutely is not. It’s just a safety device that is used to catch software that mistakenly tries to write outside the area it is told to write to. Not only malicious code in ebuilds can be implemented by using the pkg_*
functions (that execute without sandbox) or by using the addwrite
directive, or by explicitly disabling the sandbox, but if software wants to explicitly disable sandbox, it’s quite easy to do that.
For the sake of argument, the following code appends a funny phrase to your message of the day in the system, which is usually accessible only by root:
#include <stdio.h>
int main() {
FILE *fp = fopen("/etc/motd", "a");
if ( !fp ) {
fprintf(stderr, "Unable to open /etc/motd in writingn");
return 1;
}
fprintf(fp, "I peed in your sandbox!n");
fclose(fp);
return 0;
}
Code language: C++ (cpp)
Now if you compile this code and try to execute it as an user it won’t be able to access the file thus it will result in an error. If you execute it with root it will be able to write to the file, but sandbox will prevent that; on the other hand, compiling it explicitly static will make it bypass sandbox just fine:
flame@yamato % gcc test.c -o test; gcc -static test.c -o test-static
flame@yamato % sudo sandbox ./test
ACCESS DENIED open_wr: /etc/motd
Unable to open /etc/motd in writing
--------------------------- ACCESS VIOLATION SUMMARY ---------------------------
LOG FILE "/var/log/sandbox/sandbox-15132.log"
VERSION 1.0
FORMAT: F - Function called
FORMAT: S - Access Status
FORMAT: P - Path as passed to function
FORMAT: A - Absolute Path (not canonical)
FORMAT: R - Canonical Path
FORMAT: C - Command Line
F: open_wr
S: deny
P: /etc/motd
A: /etc/motd
R: /etc/motd
C: ./test
--------------------------------------------------------------------------------
flame@yamato % sudo sandbox ./test-static
QA: Static ELF ./test-static
Code language: CSS (css)
As you can see, executing the standard built version will fail with a sandbox violation, while the static copy will just warn that a static executable will be executed. Which would be fine if it means that static executable calls were considered harmful and taken care of, but since all the software based on KDE 3’s admin/
directory are going to call the dynamic linker explicitly (which is obviously a static executable), and that is not currently masked out, it really does not make much sense just yet.
But still, this does not says anything about sandbox as a security measure, because you can easily write code that works around the sandbox, again, let’s see some alternative code:
#include <stdio.h>
#include <stdlib.h>
int main() {
setenv("SANDBOX_ON", "0");
FILE *fp = fopen("/etc/motd", "a");
if ( !fp ) {
fprintf(stderr, "Unable to open /etc/motd in writing");
return 1;
}
fprintf(fp, "I peed in your sandbox!n");
fclose(fp);
return 0;
}
Code language: C++ (cpp)
Or more subtly to avoid disabling the sandbox altogether, which might be quite easy to identify.
#include <stdlio.h>
#include <stdlib.h>
int main() {
setenv("SANDBOX_WRITE", "/etc/motd");
FILE *fp = fopen("/etc/motd", "a");
if ( !fp ) {
fprintf(stderr, "Unable to open /etc/motd in writing");
return 1;
}
fprintf(fp, "I peed in your sandbox!n");
fclose(fp);
return 0;
}
Code language: C++ (cpp)
And to be even more evil, one can just disable the sandbox from loading before calling whatever command:
#include <stdio.h>
#include <stdlib.h>
int main() {
unsetenv("LD_PRELOAD");
system("/bin/echo 'I peed in your sandbox!' > /etc/motd");
return 0;
}
Code language: C++ (cpp)
Now these options can be easily worked around by intercepting setenv()
calls and at least warn that the sandbox variables are being tinkered with. But one can be even more subtle and work around the sandbox in another very interesting way:
#include <stdio.h>
#include <dlfcn.h>
FILE *(*my_fopen)(const char*, const char*);
int main() {
void *libc = dlopen("libc.so.6", RTLD_LAZY|RTLD_GLOBAL);
my_fopen = dlsym(libc, "fopen");
FILE *fp = my_fopen("/etc/motd", "a");
if ( !fp ) {
fprintf(stderr, "Unable to open /etc/motd in writing");
return 1;
}
fprintf(fp, "I peed in your sandbox!n");
fclose(fp);
return 0;
}
Code language: C++ (cpp)
In this case the original fopen()
call is loaded from the C library itself, exactly like the sandbox code does to call the original implementation. This would require dlopen()
to be wrapped around and controlled, too.
I guess I should be looking forward to try closing up some of these holes in sandbox just so that we can make it more useful to avoid shooting ourselves in the feet, but one cannot rely on this software alone as a security measure. A good alternative approach would be to make sure that the software gets built and installed always with low-privilege users; as it is right now, even though build can be done with the portage user, root is always used for the software to be installed into the destination directory, which the sandbox cannot help with.
There are a number of issues that can easily come to mind when you think that sandbox is not a security measure, so I’ll probably try to write more about this in the future, for now please try to consider what this means on a much deeper level, and review your packages, just so you’re sure.