SELinux on OpenZaurus(3)

% bitbake opie-image
OpenZaurusのルートファイルシステムイメージを作成できることになっているが、手元の環境では途中でビルドがエラーで停止するなどうまくいかなかった。しかも、トンでもなく時間がかかる。

そこで、OpenZaurusのルートファイルシステムイメージの一部を(バイナリのまま)差し替えて利用することとした。ベースとなるイメージは、OpenZaurus向けのOPIE-3.5.4.1用initrd.binを利用した。

まず、initrd.binの中身を読み出せるようにしなければならない。このファイルの本体はjffs2のイメージなので、MTD(Memory Technology Device)のエミュレーションデバイス(mtdram)を使う。
Erase Block Sizeは16KiBで、Total Sizeは32MBもあればまず十分。initrd.binの先頭16byteはヘッダ情報として使うようなので、mtdramへのロード時にはこの部分を抜いておく。

# modprobe mtdblock
# modprobe mtdram erase_size=16 total_size=32768
# dd if=initrd.bin of=/dev/mtdblock0 bs=16 skip=1
# mount -t jffs2 /dev/mtdblock0 /mnt
これでファイルを差し替える準備ができた。

今回、ファイルを追加・差替したのは以下の物件
・libselinux, libsepol
busybox
・/sbin/init

libsepol, libselinux のビルド

% export PATH=~/corgi/build/tmp/cross/bin:${PATH}
% wget http://www.nsa.gov/selinux/archives/libsepol-1.12.tgz
% tar zxvf libsepol-1.12.tgz && cd libsepol-1.12
% make CC=arm-linux-gcc CFLAGS='-DPATH_MAX=255'
(↑一部のシンボルが無いようだ)
% make DESTDIR=/mnt install
% make DESTDIR=~/corgi/build/tmp/cross/arm-linux \
PREFIX=~/corgi/build/tmp/cross/arm-linux install
% wget http://www.nsa.gov/selinux/archives/libselinux-1.30.tgz
% tar zxvf libselinux-1.30.tgz && cd libselinux-1.30
% make CC=arm-linux-gcc
% make DESTDIR=/mnt install
% make DESTDIR=~/corgi/build/tmp/cross/arm-linux \
PREFIX=~/corgi/build/tmp/cross/arm-linux install
mtdram上の区画だけでなく、クロスコンパイル環境にもヘッダファイル・ライブラリをコピーしておく必要がある。

busybox のコンパイル
これは簡単。コンフィグファイルとしてOpenZaurusのdefconfigをコピーして、menuconfigで以下の項目を有効化し、CC=arm-linux-gccをつけてmakeする。

General Configuration  --->
[*] Support NSA Security Enhanced Linux
ただ、どうもps -Zの時の動作が変だ。プロセス一覧の取得が上手くできていないように見える。
また、他にもSELinux関連コマンドがbusyboxには含まれていないので、別途コンパイルする必要がある。

Fedorabusyboxソースコードを利用する場合には、load_policyのみ使えるが、chconとかsetseboolとか必要なコマンドが実は全然足りないので、この辺は将来的にパッチを投稿するとかも必要になるだろう。
前にRussell Coker(busyboxSELinux対応コードを書いた)と話した時に、この辺追加しないとなと言っていたが、どうするつもりなんだろう。

/sbin/init の作成
OpenZaurusの場合面白いことになっていて、/sbin/initがシンボリックリンクとしてシェルスクリプトの/sbin/init.altbootを参照している。/sbin/initがELFバイナリになっているi386とは異なる。
そこで、SELinuxセキュリティポリシーをロードするためだけの小さなプログラムを作成して、それを最初のプロセスとして実行することにした。

#include <unistd.h>
#include <sys/types.h>
#include <sys/syslog.h>
#include <selinux/selinux.h>

int main(int argc, char *argv) {
char *narg
= {"/sbin/init", NULL};
int enforce;

if (selinux_init_load_policy(&enforce)) {
if (enforce > 0) {
syslog(LOG_ERR, "Unable to load SELinux Policy. "
"Machine is in enforcing mode. Halting now.\n");
exit(1);
}
}
execv(narg[0], narg);
syslog(LOG_ERR, "Can't exec '%s'.\n", narg[0]);
return 1;
}


実はカーネル側にも修正が必要。init/main.c の一番下のところにrun_init_process()関数をコールしている場所があるので、"/sbin/init.selinux"を最初に実行しようと試みるよう以下の一文を追加する。
run_init_process("/sbin/init.selinux");
これで、initプロセスが最初にSELinuxのセキュリティポリシーをロードするようになる。

とまぁ、ここまで書いてアレですが。
/sbin/init.altboot がシェルスクリプトなんだから、スクリプトの先頭に以下のようなセクションを設ければよかったかも。ちょっと後悔。

if [ "${SELINUX_INIT}" != "YES" ]; then
. /etc/selinux/config
POLICY=/etc/selinux/${SELINUXTYPE}/policy/policy.20
if load_policy ${POLICY}; then
export SELINUX_INIT=YES
exec $0
else
echo "$0: could not load SELinux policy."
exit 1
fi
fi
(*) このスクリプトは全く検証していません!just in idea

initrd.binの作成
最後に、busyboxや/sbin/initの修正を含めたルートファイルシステムのイメージを作成する。
手順は、mtdramの区画にファイルラベルを付与してmkfs.jffs2コマンドでイメージを作成する。mkfs.jffs2は--with-xattrオプションを有効にしたものが必要(mtd-utils.gitの最新版を利用すること)。

% su
# mkfs.jffs2 --faketime -n --pad --little-endian \
--root=/mnt --output=/tmp/hoge.img \
--eraseblock=0x4000 --with-xattr
% cd ~/corgi/org.openembedded.oz354x/packages
% cat sharp-binary-only/sharp-flash-header-c700/header-c700.bin \
/tmp/hoge.img > ~/initrd.bin
これでヘッダ付きのinitrd.binが出来上がる。

あとは、Zaurus本体にzImage.binとinitrd.binを転送するためのundater.shを入手して、アップデートを実行するだけ。
一旦バッテリーを抜いて電源を切り、その後ONボタンを押しながら電源を入れるとメンテナンスメニューが現れるので、アップデートを実行する。