[BitVisor-devel:31] BitVisor newprocess patch

Shougo Matsushita shougo @ softlab.cs.tsukuba.ac.jp
2012年 2月 29日 (水) 15:50:55 JST


BitVisorメーリングリストの皆さん

筑波大学 ソフトウェア研究室の松下正吾です。

今回のパッチはBitVisorに動的プロセス生成機能を追加します。BitVisorはこれまでも
newprocess()を用いてプロセスを生成することはできました。BitVisorの組み込みプロ
セスはBitVisorのソースコードcore/builtin/bin_xxx.cにあります。しかし、この組み
込みプロセスはBitVisorに静的に組み込まなければならないという制限がありました。
プロセスを変更すると、BitVisorを再コンパイル・再起動が必要になります。今回のパ
ッチを用いると、プロセスのプログラムを変更した場合でもBitVisorの再ビルドや
BitVisorの再起動が必要なく、新しいプログラムをロードしてプロセスを生成できるよ
うになります。

本パッチで追加する機能は、プロセスのプログラムを開発する人とBitVisorの管理者は
別であるという考えに基いています。危険なプロセスをBitVisorで実行されると困るの
で、BitVisorに動的にロードできるプロセスはBitVisorの管理者に予め署名されたもの
に限定します。

本パッチでプロセスを動的ロードする場合、まず公開鍵・秘密鍵のペアを予め生成し、
公開鍵をBitVisorのソースコードに埋め込んでおきます。そして動的ロードしたいプロ
セスのバイナリを用意し、BitVisorの管理者がさきほどの秘密鍵によって署名を行いま
す。実際にゲストOSからプロセスのバイナリを動的ロードする時には、プロセスのバイ
ナリとその署名をBitVisorにvmmcallの引数として送信します。


(1) ハイパバイザコール vmmcall_newprocessのインタフェース

今回追加した、vmmcall_newprocessハイパバイザーコールのインタフェースを下に示し
ます。binary_addrはロードするELF形式のバイナリが格納されている変数のアドレス、
binary_sizeはロードするバイナリのサイズです。sign_addrはBitVisorの管理者による
署名が格納されている変数のアドレス、sign_sizeは署名のサイズです。sign_addrの署
名が合わない場合、このvmmcallはエラーを返します。

int vmmcall_newprocess(ulong binary_addr, ulong binary_size, ulong
sign_addr, ulong sign_size)

この機能を実装するため、core/process.cに次の関数を追加しました。

int
newprocess2(void *addr)
{
	return process_new(0, addr);
}

この関数はBitVisorに元から実装されていたnewprocess()とは違い、BitVisor内のアド
レスからELFバイナリを読み込み、プロセスとして実行します。vmmcall_newprocess()は
最終的に、newprocess2()の呼び出しに変換されています。

実行できるプロセスの仕様はBitVisor組み込みのプロセスと同じで、32bitのELFファイ
ルとなっています。BitVisor組み込みのプロセスと同じビルド工程により生成すること
ができます。


(2) vmmcall_newprocessを利用するためのスタブ関数

ゲストOSで用いるスタブ関数を以下に示します。スタブ関数のインタフェースはvmmcall
の引数で示したものと同じです。

int vmmcall_newprocess(ulong binary_addr, ulong binary_size,
                ulong sign_addr, ulong sign_size)
{
        int r;
        asm volatile ("vmcall" : "=a" (r) : "a" (vmmcall_newprocess_num),
                        "b" (binary_addr), "c" (binary_size),
                        "d" (sign_addr), "S" (sign_size));
        return r;
}

この関数をゲストOSで呼び出すと、binary_addrに示されるアドレスからbinary_sizeを
読み込み、BitVisorのプロセスとして実行します。sign_addr, sign_sizeにはプロセス
として実行するELFバイナリの署名データとその長さを指定します。正常に終了すると、
戻り値は0以上になります。失敗すると0以下の値が返ります。

(3) 公開鍵・秘密鍵の組の生成と変換

プロセスの動的ロード機能を利用するためには、鍵の生成と署名の作成が必要となりま
す。BitVisorの管理者は、次のようなコマンドであらかじめ公開鍵と秘密鍵の組を生成
します。

   $ openssl genrsa 1024 >test_private_key.pem

上記のコマンドを実行すると、公開鍵・秘密鍵のペアが生成されます。生成されたデー
タはテキストファイルです。

   $ cat test_private_key.pem
   -----BEGIN RSA PRIVATE KEY-----
   MIICXgIBAAKBgQCutrhDOf7YygrFWdcUGBRULAJQsldIXRMvqzUrF519Uzc5epv1
   Cz+N3XOqucEjfuRxWUq7w78l3Txhubfi923ECJZbD7t2+7qjoNX2bzx6ZmQ1sAQX
   kzodbRnqpBXuMI4YUJn61RRT5fvA7EINO1bSr4mV8+JiKhF4+n5wRF3nHwIDAQAB
   AoGBAIxHUTNJAYH21ycVt0b+/nOG0mUQjFwhvyHrCUmMMHtLsW+JLmdVnLW/NPvD
   IXvqimkoFIUl6ffw5mZgw3CM0afcuZu9lPpaRhcDS9tJtMjaB6sGkS+I5T+j9jTe
   Mgzb7v9yYqBt5dR59VM8Nzmy7HXaf2TYvCBs1eXoDcymYj1BAkEA2F9ZmlKnh2d6
   9vLjE5OMtNKZuUkfyEJgQB4PGTMB5RpbMVsN4HRPc6semMj5ALX+bVJFUrnGZ6di
   F0HAWwNIaQJBAM62MiH6zT1T81D1guqObjwLra5lPDR/PQEhfV9M93mI5SUm0fEw
   Zq7d2f6TQ2PpllxOZeYj/Z7ZY405WyREAkcCQQCMK3kImHMfLNJkGIUysWt92NDt
   T4nfWeCwqMhvMrQPjzt0heA/gBnYfQqdP9TPuRbSC3INXXxCuhS5rEbIrx/BAkEA
   pDtjZ1XhXLlnVspI4lyZPoG00xtBkyAIcu6NsnrvIBNgo3zNTkg7PPGscjPEVgxP
   VzU+hnPP3DYxfxGz1QNnnwJAOlVtBNCf3hOSa/xQ0xzcZ5YSaoFGysx8DLXaKr39
   txTjbXftY8m22JHB87BRSfU+07qKkgv4ql//d+yXEYxP6g==
   -----END RSA PRIVATE KEY-----

   $ openssl rsa -in test_private_key.pem -pubout -out test_public_key.pem

上記のコマンドより、公開鍵・秘密鍵のペアから公開鍵のみを取りだします。この公開
鍵データも次のようなテキストファイルとなっています。

   $ cat test_public_key.pem
   -----BEGIN PUBLIC KEY-----
   MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCutrhDOf7YygrFWdcUGBRULAJQ
   sldIXRMvqzUrF519Uzc5epv1Cz+N3XOqucEjfuRxWUq7w78l3Txhubfi923ECJZb
   D7t2+7qjoNX2bzx6ZmQ1sAQXkzodbRnqpBXuMI4YUJn61RRT5fvA7EINO1bSr4mV
   8+JiKhF4+n5wRF3nHwIDAQAB
   -----END PUBLIC KEY-----

(4) 公開鍵のBitVisorへの埋め込み

そしてcore/newprocess_pubkey.hに公開鍵のデータを埋め込み、
core/vmmcall_newprocess.cのソースコード内でこれを#includeするようにします。

#define EXEC_PUBLIC_KEY "-----BEGIN PUBLIC KEY-----\r\n" \
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCutrhDOf7YygrFWdcUGBRULAJQ\r\n" \
"sldIXRMvqzUrF519Uzc5epv1Cz+N3XOqucEjfuRxWUq7w78l3Txhubfi923ECJZb\r\n" \
"8+JiKhF4+n5wRF3nHwIDAQAB\r\n"
"-----END PUBLIC KEY-----\r\n"

このとき改行文字として、"\r\n"で区切ることに注意してください。

プロセスのプログラムの開発者は、開発したプロセスのプログラムをBitVisorの管理者
へ送ります。管理者はその内容を確認し、先ほど生成した秘密鍵で署名します。プロセ
スのプログラムの開発者は、ユーザにその署名されたプロセスのプログラムを送ります。
ユーザは、vmmcall_newprocessハイパバイザーコールでその署名されたプロセスのプ
ログラムをBitVisorにロードすることを要求します。BitVisorは、その署名されたプロ
セスのプログラムの署名を自身が持つ管理者の公開鍵で検証し、それが正しければプロ
グラムをロードします。

vmmcall_newprocessハイパバイザーコール戻り値により、ゲストOSはロードした
BitVisorプロセスのdescを得ることができます。

ちなみに、プロセスのプログラムを変更すると、これまでの署名が使えなくなるため、
もう一度署名を生成してもらう必要があります。

(5) プログラムの開発

プロセスのプログラムはcore/builtin/bin_xxx.cにあるBitVisorの組み込みコマンドと
同様に開発することができます。
今回のパッチでは、samples/loader/bin_hello.cに下のようなサンプルプログラムを用意しました。
プログラムの内容は、単純に標準出力にメッセージを表示するだけです。単にmakeを実
行するだけでビルドすることができるので参考にしてください。

--------------------------------------------------------------------------
#include <lib_lineinput.h>
#include <lib_printf.h>
#include <lib_syscalls.h>

int _start (int msgcode, int proc_number, struct msgbuf *buf, int bufcnt)
{
        printf("Hello, World!\n");
        return 0;
}
--------------------------------------------------------------------------

(6) 秘密鍵での署名

ELFバイナリの署名を簡単に生成するため、signという簡易的な署名ツールを作成しまし
た。Linux環境で動作を確認しています。ビルドにはOpenSSLの開発者用ライブラリが必
要です。ソースコードはguest/loader/sign.cにあります。


このプログラムの実行例は以下のようになります。
        $ ./sign bin_hello test_private_key.pem test_sign

出力が何もなければ、署名ファイルtest_signが生成されています。

(7) ロードと実行

vmmcall_newprocess()を簡単に使用できるようにするため、loaderというプログラムを
作成しました。Linux環境での動作を確認しています。ソースコードは
guest/loader/loader.cにあります。


このプログラムの実行例は以下のようになります。
        $ ./loader bin_hello test_sign
        desc = 6

プログラムの第一引数はプロセスとして実行するELFバイナリのファイル名です。
BitVisor組み込みのプロセスと同様にして、あらかじめ作成しておきます。
test_signにはあらかじめ生成しておいた署名データを指定します。
-------------- next part --------------
テキスト形式以外の添付ファイルを保管しました...
ファイル名: bitvisor-newprocess.patch
型:         text/x-patch
サイズ:     18073 バイト
説明:       無し
URL:        <http://www.bitvisor.org/archives/bitvisor-devel/attachments/20120229/a5b6ac46/attachment.bin>


BitVisor-devel メーリングリストの案内