Some root causes

In a previous article, I described a way to jump into a root file system in order to compile and tests softwares in this new environment. We used PRoot to jump in this new root file system and to bind some needed resources, like /dev, /proc or /sys, in this file system.

Inside, this fresh root file system, the first and obvious step is to use your favorite package manager to install some tools like git. For instance on a Debian file system:

#!/bin/bash
ivoire@machine: /tmp% proot -b /dev -b /proc -b /sys -b $HOME
                       -b /tmp -b /var/run -0 debian-i386-sid
root@machine: /tmp% apt-get install git
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  git git-man libbsd0 libedit2 liberror-perl
  openssh-blacklist openssh-blacklist-extra
  openssh-client rsync xauth
0 upgraded, 10 newly installed, 0 to remove and 0 not upgraded.
Need to get 0 B/12.9 MB of archives.
After this operation, 26.4 MB of additional disk space will be used.
Do you want to continue [Y/n]?
[...]
Unpacking git (from .../git_1%3a1.7.10.4-2_i386.deb) ...
dpkg: error processing /var/cache/apt/archives/git_1%3a1.7.10.4-2_i386.deb (--unpack):
unable to open '/usr/share/perl5/Git/SVN/Memoize/YAML.pm.dpkg-new': Permission denied
Errors were encountered while processing:
/var/cache/apt/archives/git_1%3a1.7.10.4-2_i386.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)

A strange error appears:

unable to open '/usr/share/perl5/Git/SVN/Memoize/YAML.pm.dpkg-new': Permission denied

Analysis

The error is a "Permission denied", which seems impossible because:

  • Every files in this root file system, belongs to the current user
  • PRoot is launched with the -0 switch that hooks some syscall and avoid permissions issues

For these two reasons, we should not have any permission denied!

In fact, the bug is tricker: apt-get is relying on the fact that root can browse and create files in a directory where it does not have any rights. For instance the following:

#!/bin/bash
~ mkdir /tmp/test
~ chmod 000 /tmp/test
~ ls -ld /tmp/test
d--------- 2 root root 4.0K /tmp/test/
~ touch /tmp/test/one_file
~ ls -l /tmp/test/one_file
-rw-r--r-- 1 root root 0 /tmp/test/one_file

This script will obviously fail for every users, except for root!

Just-in-Time permission changes

This behavior has to be taken into account by the -0 switch if we want it to behave like root.

We introduced an new mechanism in PRoot, only activated by -0, that change the permission when entering a syscall and restoring them when leaving the syscall. The following graph explains the patching mechanism:

fake_id0 principle

For every path segment, the PRoot extension looks for the permissions and patch them if needed. When leaving the syscall, every changes are restored. This way, accessing to directories is possible even without the right permissions.

The algorithm is quite simple:

  • Entering the syscall
  • Patching the premissions
  • Running the syscall
  • Unpatching the permissions
  • Leaving the syscall

Next step

With this feature, it's now possible to use apt-get or yum under PRoot to manage the packages in a file system.

If you haven't try it, I advice you to try to jump into a file system using PRoot to compile and test some of your projects. This may find some tricky bugs that only appears in some distributions that you don't run on your development machine.