前のチュートリアル チュートリアルの紹介および目次

Java GSS-APIおよびJAASで実行可能なほかの操作



前のチュートリアル、「JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換」では、2つのアプリケーション(特にクライアントとサーバー)がJava GSS-APIを使用して相互間のセキュリティ保護されたコンテキストを確立して、メッセージを安全に交換する方法を示しました。

コンテキスト・イニシエータ(このクライアント/サーバーの例ではクライアント)を使用してコンテキストを確立したあとで、コンテキスト・アクセプタ(サーバー)が実行可能な操作はほかにもあります。基本的に、サーバーはクライアントを「装う」ことができます。偽装のレベルは、クライアントがクレデンシャルをサーバーに委譲しているかどうかにより異なります。


クライアント・ユーザーでのコードの実行

サーバーがクライアントを装う1つの方法は、クライアント・コードを実行するのと同じエンティティ(ユーザー)でコードを実行することです。通常、スレッドにより実行されるメソッドは、そのスレッド自体のアクセス制御設定を使用します。ただし、このチュートリアルでは、サーバーがクライアントを装う際、クライアントのアクセス制御設定を使用するため、サーバーはクライアント自体が実行時に保持する、厳密に同じリソースにアクセスできます。

このチュートリアルで使用する方法の主要な利点は、JAAS承認コンポーネントをアクセス制御に使用できることです。JAAS承認コンポーネントがない場合、サーバー・プリンシパルは、クライアント・ユーザーの代わりに実行されるコードがアクセスするすべてのリソースにアクセスできる必要があり、サーバー・コードには、ユーザーがそのリソースにアクセスすることが承認されているかどうかを判別するためのアクセス制御ロジックを含める必要があります。JAAS承認を利用することにより、プリンシパルベースのアクセス制御が提供されるため、アクセス制御が自動的に処理されます。これらのコード内のセキュリティ関連操作のアクセス権は、そのユーザーにのみ付与する必要があり、サーバー・コードに付与する必要はありません。JAAS承認の詳細は、「JAAS承認」チュートリアルを参照してください。

基本的なアプローチ

サーバーは、どのようにしてクライアントを「装い」、クライアント・コードを実行するユーザーに代わってコードを実行できるのでしょうか。基本的に、そのユーザーに代わってクライアント・コードを実行するように設定する方法と同じです。サーバー・コードで認識する必要があるのは、ユーザーのプリンシパル名のみです。これは、クライアントで確立されたコンテキストから取得できます。

クライアント・コードを実行するユーザーのJAAS認証により、ユーザー(プリンシパル)名を保持するプリンシパルを含むサブジェクトが作成されることはすでに学びました。その後、プリンシパルは(LoginユーティリティからのSubject.doAsPrivileged呼出しを介して)新しいアクセス制御コンテキストに関連付けられ、クライアント・コードがそのユーザーに代わって実行されるものと見なされます。以降のアクセス制御は、クライアント・コードを実行する特定のユーザーに必要なアクセス権が付与されるかどうかに基づいて決定されます。

サーバー・コードも同様に処理されます。ただし、一般に、認証に指定されるプリンシパルはユーザー・プリンシパルではなく「サービス・プリンシパル」である点が異なります。再度、指定されたプリンシパル名のプリンシパルを含むサブジェクトが作成され、Subject.doAsPrivilegedが呼び出されます。サーバー・コードは指定されたプリンシパルに代わって実行されると見なされます。以降のアクセス制御は、サーバー・コードを実行する特定のプリンシパルに必要なアクセス権が付与されるかどうかに基づいて決定されます。

いったんクライアントおよびサーバーが相互コンテキストを確立すると、次のコードでコンテキスト・イニシエータの名前(クライアントのプリンシパル名)を確認できます。

GSSName clientGSSName = context.getSrcName();

コンテキスト・アクセプタ(サーバー)は、この名前を使用して、同じエンティティを表すプリンシパルを含むサブジェクトを生成できます。たとえば、Sun Microsystemsの提供するJREを使用している場合、次の方法でサブジェクトを生成できます。

Subject client = 
  com.sun.security.jgss.GSSUtil.createSubject(clientGSSName, null);

createSubjectメソッドは、引数として指定されたGSSNameおよびGSSCredentialから新しいサブジェクトを作成します。サーバー・コードが単にローカルJVM内でユーザーに代わってコードを実行する場合、ユーザーのクレデンシャルは必要ではありません。実際のところ、クライアントがサーバーにクレデンシャルを委譲しているのでないかぎり、ユーザーのクレデンシャルは取得できません。詳細は、「クライアントから委譲されたクレデンシャルの使用」を参照してください。ここではクレデンシャルは必要ではないため、GSSCredential引数にnullを渡します。


注: Sun Microsystemsの提供するJREを使用しているのではない場合、これを実行する別の方法は、次のようにしてKerberosPrincipalインスタンスを生成することです。
KerberosPrincipal principal = 
  new KerberosPrincipal(clientGSSName.toString());

次に、このプリンシパルを使用して新たなサブジェクトを生成するか、既存のサブジェクトのプリンシパル・セット内でこのプリンシパルを生成します。


サーバーがユーザーとして実行するコードは、java.security.PrivilegedAction (またはjava.security.PrivilegedExceptionAction)を実装するクラスのrunメソッドで開始する必要があります。つまり、コードをこのrunメソッド内に配置することも、runメソッドから呼び出すこともできます。

サーバー・コードは、PrivilegedAction (またはPrivilegedExceptionAction)のインスタンスとともにサブジェクトをSubject.doAsPrivilegedに渡し、以降のコードをPrivilegedActionのrunメソッドから、指定されたサブジェクトのプリンシパル(ユーザー)で開始できます。

たとえば、PrivilegedActionクラスがReadFileActionを呼び出し、引数としてプリンシパル名を保持するStringを取る場合を考えましょう。このクラスのインスタンスは、次のコードで作成できます。

String clientName = clientGSSName.toString();
PrivilegedAction readFile = 
    new ReadFileAction(clientName);

doAsPrivilegedの呼出しは、次のようになります。

Subject.doAsPrivileged(client, readFile, null);

サンプル・コードおよびポリシー・ファイル

次のサンプル・コードおよびポリシー・ファイルは、クライアントを実行する特定のユーザーのみに許可されるセキュリティ関連操作用のコードを実行するために、サーバーがクライアントを装う方法を示します。

SampleServerImp.java

SampleServerImp.javaファイルは、クライアントとメッセージを交換したあと、クライアント・ユーザーとしてReadFileActionを実行するための次のようなコードが生成される点を除けば、前のチュートリアル(「JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換」)で紹介したSampleServer.javaファイルとまったく同じです。

System.out.println("Impersonating client.");

/*
 * Extract the KerberosPrincipal from the client GSSName and 
 * populate it in the principal set of a new Subject. Pass in a 
 * null for credentials since credentials will not be needed.
 */
GSSName clientGSSName = context.getSrcName();
System.out.println("clientGSSName: " + clientGSSName);
Subject client =
   com.sun.security.jgss.GSSUtil.createSubject(clientGSSName,
        null);

/*
* Construct an action that will read a file meant only for the
* client
*/
String clientName = clientGSSName.toString();
PrivilegedAction readFile = 
   new ReadFileAction(clientName);

/*
* Invoke the action via a doAsPrivileged. This allows the
* action to be executed as the client subject, and it also 
* runs that code as privileged. This means that any permission 
* checking that happens beyond this point applies only to 
* the code being run as the client.
*/
Subject.doAsPrivileged(client, readFile, null);

ReadFileAction.java

ReadFileAction.javaファイルには、ReadFileActionクラスが含まれます。このコンストラクタは、クライアント・ユーザー名のStringを引数に取ります。クライアント・ユーザー名は、ReadFileActionが読取りを試みるファイルのファイル名の作成に使用されます。ファイル名は、次のようになります。

./data/<name>_info.txt
ここで、<name>は対応するレルムを含まないクライアント・ユーザー名になります。たとえば、ユーザー名全体がmjones@KRBNT-OPERATIONS.EXAMPLE.COMの場合、ファイル名は次のようになります。
./data/mjones_info.txt
注: Microsoft Windowsシステムの場合、スラッシュをバックスラッシュで置き換えてください。

ReadFileActionのrunメソッドは、指定されたファイルを読み取り、その内容を出力します。

serverimp.policy

ReadFileActionはファイルを読み取ろうとします。この操作はセキュリティ・チェックの対象になります。ReadFileActionはクライアント・ユーザー(プリンシパル)として実行されると見なされるので、ReadFileActionコード自体のみでなく、クライアント・プリンシパルに対して適切なアクセス権を付与する必要があります。

ReadFileActionクラスがReadFileAction.jarという名前のJARファイル内に配置され、ユーザー・プリンシパル名がmjones@KRBNT-OPERATIONS.EXAMPLE.COMである場合を考えましょう。この場合、ポリシー・ファイル内に次のコードを記述して、アクセス権を付与します。

grant CodeBase "file:./ReadFileAction.jar" 
    Principal javax.security.auth.kerberos.KerberosPrincipal 
        "mjones@KRBNT-OPERATIONS.EXAMPLE.COM" {

    permission java.io.FilePermission "data/mjones_info.txt", 
        "read";
};
serverimp.policyファイルは、SampleServerコードにdoAsPrivilegedメソッドの呼出しに必要なアクセス権javax.security.auth.AuthPermission "doAsPrivileged"を付与すること、および上に示したFilePermissionを付与するプレースホルダーを保持することを除き、前の(「JAAS LoginユーティリティおよびJava GSS-APIを使用したセキュアなメッセージ交換」)チュートリアルのserver.policyファイルと厳密に同一です。次に、FilePermissionを付与するプレースホルダーを示します。
grant CodeBase "file:./ReadFileAction.jar" 
    Principal javax.security.auth.kerberos.KerberosPrincipal 
        "your_user_name@your_realm" {

    permission java.io.FilePermission "data/your_user_name_info.txt", 
        "read";
};

ここで、your_realmは使用するKerberosレルムで、your_user_name@your_realmdata/your_user_name_info.txt内のyour_user_nameは使用するユーザー名で置き換える必要があります。また、Microsoft Windowsシステムの場合、data/your_user_name_info.txt内の「/」を「\」で置き換えてください。

サンプル・コードの実行

クライアントを装うサーバーのサンプル・コードを実行する場合、前のチュートリアルの「SampleClientおよびSampleServerプログラムの実行」に説明されているのと同じ操作を実行してください。ただし、次に示す点が異なります。

クライアントから委譲されたクレデンシャルの使用

クライアントがクレデンシャルをサーバーに委譲する場合、完成度のもっとも高い方法でクライアントを装うことができます。

コンテキスト・アクセプタ(前のチュートリアルのサーバー)とのコンテキストを確立する前に、コンテキスト・イニシエータ(クライアント)によりさまざまなコンテキスト・オプションの設定が行われたことを思い起こしてください。次に示すように、イニシエータがcontextオブジェクトに対し、引数trueを指定してrequestCredDelegメソッドを呼び出す場合を考えましょう。

context.requestCredDeleg(true);
この場合、コンテキスト確立時に、イニシエータのクレデンシャルをアクセプタに委譲することが求められます。

イニシエータからアクセプタにクレデンシャルを委譲することにより、アクセプタが自らをイニシエータのエージェントまたは代理人として認証することが可能になります。

コンテキスト確立後に、アクセプタはクレデンシャルの委譲が実際に行われたかどうかを最初に確認する必要があります。これは、getCredDelegStateメソッドを呼び出すことで実行されます。

boolean delegated = context.getCredDelegState();

クレデンシャルが委譲されている場合、アクセプタはgetDelegCrメソッドを呼び出して、そのクレデンシャルを取得できます。

GSSCredential clientCr = context.getDelegCr();

結果のGSSCredentialオブジェクトを使用して、以降のGSS-APIコンテキストをイニシエータの「委任」として開始できます。たとえば、サーバーは、バックエンド・サーバーに対し、クライアントとして認証できます。バックエンド・サーバーには、中間的サーバーはどれかということよりも、元のクライアントがどれかということの方が重要です。

サーバーは、クライアントとして動作することにより、バックエンド・サーバーとの接続確立、結合セキュリティ・コンテキストの確立、およびメッセージ交換を、クライアントやサーバーと基本的に同じ方法で実行できます。

これを実行する1つの方法は、サーバーがGSSManagerのcreateContextメソッドを呼び出す際に、createContextnullではなく委譲されたクレデンシャルを渡すことです。

別の選択肢として、サーバー・コードが最初にcom.sun.security.jgss.GSSUtil createSubjectメソッドを呼び出し、それに委譲されたクレデンシャルを渡すという方法もあります。このメソッドは、これらのクレデンシャルをデフォルトのクレデンシャルとして含むサブジェクトを返します。そのあと、「JAAS承認」チュートリアルの「サブジェクトのアクセス制御コンテキストへの関連付け」で説明したように、サーバーは、このサブジェクトを現行のAccessControlContextに関連付けることができます。次に、サーバー・コードは、GSSManager createContextメソッドの呼出し時に、null (「現行の」サブジェクトをクレデンシャルとして使用することを指示する)を渡すことができます。違う言い方をすれば、サーバーが実質的にクライアントになります。GSSを使用するそのあとのバックエンド・サーバーへの接続は、前のチュートリアルで説明されるとおりの方法で確立できます。これは、委譲されたクレデンシャルを使用するコードが、デフォルトのローカル・クレデンシャルを使用するコードと同一であることが必要な場合に有用な方法です。

クレデンシャルの委譲に必要なアクセス権

クレデンシャルを委譲するには、コンテキスト・イニシエータ(前のチュートリアルではSampleClient)がjavax.security.auth.kerberos.DelegationPermissionを保持する必要があります。例を次に示します(斜体のプレースホルダーには実際の値を指定)。

permission javax.security.auth.kerberos.DelegationPermission
  "\"service_principal@your_realm\"  
     \"krbtgt/your_realm@your_realm\"";

DelegationPermissionが保持する単一のターゲット内に、引用符で囲まれた2つの項目が含まれることに注目してください。内部の各引用符は、「\」でエスケープされています。このため、最初の項目は次のようになります。

"service_principal@your_realm"
2番目の項目は、次のようになります。
"krbtgt/your_realm@your_realm"

これは、基本的に、クライアントとして実行されるコードに対し、指定されたピアにKerberosチケットを転送するためのアクセス権を付与します。Kerberosチケットは、krbtgt/your_realm@your_realmからのサービス利用に使用します。

表示されているyour_realmは、すべて実際のレルムで置き換えてください。また、service_principal@your_realmも、サービス・プリンシパル名(サーバーを表すサービス・プリンシパルの名前)で置き換えてください。(前のチュートリアルの「Kerberosユーザーおよびサービス・プリンシパルの名前」を参照)。レルムがKRBNT-OPERATIONS.EXAMPLE.COMで、サービス・プリンシパルが「sample/raven.example.com@KRBNT-OPERATIONS.EXAMPLE.COM」である場合を考えてみましょう。この場合、アクセス権をポリシー・ファイル内で次のように表示できます。

permission javax.security.auth.kerberos.DelegationPermission
  "\"sample/raven.example.com@KRBNT-OPERATIONS.EXAMPLE.COM\"  
     \"krbtgt/KRBNT-OPERATIONS.EXAMPLE.COM@KRBNT-OPERATIONS.EXAMPLE.COM\"";


前のチュートリアル チュートリアルの紹介および目次

Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.