ONC+ 開発ガイド

AUTH_KERB 認証

AUTH_KERB の S 実装に使用されたカーネルは、Kerberos のコードをオペレーティングシステムのカーネルへコンパイルしないで、kerbd という代理の RPC デーモンを使用します。このデーモンは、以下の 3 つの手続きをエクスポートします。

Kerberos の内容を的確に説明するには、現在 Kerberos を実装しているサービスであるネットワークファイルシステム (NFS) を例として使用するのが良いでしょう。 サーバー s の NFS サービスは、nfs.s という周知の主体名を持つとします。 クライアント c の特権ユーザーは、root という一次名と、インスタンス c をもっているとします。AUTH_DES の場合とは異なり、ユーザーのチケット発行用のチケットの期限が切れた場合は、kinit() を再び呼び出さなければならないことに注意してください。Kerberos マウントの NFS サービスは、新しいチケット発行用のチケットを獲得するまで成功しません。

NFS マウント例

この節全体を通して、AUTH_KERB を使用した NFS マウント要求について説明します。マウント要求は、ルートで実行されるので、ユーザーの識別情報は、root.c.になります。

クライアント c は、マウントするディレクトリのファイルハンドルを獲得するために、サーバー sMOUNTPROC_MOUNT 要求を実行します。クライアントのマウントプログラムは、ファイルハンドル、mountflavor、時間同期アドレス、サーバーの既知の主体名である nfs.s を、クライアントのカーネルに渡して、NFS マウントシステムコールを実行します。 次に、クライアントのカーネルが時間同期ホストでサーバーに接続し、クライアント/ サーバー間の時間差を取得します。

クライアントのカーネルは、次の RPC 呼び出しを行います。

  1. チケットとセッション鍵を取得するためのローカル kerbd への KSETKCRED

  2. フルネーム資格とベリファイアを使用する、サーバーの NFS サービスへの NFSPROC_GETATTR。 サーバーは、呼び出しを受信し、ローカルの kerbdKGETKCRED 呼び出しを行なってクライアントのチケットを検査します。

サーバーの kerbd と Kerberos ライブラリは、チケットの暗号を解除し、主体名および DES セッション鍵を他のデータの中に返します。サーバーは、チケットがまだ有効であることをチェックし、セッション鍵を使用して資格、ベリファイアの DES の暗号化された部分を複号化し、ベリファイアが有効であることを検査します。

この時に返される可能性のある Kerberos 認証エラーは、次のとおりです。

エラーを受信しない場合、サーバーはクライアントの識別情報をキャッシュに書き込み、NFS 回答に返されるニックネームである小さい整数を割り当てます。その時サーバーは、クライアントがサーバーと同じ領域かどうかをチェックします。クライアントがサーバーと同じ領域の場合、サーバーは、KGETUCRED をローカルの kerbd に呼び出して、主体名を UNIX の資格に変換します。以前の名前が変換できない場合は、ユーザーは匿名と指定されます。サーバーは、ファイルシステムのエクスポート情報に対するこれらの資格を検査します。次の場合について検討します。

  1. KGETUCRED 呼び出しが失敗し、匿名の要求が受け入れられた場合、匿名のユーザーに UNIX 資格が割り当てられます。

  2. KGETUCRED 呼び出しが失敗し、匿名の要求が受け入れられない場合、NFS 呼び出しは失敗し、AUTH_TOOWEAK が返されます。

  3. KGETUCRED 呼び出しが成功する場合は、資格が割り当てられ、その後にルートのアクセス権のチェックも含む、正常な保護検査が行われます。

次に、サーバーが、ニックネームおよびサーバーのベリファイアを組み込んで NFS 回答を送信します。クライアントは回答を受信し、ベリファイアの複号化および妥当性検査を行い、これからの呼び出しのためにニックネームを格納します。クライアントがサーバーに 2 番目の NFS 呼び出しを行うと、先にサーバーに書込まれた呼び出しが繰り返されます。クライアントのカーネルが、以前に記述されたニックネーム資格およびベリファイアを使用して、サーバーの NFS サービスに NFSPROC_STATVFS 呼び出しを行います。サーバーは呼び出しを受信し、ニックネームの妥当性検査を行います。これが範囲外であれば、エラー AUTH_BADCRED を返します。サーバーは、獲得したばかりのセッション鍵を使用して、ベリファイアの DES の暗号化された部分を複号化し、ベリファイアの妥当性検査を行います。

この時に返される可能性のある Kerberos 認証エラーは、次のとおりです。

エラーが受信されない場合、サーバーは、ニックネームを使用して、呼び出し側の UNIX 資格を検出します。それから、サーバーはファイルシステムのエクスポート情報に対するこれらの資格を検査し、ニックネームおよびサーバーのベリファイアを組み込んだ NFS 回答を送信します。クライアントは回答を受信し、ベリファイアの複号化および妥当性検査を行い、これからの呼び出しのためにニックネームを格納します。最後に、クライアントの NFS マウントシステムコールが返り、要求が終了します。

KERB 認証プロトコル

次の AUTH_KERB の例は AUTH_DES と多くの点で似ていることが次のコーディング例からわかります。両者の違いに注意してください。


例 B–3 AUTH_DES 認証プロトコル

#define AUTH_KERB 4
/*
 * 資格は 2 種類ある。1 つはクライアントが
 *  
Kerberos チケット (以前に暗号化された) を送信する際の資格で、
 * もう 1 つはサーバーがクライアントに与えた
 * ニックネーム (単純な符号なし整数)。
 * クライアントはサーバーとの最初のトランザクションではそのフルネームを
 * 使用する必要がある。その際、サーバーはクライアントへニックネームを返す。
 * クライアントはそれ以降のサーバーとのすべてのトランザクションで
 * このニックネームを使用できる (チケットが期限切れとなるまで)。
 * ニックネームは使用しなくてもかまいませんが、パフォーマンス上の理由から
 * 使用するほうが望ましい。
 */
enum authkerb_namekind {
 	AKN_FULLNAME = 0,
 	AKN_NICKNAME = 1
 };
 
/*
 * フルネームには暗号化されたサービスチケットと
 * ウィンドウが含まれる。ウィンドウは資格の有効期限。
 * ベリファイアのタイムスタンプに記載された時刻に
 * ウィンドウの時間を加えた時刻が過ぎると、サーバーは要求を期限切れとし、
 * 許可しない。要求を再送信しないように、最初のトランザクションを除き、
 * サーバーは前回よりも大きな値のタイムスタンプを要求する。
 * 最初のトランザクションでは、サーバーはタイムスタンプを
 * チェックする代わりに、ウィンドウベリファイアがウィンドウより 1 だけ
 * 少ないかどうかを調べる。
 */
struct authkerb_fullname {
 	KTEXT_ST ticket;              /* Kerberos サービスチケット */
 	unsigned long window;        /* 暗号化されたウィンドウ */
};                             
/*
 * 資格はフルネームまたはニックネームのどちらか。
 */
union authkerb_credswitch(authkerb_namekind akc_namekind){
 	case AKN_FULLNAME:
 		authkerb_fullname akc_fullname;
 	case AKN_NICKNAME:
 		unsigned long akc_nickname;
};
 
/*
 * タイムスタンプは 1970 年 1 月 1 日午前 0 時からの秒数を符号化したもの。
 */
struct timestamp {
 	unsigned long seconds;      /* 秒 */
 	unsigned long useconds;     /* マイクロ秒 */
};
 
/*
 * ベリファイア: クライアント側 
 */
struct authkerb_verf_clnt {
 	timestamp akv_timestamp;   /* 暗号化されたタイムスタンプ */
 	unsigned long akv_winverf;  /* 暗号化されたウィンドウベリファイア */
};
 
/*
 * ベリファイア: サーバー側
 * サーバーは、クライアントがサーバーに渡したタイムスタンプと同じ
 * タイムスタンプ (暗号化された) を返す。 サーバーはまた、
 * クライアントへ今後のトランザクションで使用する
 * ニックネーム (暗号化されていない) を通知する。 
 */
struct authkerb_verf_svr {
 	timestamp akv_timeverf;    /* 暗号化されたベリファイア */
 	unsigned long akv_nickname; /* クライアントの新しいニックネーム */
};