java向けlibselinux(??)

Javaからlibselinuxの機能を利用するためのライブラリを考えてみた。
利用するのはJNI(Java Native Interface)で、C言語で記述されたコードをJava側から呼び出せるようになるというもの。
方法としては、特定のJavaクラスをスタブジェネレータに放り込めばC言語用のスタブが作成されるので、これを元にコーディングして共有ライブラリとしてビルドすればよい。

まず、getcon()関数をJavaから使うためのコードを書いてみる。

package selinux;
import java.lang.*;

public class libselinux {
static {
System.loadLibrary("selinux_java");
}

public static native String getcon()
throws Exception;

public static void main(String[] args) {
try {
System.out.println(getcon());
} catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
}
};

selinux.libselinux.getcon()メソッドを呼び出したプロセスのセキュリティコンテキストが返却されるはずである。
重要なのは、static{}の中でlibselinux_java.soをロードしていること(libと.soは省略)と、getcon()メソッドの前のnative修飾子
このコードをコンパイルすると、selinux/libselinux.classが生成されるので、次に以下のコマンドを実行してC言語スタブを作成する。
% javah selinux.libselinux
これによってselinux_libselinux.hというファイルが生成され、ここに必要な関数の宣言が記述される。
また、ヘッダファイルが必要になるので/usr/java/jdk1.5.0_06/include/以下からjni.hとlinux/jni_md.hをカレントディレクトリにコピーしておく。
% cp /usr/java/jdk1.5.0_06/include/jni.h ./
% cp /usr/java/jdk1.5.0_06/include/linux/jni_md.h ./
続いて本体となるselinux_libselinux.cを作成し、スタブに基づいて関数群を記述していく。
#include <selinux/selinux.h>
#include "jni.h"

JNIEXPORT jstring JNICALL
Java_selinux_libselinux_getcon(JNIEnv *jenv, jclass libselinux) {
security_context_t context;
jclass Exception;

if (getcon(&context))
return (*jenv)->NewStringUTF(jenv, context);

/* an error was occured*/
Exception = (*jenv)->FindClass(jenv, "java/lang/Exception");
if (Exception) {
if *1
(*jenv)->FatalError(jenv, "could not generate an Exception");
} else {
(*jenv)->FatalError(jenv, "could not find java.lang.Exception");
}
return NULL;
}

Java_selinux_libselinux_getcon()はスタブジェネレータ(javah)によって生成された関数で、Java的には引数を取らずStringを返却することになっている。
引数で与えられるJNIEnvへのポインタには、JNIメソッドへの関数ポインタや実行環境情報が格納されているので、必要に応じてこれらの関数を呼び出す。
ここでは、C言語文字列をJava文字列に変換して呼び出し元に返しているのと、エラーが発生した場合には例外を生成させている。

で、これを実行すると以下の通り。

% java selinux.libselinux
user_u:system_r:unconfined_t
のように、getcon()の返り値をJava側できちんと拾える。

よーしこれでlibselinuxのJavaインターフェースでも作るかと思ったが、問題が一つ二つ…。
例えば、int getpeercon(int fd, security_context_t *context);は引数にソケットのファイルディスクリプタを取るが、Javaでは直接ソケットのファイルディスクリプタを参照する方法がない(たぶんどこかに隠蔽されている)。
なので、う〜ん困ったなぁ状態。

getpeercon()はかなり重要機能なので、これが使えないとなるとモチベーション↓ orz...
どないしたものか…。

*1:*jenv)->ThrowNew(jenv, Exception, "libselinux: getcon() returned non zero."