Scratchpad for : Utumno
Our main development servers at work use almost 100% free software; however, recently I had a rare pleasure of having to install a piece of a binary blob. The Blob reared its ugly head as soon as I tried its installation routine:dev_server# ./blob_install Your Linux distribtion 'Debian GNU/Linux 5.0 \n \l' is unsupported.
Supported distributions: Fedora 8, 9 and 10; Ubuntu 8.04 and 8.10. Exiting.
Read on to see how per-process namespaces can help defeat The Blob.
The Attack
The Blob's only function is to check out pre-compiled software from one of our subcontractor's SVN servers, create workspace ( read: create a bunch of directories, move some files around, maybe set up a few links, check availability of some tools ). In short: it sets up environment for development. It is purely a console software, has no graphical parts, for sure does not interact with any other processes or daemons, does not use any advanced libraries and as such, should easily work in Debian or any other reasonably recent distribution.So let's install it anyway: run it under strace and see it open /etc/issue:
dev_server# cat /etc/issue Debian GNU/Linux 5.0 \n \lNow open blob_install with a hex editor and see that it expects "Ubuntu 8.10 \n \l" there. Temporarily change /etc/issue and watch The Blob install flawlessly.
The Defeat
At this point I thought I could just change /etc/issue back to its Debian version and start using The Blob; unfortunately, it turned out I underestimated its stubbornness: it actually checks the distribution every single time it performs any action.It thus became clear that I would either have to permanently keep /etc/issue in its Ubuntu form (which I don't want to do) or try to fool it in more clever way.
Regrouping
Fortunately I stumbled upon Mike Hommey's excellent write-up about per-process namespaces which turned out to be an excellent ammunition to defeat The Blob with.Per-process namepaces let one selectively 'unshare' any resources that were being shared at the time of creation of the process. This feature, among other things, allows each process to have a different set of mount points. Combined with bind mounts, it can allow some useful setups: we can create a new namespace, bind-mount something, run a process and only this process (and its children) will see the new mount. Furthermore, as soon as the process which created the namespace exits, the namespace and mounts inside it disappear.
Let's see an example. First we have to create a new namespace. This can be achieved with help of Mike's helper program 'newns':
#includeHere we used the unshare(2) syscall to create the namespace - we have to do it this way because Lenny's glibc does not implement the syscall.#include #include int main(int argc, char *argv[]) { syscall(SYS_unshare, CLONE_NEWNS); if (argc > 1) return execvp(argv[1], &argv[1]); return execv(ââ¬Â/bin/shââ¬Â, NULL); }
Compile this , create two files
dev_server# echo FIRST > first dev_server# echo SECOND > secondand try
dev_server# ./newns dev_server# mount -n --bind second first dev_server# cat first SECONDNow in a different console
dev_server# cat first FIRSTYou can see that the bind-mount is only seen by the process which called 'unshare' and its children. As soon as you exit this process, the new namespace and its mounts will disappear. More concise way of seeing the above would be
dev_server# ./newns sh -c "mount -n --bind second first; cat first" SECOND
The Counterattack
Armed with this knowledge we can attack The Blob again:1) Create /etc/issue.ubuntu with the proper Ubuntu string inside
2) move The Blob's binaries to /usr/local/bin/theblob
3) create a script /usr/local/bin/ubuntize:
#!/bin/sh /usr/local/bin/newns sh -c "mount -n --bind /etc/issue.ubuntu /etc/issue; /usr/local/bin/theblob/$0 $@"4) for each binary, create a link:
dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_install dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_checkout dev_server# ln -s /usr/local/bin/ubuntize /usr/local/bin/blob_create ... and so on ...
And we're done! All binaries called through their newly created links in /usr/local/bin will think they are running on a Ubuntu 8.10 system, and everything else will continue to work normally. Success!