tomoyotitle.png

TOMOYO Linux on Android

このページでは、 TOMOYO Linux を arm 用 Android エミュレータ上で動作させる手順について説明します。ホスト環境として x86_64 上で動作している Ubuntu 10.04.3 を使用します。

ステップ1:必要なパッケージのインストール

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

ステップ2:環境変数の設定

以下の内容を環境変数として指定します。 ~/.bashrc などに追加しておくことをお勧めします。

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

必要なディレクトリを作成します。

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/

ステップ3: Android 環境の構築

エミュレータをコンパイルします。

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

ステップ4:ホスト環境用ツールのインストール

Android エミュレータを遠隔操作するために、ホスト側に TOMOYO Linux のツールをインストールします。

cd $ANDROID_HOME/tmp/
wget https://sourceforge.net/projects/tomoyo/files/ccs-tools/1.8/ccs-tools-1.8.9-20210910.tar.gz
wget https://sourceforge.net/projects/tomoyo/files/ccs-tools/1.8/ccs-tools-1.8.9-20210910.tar.gz.asc
wget https://tomoyo.sourceforge.net/kumaneko-key
gpg --import kumaneko-key
gpg ccs-tools-1.8.9-20210910.tar.gz.asc
tar -zxf ccs-tools-1.8.9-20210910.tar.gz
cd ccs-tools
make
sudo make install

また、ポリシーをチューニングするためのツールを使うので、そのための準備を行います。

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

ステップ5:エミュレータ環境用ツールのインストール

Android エミュレータ側にインストールするためのエージェントをコンパイルします。

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

ステップ6:初期ポリシーの作成

カーネル内に埋め込んでおくためのポリシーを作成します。

という設定での例を示します。 <kernel> //./app-user ドメインに対して ANY_PATHNAME への書き込み許可は与えすぎです。実際に利用する場合には例えば /system/ 以下への書き込みを禁止するなどの対策を行うべきです。

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

ステップ7: Android 用カーネルの構築

カーネルのソースコードをダウンロードして展開します。

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

TOMOYO Linux パッチを適用します。

cd $ANDROID_HOME/tmp/goldfish/
wget https://sourceforge.net/projects/tomoyo/files/ccs-patch/1.8/ccs-patch-1.8.11-20240715.tar.gz
wget https://sourceforge.net/projects/tomoyo/files/ccs-patch/1.8/ccs-patch-1.8.11-20240715.tar.gz.asc
gpg ccs-patch-1.8.11-20240715.tar.gz.asc
tar -zxf ccs-patch-1.8.11-20240715.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/

カーネルをコンパイルします。

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

ステップ8:イメージファイルのコピー

Android エミュレータで指定するためのイメージファイルをコピーします。

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

ステップ9:RAMディスクの編集

Android エミュレータで使用するRAMディスクにエージェントをコピーし、自動的に起動されるようにします。

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

ステップ10:エミュレータの起動

Android エミュレータを起動します。カーネルはステップ7で作成したものを、RAMディスクはステップ9で作成したものを指定します。

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

ステップ11:ポート転送の指定

エミュレータ内で動作しているエージェントと通信するために、ポート転送を指定します。以下のように指定した場合、ホスト側でのポート 10000 への TCP 接続は、エミュレータ側でのポート 7000 へと転送されます。ステップ9で ccs-editpolicy-agent をポート 7000 で待機するように指定しているので、ホスト側でポート 10000 へ接続することにより、エージェントとの通信が可能になります。

adb forward tcp:10000 tcp:7000

ステップ12:エージェント経由での操作

以下のように ccs-editpolicy を起動すると、エミュレータ内のエージェント経由で ポリシーを閲覧/編集できます。

/usr/sbin/ccs-editpolicy 127.0.0.1:10000

以下のように ccs-auditd を起動すると、エミュレータ内のエージェント経由でアクセス許可ログ/拒否ログを取得して保存することができます。なお、アクセスログは大量に出力されるため、ディスクの空き容量に注意してください。

/usr/sbin/ccs-auditd 127.0.0.1:10000

以下のように ccs-queryd を実行すると、エミュレータ内のエージェント経由で、強制モードでのポリシー違反を対話的に処理できます。 ccs-queryd を終了させるには Ctrl-C を押してください。

/usr/sbin/ccs-queryd 127.0.0.1:10000

ワイルドカード化を行うには以下のようにしてください。ワイルドカード化のルールは /etc/ccs/tools/patternize.conf で定義されているので、適宜編集してください。

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

ステップ13:ポリシーのアップデート

ポリシーはメモリ上にのみ存在しているため、エミュレータを終了すると失われてしまいます。エミュレータを終了する前に忘れずにポリシーを保存するようにしてください。

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

また、保存したポリシーをカーネル内に埋め込むには以下のように行ってください。

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

カーネルを再コンパイルします。

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

再度エミュレータを実行します。

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

最終的なポリシーが完成したら、カーネル内に埋め込まれている domain_policy.conf に enforcing mode 用のプロファイルを割り当てておく( use_profile 3 )ことにより、 /init が実行される時点からアクセス制御を有効にすることができます。その状態で正常に動作することを確認したら、ステップ9で追加した /sbin/ccs-editpolicy-agent を削除します。また、 /init.rc およびカーネル内に埋め込まれた profile.conf からも ccs-editpolicy-agent を削除します。

補足:ポリシーの更新を認めたい場合のヒント

Android ではユーザがアプリケーションを追加することができます。上記手順では、ユーザIDに基づいて3つのカテゴリに分類するという方法により、アプリケーションが追加されてもポリシーを更新する必要が無いような仕様になっています。

しかし、追加されるアプリケーションによっては、ポリシーを更新したい場合があるかもしれません。そのような場合、起動時に必要になるポリシーとしてカーネル内に埋め込む固定分と、起動後に必要になるポリシーとしてカーネル内には埋め込まない変動分とを分離することで対処するという方法が考えられます。

起動時に必要となるポリシーはカーネルを読み込み専用のパーティションに配置することで、改ざんされる可能性を減らすことができます。起動後に必要になるポリシーは読み書きが可能なパーティションに配置することになるので改ざんされる可能性が高くなります。そのため、例えば GPG で署名し、改ざんされていないことを確認できた場合にのみポリシーを追加読み込みするという方法が考えられます。

TOMOYO Linux では強制モードにおけるポリシー違反を契機として外部のプログラムに問い合わせを行うことができます。 ccs-queryd 相当の処理を実装し、 ccs-editpolicy-agent の代わりに常駐させておくという方法を利用できます。

補足:ポリシーをカーネル内に埋め込みたくない場合のヒント

何かの理由によりポリシーをカーネル内に埋め込みたくない場合、ステップ7の

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/

という部分を

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

に置き換えてください。このようにした場合、ポリシーローダとして /sbin/ccs-init (および /sbin/ccs-init が依存するファイルとして /system/bin/linker /system/lib/libc.so /system/lib/libm.so )もRAMディスク内にコピーしてください。 /sbin/ccs-init が実行される時点では /system/ パーティションがマウントされていないため、シンボリックリンクでの代用はできません。また、 ccs-init.c はポリシーを /etc/ccs/ から読み込むようになっていますので、適宜修正(例えば $ANDROID_HOME/tmp/policy/*.conf の内容を ccs-init.c に埋め込んでしまうなど)してください。 Android エミュレータで使用するRAMディスクに含まれている /init.rc が /etc という名前で /system/etc/ ディレクトリへのシンボリックリンクを作成するため、以下の例では、ポリシーを /ccs/ ディレクトリから読み込むように修正しています。

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