tomoyotitle.png

TOMOYO Linux on Android

This page describes how to run TOMOYO Linux on Android emulator for ARM architecture. This page assumes Ubuntu 10.04.3 for x86_64 architecture as the host environment.

Step 1: Install required packages.

Install packages as suggested at https://source.android.com/source/download.html .

sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
sudo add-apt-repository "deb-src http://archive.canonical.com/ubuntu lucid partner"
sudo apt-get update
sudo apt-get install sun-java6-jdk
sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev libc6-dev \
lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev \
libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils xsltproc

Step 2: Set environment variables.

Set environment variables shown below. Adding to user's initrc script (e.g. ~/.bashrc ) is recommended.

export ANDROID_HOME=$HOME/mydroid/
export ANDROID_IMG=$ANDROID_HOME/image/

Create directories.

mkdir -p $ANDROID_HOME
mkdir -p $ANDROID_HOME/tmp
mkdir -p $ANDROID_HOME/tmp/policy
mkdir -p $ANDROID_IMG
mkdir -p $ANDROID_IMG/tmp
sudo mkdir -p /var/log/tomoyo
sudo chown -R `id -u` /var/log/tomoyo/

Step 3: Build the Android environment.

Download the source code and compile the emulator.

cd $ANDROID_HOME
wget https://dl-ssl.google.com/dl/googlesource/git-repo/repo
chmod 755 repo
./repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1
./repo sync
source build/envsetup.sh
lunch full-eng
make

Step 4: Compile tools for host environment.

Install TOMOYO Linux's userland tools into host environment in order to manage Android emulator remotely.

cd $ANDROID_HOME/tmp/
wget -O ccs-tools-1.8.7-20200505.tar.gz 'https://osdn.jp/frs/redir.php?m=jaist&f=/tomoyo/49693/ccs-tools-1.8.7-20200505.tar.gz'
wget -O ccs-tools-1.8.7-20200505.tar.gz.asc 'https://osdn.jp/frs/redir.php?m=jaist&f=/tomoyo/49693/ccs-tools-1.8.7-20200505.tar.gz.asc'
wget https://tomoyo.osdn.jp/kumaneko-key
gpg --import kumaneko-key
gpg ccs-tools-1.8.7-20200505.tar.gz.asc
tar -zxf ccs-tools-1.8.7-20200505.tar.gz
cd ccs-tools
make
sudo make install

Also, create default configuration in order to prepare for policy management tools.

sudo /usr/lib/ccs/init_policy
sudo chown -R `id -u` /etc/ccs/
echo 'rewrite head_pattern /acct/uid/\$/' >> /etc/ccs/tools/patternize.conf

Step 5: Compile tools for emulator environment.

Install TOMOYO Linux's agent program into Android emulator environment.

cd $ANDROID_HOME/tmp/
wget -O agcc https://plausible.org/andy/agcc
sed -i -e 's@4\.2\.1@4.4.3@g' -e 's@interwork/@@g' -- agcc
chmod 755 agcc
./agcc -o ccs-editpolicy-agent $ANDROID_HOME/tmp/ccs-tools/usr_lib_ccs/ccs-editpolicy-agent.c
chmod 700 ccs-editpolicy-agent

Step 6: Create initial policy.

Create initial policy which will be embedded into the kernel.

An example with conditions above is shown below. But it would be too loose to give write access to ANY_PATHNAME to "<kernel> //./app-user" domain. When applying to real devices, you should use more strict permissions such as forbidding write access to /system/ directory.

cd $ANDROID_HOME/tmp/policy/
cat > profile.conf << "EOF"
PROFILE_VERSION=20100903
0-COMMENT=-----Disabled Mode-----
0-PREFERENCE={ max_audit_log=1024 max_learning_entry=2048 enforcing_penalty=0  }
0-CONFIG={ mode=disabled grant_log=no reject_log=yes }
1-COMMENT=-----Learning Mode-----
1-PREFERENCE={ max_audit_log=1024 max_learning_entry=2048 enforcing_penalty=0  }
1-CONFIG={ mode=learning grant_log=no reject_log=yes }
2-COMMENT=-----Permissive Mode-----
2-PREFERENCE={ max_audit_log=1024 max_learning_entry=2048 enforcing_penalty=0  }
2-CONFIG={ mode=permissive grant_log=no reject_log=yes }
3-COMMENT=-----Enforcing Mode-----
3-PREFERENCE={ max_audit_log=1024 max_learning_entry=2048 enforcing_penalty=0  }
3-CONFIG={ mode=enforcing grant_log=no reject_log=yes }
EOF
cat > exception_policy.conf << "EOF"
path_group ANY_PATHNAME /
path_group ANY_PATHNAME /\{\*\}/
path_group ANY_PATHNAME /\{\*\}/\*
path_group ANY_PATHNAME /\*
path_group ANY_PATHNAME \*:/
path_group ANY_PATHNAME \*:/\{\*\}/
path_group ANY_PATHNAME \*:/\{\*\}/\*
path_group ANY_PATHNAME \*:/\*
path_group ANY_PATHNAME \*:[\$]
acl_group 0 file getattr @ANY_PATHNAME
acl_group 0 file ioctl @ANY_PATHNAME 0-0xFFFFFFFF
acl_group 0 file read /dev/urandom
acl_group 0 file read /system/bin/linker
acl_group 0 file read /system/lib/lib\*.so
acl_group 0 misc env _
acl_group 0 misc env ANDROID_ASSETS
acl_group 0 misc env ANDROID_BOOTLOGO
acl_group 0 misc env ANDROID_DATA
acl_group 0 misc env ANDROID_DNS_MODE
acl_group 0 misc env ANDROID_PROPERTY_WORKSPACE
acl_group 0 misc env ANDROID_ROOT
acl_group 0 misc env ANDROID_SOCKET_\*
acl_group 0 misc env ASEC_MOUNTPOINT
acl_group 0 misc env BOOTCLASSPATH
acl_group 0 misc env EXTERNAL_STORAGE
acl_group 0 misc env HOME
acl_group 0 misc env LD_LIBRARY_PATH
acl_group 0 misc env LOOP_MOUNTPOINT
acl_group 0 misc env ndns
acl_group 0 misc env PATH
acl_group 0 misc env qemu
acl_group 0 misc env RANDOM
acl_group 0 misc env SHELL
acl_group 0 misc env TERM
acl_group 0 task auto_domain_transition <kernel> //./app-user task.gid=10000-4294967295
acl_group 0 task auto_domain_transition <kernel> //./app-user task.uid=10000-4294967295
acl_group 0 task auto_domain_transition <kernel> //./system-user task.gid=1-9999
acl_group 0 task auto_domain_transition <kernel> //./system-user task.uid=1-9999
acl_group 1 file getattr @ANY_PATHNAME
acl_group 1 file ioctl @ANY_PATHNAME 0-0xFFFFFFFF
acl_group 1 file read /dev/urandom
acl_group 1 file read /system/bin/linker
acl_group 1 file read /system/lib/lib\*.so
acl_group 1 misc env _
acl_group 1 misc env ANDROID_ASSETS
acl_group 1 misc env ANDROID_BOOTLOGO
acl_group 1 misc env ANDROID_DATA
acl_group 1 misc env ANDROID_PROPERTY_WORKSPACE
acl_group 1 misc env ANDROID_ROOT
acl_group 1 misc env ANDROID_SOCKET_\*
acl_group 1 misc env ASEC_MOUNTPOINT
acl_group 1 misc env BOOTCLASSPATH
acl_group 1 misc env EXTERNAL_STORAGE
acl_group 1 misc env HOME
acl_group 1 misc env HOSTNAME
acl_group 1 misc env LD_LIBRARY_PATH
acl_group 1 misc env LOOP_MOUNTPOINT
acl_group 1 misc env MKSH
acl_group 1 misc env PATH
acl_group 1 misc env PS1
acl_group 1 misc env RANDOM
acl_group 1 misc env SHELL
acl_group 1 misc env TERM
acl_group 1 misc env USER
EOF
cat > domain_policy.conf << "EOF"
<kernel>
use_profile 1
use_group 0

<kernel> //./system-user
use_profile 1
use_group 1

file read/write/unlink/rmdir/truncate/symlink @ANY_PATHNAME task.euid=1-9999 task.egid=1-9999
file create/mkdir/mkfifo/mksock/chmod @ANY_PATHNAME 0-0777 task.euid=1-9999 task.egid=1-9999
file link/rename @ANY_PATHNAME @ANY_PATHNAME task.euid=1-9999 task.egid=1-9999

<kernel> //./app-user
use_profile 1
use_group 1

file read/write/unlink/rmdir/truncate/symlink @ANY_PATHNAME task.euid=10000-4294967295 task.egid=10000-4294967295
file create/mkdir/mkfifo/mksock/chmod @ANY_PATHNAME 0-0777 task.euid=10000-4294967295 task.egid=10000-4294967295
file link/rename @ANY_PATHNAME @ANY_PATHNAME task.euid=10000-4294967295 task.egid=10000-4294967295
EOF
echo /sbin/ccs-editpolicy-agent > manager.conf
echo > stat.conf

Step 7: Build the Android kernel.

Download and extract the kernel source code.

cd $ANDROID_HOME/tmp/
git clone https://android.googlesource.com/kernel/goldfish
cd goldfish/
git checkout origin/android-goldfish-2.6.29
ARCH=arm CROSS_COMPILE=$ANDROID_EABI_TOOLCHAIN/arm-linux-androideabi- make -s goldfish_armv7_defconfig

Apply TOMOYO Linux patch.

cd $ANDROID_HOME/tmp/goldfish/
wget -O ccs-patch-1.8.7-20200922.tar.gz 'https://osdn.jp/frs/redir.php?m=jaist&f=/tomoyo/49684/ccs-patch-1.8.7-20200922.tar.gz'
wget -O ccs-patch-1.8.7-20200922.tar.gz.asc 'https://osdn.jp/frs/redir.php?m=jaist&f=/tomoyo/49684/ccs-patch-1.8.7-20200922.tar.gz.asc'
gpg ccs-patch-1.8.7-20200922.tar.gz.asc
tar -zxf ccs-patch-1.8.7-20200922.tar.gz
patch -p1 < patches/ccs-patch-2.6.29-android-goldfish.diff
sed -e 's/# CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER is not set/CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER=y/' -- config.ccs >> .config
mkdir -p security/ccsecurity/policy/
cp -p $ANDROID_HOME/tmp/policy/*.conf security/ccsecurity/policy/

Compile the kernel.

cd $ANDROID_HOME/tmp/goldfish/
ARCH=arm CROSS_COMPILE=$ANDROID_EABI_TOOLCHAIN/arm-linux-androideabi- make -s
cp -p arch/arm/boot/zImage $ANDROID_IMG/kernel.img

Step 8: Copy Android's image files.

Copy image file used by Android emulator.

cd $ANDROID_HOME/out/target/product/generic/
cp -p system.img ramdisk.img userdata.img $ANDROID_IMG

Step 9: Edit Android's ramdisk image.

Copy the agent program into Android emulator's ramdisk and configure the agent to be automatically executed upon boot.

cd $ANDROID_IMG/tmp/
zcat ../ramdisk.img | cpio -id
echo >> init.rc
echo 'service ccs_agent /sbin/ccs-editpolicy-agent 0.0.0.0:7000' >> init.rc
echo '    class core' >> init.rc
echo '    oneshot' >> init.rc
cp -p $ANDROID_HOME/tmp/ccs-editpolicy-agent sbin/
find . -print0 | cpio -o0 -H newc | gzip -9 > ../ramdisk.img

Step 10: Start the Android emulator.

Start the Android emulator. Specify the kernel made at step 7 and the ramdisk made at step 9.

emulator -kernel $ANDROID_IMG/kernel.img -ramdisk $ANDROID_IMG/ramdisk.img -sysdir $ANDROID_IMG \
-data $ANDROID_IMG/userdata.img -show-kernel

Step 11: Enable TCP port forwarding.

Configure port forwarding in order to communicate with the agent program running in the emulator. Below line makes TCP connection requests sent to host environment's port 10000 are forwarded to emulator environment's port 7000. As you have configures ccs-editpolicy-agent to listen at port 7000 at step 9, you can communicate with the agent program by connecting to host environment's port 10000.

adb forward tcp:10000 tcp:7000

Step 12: Operate via agent.

You can browse/edit policy via agent program by starting ccs-editpolicy as shown below.

/usr/sbin/ccs-editpolicy 127.0.0.1:10000

You can save audit logs by starting ccs-auditd as shown below. Please be careful with disk's free space because a lot of logs are generated.

/usr/sbin/ccs-auditd 127.0.0.1:10000

You can interactively handle policy violation in enforcing mode by starting ccs-queryd as shown below. Press Ctrl-C to terminate ccs-queryd.

/usr/sbin/ccs-queryd 127.0.0.1:10000

You can make patterns by running ccs-patternize as show below. Edit /etc/ccs/tools/patternize.conf as needed since the rules for making patterns are defined in that file.

cd $ANDROID_HOME/tmp/policy/
/usr/sbin/ccs-savepolicy -d 127.0.0.1:10000 > domain_policy.old
/usr/sbin/ccs-patternize < domain_policy.old > domain_policy.new
/usr/sbin/ccs-diffpolicy domain_policy.old domain_policy.new > domain_policy.diff
less domain_policy.diff
/usr/sbin/ccs-loadpolicy -d 127.0.0.1:10000 < domain_policy.diff

Step 13: Updating policy

Since the policy updated after the boot resides only in the kernel memory, the updated policy will be lost when the emulator is terminated. Be sure to save the updated policy before terminating the emulator.

cd $ANDROID_HOME/tmp/policy/
/usr/sbin/ccs-savepolicy -e 127.0.0.1:10000 > exception_policy.conf
/usr/sbin/ccs-savepolicy -d 127.0.0.1:10000 > domain_policy.conf
/usr/sbin/ccs-savepolicy -p 127.0.0.1:10000 > profile.conf

Run below commands to update policy which will be embedded into the kernel.

cp -p $ANDROID_HOME/tmp/policy/*.conf $ANDROID_HOME/tmp/goldfish/security/ccsecurity/policy/

Recompile the Android kernel.

cd $ANDROID_HOME/tmp/goldfish/
ARCH=arm CROSS_COMPILE=$ANDROID_EABI_TOOLCHAIN/arm-linux-androideabi- make -s
cp -p arch/arm/boot/zImage $ANDROID_IMG/kernel.img

Restart the Android emulator.

emulator -kernel $ANDROID_IMG/kernel.img -ramdisk $ANDROID_IMG/ramdisk.img -sysdir $ANDROID_IMG \
-data $ANDROID_IMG/userdata.img -show-kernel

When you have finished developing the final policy files, you can specify a profile for enforcing mode (use_profile 3) to domain_policy.conf which will be embedded into the kernel. By using a profile for enforcing mode, you can enable access control from the moment /init in the initramfs is executed. After you have verified that the emulator works as expected with a profile for enforcing mode, you can remove /sbin/ccs-editpolicy-agent added at step 9. Also, you can remove ccs-editpolicy-agent from /init.rc and from profile.conf which will be embedded into the kernel.

Appendix: Hints for allowing policy updates after boot

Regarding Android devices, users can add applications which are not shipped with the device. Above procedure assumed there will be no need for updating policy when applications are added by categorizing into 3 groups based on the Linux's UID value.

However, you may want to allow updating policy when special applications are added. In that case, you can split policy files into the "fixed" part which will be embedded into the kernel for use at the boot stage and the "variant" part which will not be embedded into the kernel for use after the boot stage.

You can reduce the risk of tampering the "fixed" part by deploying the kernel into the read-only partition. But you may not be able to reduce the risk of tampering the "variant" part because the "variant" part will likely be located into the read-write partition. In that case, you can use (e.g.) GPG signature and append the policy into the kernel only when you verified that the "variant" part is not tampered.

TOMOYO Linux provides a mechanism for querying external userland application when a policy violation in enforcing mode has occurred. You can implement a program like ccs-queryd and daemonize the program instead of ccs-editpolicy-agent .

Appendix: Hints for not embedding policy into the kernel

If you want not to embed policy files into your kernel by some reason, you can replace

sed -e 's/# CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER is not set/CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER=y/' -- config.ccs >> .config
mkdir -p security/ccsecurity/policy/
cp -p $ANDROID_HOME/tmp/policy/*.conf security/ccsecurity/policy/

with

sed -e 's:/sbin/init:/init:' -- config.ccs >> .config

in Step 7. If you do so, you will need to copy /sbin/ccs-init (as a policy loader, and /system/bin/linker /system/lib/libc.so /system/lib/libm.so which /sbin/ccs-init depends on) into ramdisk image. You cannot use symlinks to files in /system/ partition because /system/ partition is not yet mounted as of /sbin/ccs-init is executed. Also, please modify ccs-init.c as needed (for example, embed the content of $ANDROID_HOME/tmp/policy/*.conf into ccs-init.c) because it is designed to read policy files from /etc/ccs/ directory. Below example changes ccs-init.c to read policy files from /ccs/ directory because /init.rc in Android emulator's ramdisk creates /etc as a symlink to /system/etc/ directory.

cd $ANDROID_HOME/tmp/
sed -e 's:etc/ccs:ccs:g' $ANDROID_HOME/tmp/ccs-tools/sbin/ccs-init.c > $ANDROID_HOME/tmp/ccs-tools/sbin/ccs-init2.c
./agcc -o ccs-init $ANDROID_HOME/tmp/ccs-tools/sbin/ccs-init2.c
cd $ANDROID_IMG/tmp/
mkdir -p sbin system/bin system/lib
cp -p $ANDROID_HOME/tmp/ccs-init sbin/
cp -p $ANDROID_HOME/out/target/product/generic/system/bin/linker system/bin/
cp -p $ANDROID_HOME/out/target/product/generic/system/lib/libc.so system/lib/
cp -p $ANDROID_HOME/out/target/product/generic/system/lib/libm.so system/lib/
chmod 700 sbin/ccs-init system/bin/linker system/lib/libc.so system/lib/libm.so
find . -print0 | cpio -o0 -H newc | gzip -9 > ../ramdisk.img