ネットワーク操作を実行するアプリケーションは、多くの場合そのトランザクションのセキュリティが保証されることを確認しなければなりません。RPCSEC_GSS アプリケーションプログラミングインタフェース (API) を使用すると、開発者はさまざまなセキュリティ機構 (SEAM や Kerberos V5 を含む) を利用できます。また、同様に重要なことですが、RPCSEC_GSS には、完全性とプライバシのサービスが含まれて、認証よりもセキュリティが強化されています。RPCSEC_GSS は SEAM の一部でもなく、SEAM に固有なものでもありません。したがって、プログラマにとって Kerberos V5 の利点をアプリケーションに活かすには、RPCSEC_GSS を使用することが必要になってきます。事実、RPCSEC_GSS は機構に依存しないため、SEAM や Kerberos V5 をセキュリティ機構として使用していなくても、プライバシと完全性の利点を活かすには RPCSEC_GSS を使用することをお勧めします。
この章は、 RPC プログラミングの十分な知識があることを前提としています。RPC については、『ONC+ 開発ガイド』を参照してください。さらに、この章では概要を説明します。RPCSEC_GSS に固有な情報 (関数やデータ構造など) については、rpcsec_gss(3N) のマニュアルページ、あるいは、この章で説明する関数のマニュアルページを参照してください。
この節では、RPCSEC_GSS API の開発過程を説明します。
RPC がサポートした最初のセキュリティフレーバの 1 つは AUTH_SYS (AUTH_UNIX とも呼ぶ) でした。AUTH_SYS は、ユーザー ID とグループ ID を使用する UNIX 方式の資格を提供して、メッセージの送信者と受信者を識別していました。AUTH_SYS は簡単に実装できましたが、同様に、簡単に資格を偽ることもできました。これは、AUTH_SYS が本当の認証を提供しなかったためです。つまり、サーバーには、そのクライアントが本当に要求しているクライアントであるのかどうかを確認する方法がありませんでした。したがって、AUTH_SYS 下では、比較的簡単に不当な要求をネットワークへ出すことができました。
AUTH_SYS から短期間のうちに次のセキュリティフレーバ AUTH_DES が考え出されました。AUTH_DES は公開鍵認証を元に作成されました。つまり、Diffie-Hellman 鍵交換を使用して、クライアントの公開鍵とサーバーの公開鍵の間で共通鍵を生成します。次に、この共通鍵で DES セッション鍵を暗号化します。そして、サーバーがこの DES セッション鍵の暗号を復号化して、セッションを使用可能にします。
AUTH_DES は AUTH_SYS よりも優れていましたが、使用には制限がありました。主な欠点は、鍵のサイズが今日の暗号化の標準と比べると幾分小さいということでした。
最終的に、もう 1 つの RPC セキュリティフレーバ AUTH_KERB が導入されました。AUTH_KERB は Kerberos V4 を元に作成され、AUTH_DES や AUTH_SYS よりも高いセキュリティを提供します。しかし、資源を多く使用する可能性がありました。
上記セキュリティフレーバについての詳細は、『ONC+ 開発ガイド』を参照してください。
セキュリティを高めるため、新しいネットワーク層 Generic Security Standard API (GSS-API) が追加されました。GSS-API フレームワークは認証よりも優れた 2 つのセキュリティサービスを提供します。
完全性
完全性サービスを利用すると、GSS-API はプログラム間で交換されるメッセージを機構に基づいて認証できます。暗号チェックサムによって、次のことが可能となります。
データの受信者に対する送信者の識別
データの送信者に対する受信者の識別 (相互認証が要求された場合)
転送されるデータ自体の認証
プライバシ
プライバシサービスには完全性サービスが含まれます。さらに、転送されるデータも暗号化されるため、第三者からデータを保護できます。
米国の輸出規制により、プライバシサービスはすべての SEAM ユーザーが利用できるわけではありません。
現在、GSS-API は公開されていません。しかし、一部の GSS-API 機能は RPCSEC_GSS 関数を通じて参照できます。つまり、このような機能は確定されない方法で操作できます。プログラマはこれらの値に直接関わる必要はありません。
RPCSEC_GSS セキュリティフレーバを使用すると、ONC RPC アプリケーションは、GSS-API の特徴を利用できます。RPCSEC_GSS は、次のように GSS-API 層の上に位置します。
RPCSEC_GSS のプログラミングインタフェースを使用すると、ONC RPC アプリケーションは次のことを指定できます。
セキュリティパラダイム。セキュリティ機構が提供するデータ保護はその種類ごとに異なり、同様に、データ保護のレベルも 1 つまたは複数存在する。この場合は、GSS-API がサポートする任意のセキュリティ機構 (Kerberos V5、RSA 公開鍵など)
プライバシまたは完全性のどちらか (あるいは、どちらでもない)。デフォルトは完全性。サービスは機構に依存しない
保護特性 (Quality of Protection)。プライバシまたは完全性のサービスを実装するときに使用される暗号化アルゴリズムのタイプを指定する。各セキュリティ機構は 1 つまたは複数の関連する QOP を持つことができる
アプリケーションは、RPCSEC_GSS が提供する関数を使用して、有効な QOP と機構のリストを取得できます (「さまざまな関数」を参照)。新しいまたは異なる機構や QOP を使用するたびにアプリケーションを変更する必要が生じないように、開発者は機構や QOP をアプリケーションでハードコーディングすることは避けてください。
従来「セキュリティフレーバ」と「認証フレーバ」の意味は同じでした。RPCSEC_GSS の導入とともに、「フレーバ」は現在少し意味が変わりました。フレーバに現在、認証とともに、(完全性またはプライバシの) サービスも含まれています。ただし、現在のところ、その対象は RPCSEC_GSS だけです。
他のフレーバと同様に、ONC RPC アプリケーションは RPCSEC_GSS を使用して、ピアとセキュリティコンテキストを確立し、データを交換し、コンテキストを破棄します。コンテキストが確立されると、アプリケーションは送信されるデータ単位ごとに QOP とサービスを変更できます。
RPCSEC_GSS についての詳細 (RPCSEC_GSS データタイプも含む) は、rpcsec_gss(3N) のマニュアルページを参照してください。
表 8-1で、RPCSEC_GSS コマンドについて説明します。この表は RPCSEC_GSS 関数の概要であり、個々についての説明ではありません。各関数の詳細は、各マニュアルページを参照してください。また、各関数についての概要 (RPCSEC_GSS データ構造のリストも含む) は、rpcsec_gss(3N) のマニュアルページを参照してください。
表 8-1 RPCSEC_GSS の関数動作 | 関数 | 入力 | 出力 |
---|---|---|---|
セキュリティコンテキストを作成する | rpc_gss_seccreate() | CLIENT ハンドル、プリンシパル名、機構、QOP、サービスタイプ | AUTH ハンドル |
コンテキストの QOP またはサービスタイプを変更する | rpc_gss_set_defaults() | 古い QOP、サービス | 新しい QOP、サービス |
セキュリティ変換前のデータの最大サイズを表示する | rpc_gss_max_data_length() | 転送可能なデータの最大サイズ | 変換前のデータの最大サイズ |
セキュリティ変換前のデータの最大サイズを表示する | rpc_gss_svc_max_data_length() | 転送可能なデータの最大サイズ | 変換前のデータの最大サイズ |
サーバープリンシパル名を表示するように設定する | rpc_gss_set_svc_name() | プリンシパル名、RPC プログラム、バージョン番号 | 成功した場合は TRUE |
呼び出し元 (クライアント) の資格を取り出す | rpc_gss_getcred() | svc_req 構造体へのポインタ | UNIX 資格、RPCSEC_GSS 資格、cookie |
(ユーザー作成の) コールバック関数を指定する | rpc_gss_set_callback() | コールバック関数へのポインタ | 成功した場合は TRUE |
一意のパラメータからプリンシパル名の RPCSEC_GSS 構造体を作成する | rpc_gss_get_principal_name() | 機構、ユーザー名、マシン名、ドメイン名 | RPCSEC_GSS プリンシパル名の構造体 |
RPCSEC_GSS ルーチンが失敗したときにエラーコードを取り出す | rpc_gss_get_error() |
| エラー番号が適用可能な場合は、RPCSEC_GSS エラー番号 |
インストールされている機構の文字列を取得する | rpc_gss_get_mechanisms() |
| 有効な機構のリスト |
有効な QOP 文字列を取得する | rpc_gss_get_mech_info() | 機構 | 当該機構に有効な QOP |
サポートされる RPCSEC_GSS のバージョン番号の最大値と最小値 | rpc_gss_get_versions() |
| バージョン番号の最高値と最低値 |
機構がインストールされているかどうかを調べる | rpc_gss_is_installed() | 機構 | インストールされている場合は TRUE |
ASCII 機構を RPC オブジェクト識別子に変換する | rpc_gss_mech_to_oid() | 機構 (文字列) | 機構 (OID) |
ASCII QOP を整数に変換する | rpc_gss_qop_to_num() | QOP (文字列) | QOP (整数) |
コンテキストを作成するには、rpc_gss_seccreate() を呼び出します。この関数は次の引数を取ります。
クライアントのハンドル (たとえば、clnt_create() が戻す)
サーバープリンシパル名 (たとえば、nfs@acme.com)
セッションの機構 (たとえば、Kerberos V5)
セキュリティサービスのタイプ (たとえば、プライバシ)
セッションの QOP
たいてい未定義なまま使用できる (つまり、プログラマが NULL 値を指定できる) 2 つの GSS-API パラメータ
rpc_gss_seccreate() を呼び出すと AUTH 認証ハンドルを戻します。例 8-1 に、rpc_gss_seccreate() で、Kerberos V5 セキュリティ機構と完全性サービスを使用してコンテキストを作成する方法を示します。
CLIENT *clnt; /* クライアントハンドル */ char server_host[] = "foo"; char service_name[] = "nfs@eng.acme.com"; char mech[] = "kerberos_v5"; clnt = clnt_create(server_host, SERVER_PROG, SERV_VERS, "netpath"); clnt->clnt_auth = rpc_gss_seccreate(clnt, service_name, mech, rpc_gss_svc_integrity, NULL, NULL, NULL); . . .
例 8-1 では、次のことに注意してください。
(読みやすいように) 機構は明示的に宣言しています。しかし、プログラムとしては、rpc_gss_get_mechanisms() を使用して利用可能な機構のテーブルから取得する方がより一般的です。
QOP として NULL が渡されているため、この機構のデフォルトが QOP に設定されます。それ以外の場合、機構に有効な値を取得するには rpc_gss_get_mechanisms() を使用できます。詳細は、rpc_gss_get_mechanisms(3N) のマニュアルページを参照してください。
セキュリティサービスのタイプ rpc_gss_svc_integrity は、RPCSEC_GSS タイプ rpc_gss_service_t の enum です。次に、rpc_gss_service_t の形式を示します。
typedef enum { rpc_gss_svc_default = 0, rpc_gss_svc_none = 1, rpc_gss_svc_integrity = 2, rpc_gss_svc_privacy = 3 } rpc_gss_service_t;
デフォルトのセキュリティサービスは完全性であるため、プログラマは rpc_gss_svc_default を指定しても同じ結果を得ることができます。
詳細は、rpc_gss_seccreate(3N) のマニュアルページを参照してください。
コンテキストを設定した後に、転送される個々のデータ単位の QOP やサービスの値をアプリケーションが変更しなければならない場合もあります。たとえば、パスワードを暗号化するがログイン名は暗号化しないプログラムが必要な場合です。このためには、rpc_gss_set_defaults() を使用します。
rpc_gss_set_defaults(clnt->clnt_auth, rpc_gss_svc_privacy, qop); . . .
この場合、セキュリティサービスはプライバシに設定されます (「コンテキストの作成」を参照)。qop は新しい QOP を示す文字列へのポインタです。
コンテキストを通常どおり破棄するには、auth_destroy() を使用します。
サービスと QOP の変更についての詳細は、rpc_gss_set_defaults(3N) のマニュアルページを参照してください。
セキュリティコンテキストを確立および保守するには、2 種類のプリンシパル名が必要です。
サーバープリンシパル名は常に NULL で終わる ASCII 文字列として、service@host の形式で指定されます (たとえば、nfs@eng.acme.com)。
クライアントがセキュリティコンテキストを作成するときは、サーバープリンシパル名を上記の形式で指定します (「コンテキストの作成」を参照)。同様に、サーバーが表現するプリンシパル名を設定する必要がある場合は rpc_gss_set_svc_name() を使用します。この関数は、上記の形式のプリンシパル名を引数として取ります。
(サーバーが受信する) クライアントプリンシパル名は rpc_gss_principal_t 構造体形式から取れます。これは、使用される機構によって決まる、カウントと不定バイト文字列です。この構造体については、rpcsec_gss(3N) のマニュアルページを参照してください。
サーバーには、起動時、そのプリンシパル名を設定しておく必要があります (サーバーは複数のプリンシパルとして動作する可能性があります)。rpc_gss_set_svc_name() を使用すればプリンシパル名が設定されます。
char *principal, *mechanism; u_int req_time; principal = "nfs@eng.acme.com"; mechanism = "kerberos_v5"; req_time = 10000; /* 資格が有効である時間 */ rpc_gss_set_svc_name(principal, mechanism, req_time, SERV_PROG, SERV_VERS);
Kerberos は req_time パラメータを無視します。他の認証システムはこのパラメータを使用できます。
詳細は、rpc_gss_set_svc_name(3N) のマニュアルページを参照してください。
サーバーはクライアントプリンシパル名を操作できる必要があります。たとえば、クライアントプリンシパル名をアクセス制御リストと比較するときや、そのクライアントの資格が存在するかどうか UNIX 資格を検索するときなどです。このようなプリンシパル名は rpc_gss_principal_t 構造体ポインタの形式で保存されます (rpc_gss_principal_t についての詳細は、rpcsec_gss(3N) のマニュアルページを参照)。サーバーが受信したプリンシパル名を既知のエンティティの名前と比較したい場合、受信した形式でプリンシパル名を生成できる必要があります。
rpc_gss_get_principal_name() の呼び出しでは、ネットワーク上でユーザーを一意に識別するためのいくつかのパラメータを入力する必要があり、プリンシパル名を rpc_gss_principal_t 構造体ポインタとして生成します。
rpc_gss_principal_t *principal; rpc_gss_get_principal_name(principal, mechanism, name, node, domain); . . .
次に、rpc_gss_get_principal_name() の引数を示します。
principal は、設定する rpc_gss_principal_t 構造体へのポインタです。
mechanism は使用するセキュリティ機構です (生成されるプリンシパル名は機構に依存します)。
name はユーザー名またはサービス名です (joeh や nfs など)。あるいは、ユーザー定義のアプリケーション名にもなります。
node は UNIX マシン名などです。
domain は DNS、NIS、または NIS+ のドメイン名、あるいは Kerberos レルムなどです。
各セキュリティ機構には異なる識別パラメータが必要です。たとえば、Kerberos V5 には、ユーザー名と (オプションとして) 修飾されたノードとドメイン名 (Kerberos の用語では、ホストとレルムの名前) が必要です。
詳細は、rpc_gss_get_principal_name(3N) のマニュアルページを参照してください。
プリンシパル名を解放するには、free() ライブラリを呼び出します。
サーバーはクライアントの資格を取り出せる必要があります。rpc_gss_getcred() 関数 (例 8-5 を参照) を使用すると、サーバーは UNIX 資格または RPCSEC_GSS 資格のどちらか (あるいは、両方) を取り出すことができます。関数が成功した場合、これらの資格は次の 2 つの引数に設定されます。1 つは、rpc_gss_ucred_t 構造体へのポインタで、呼び出し側の UNIX 資格が (存在すれば) 入ります。
typedef struct { uid_t uid; /* ユーザー ID */ gid_t gid; /* グループ ID */ short gidlen; git_t *gidlist; /* グループのリスト */ } rpc_gss_ucred_t;
もう 1 つの引数は、rpc_gss_raw_cred_t 構造体へのポインタです。
typedef struct { u_int version; /* RPCSEC_GSS プログラムのバージョン */ char *mechanism; char *qop; rpc_gss_principal_t client_principal; /* クライアントプリンシパル名 */ char *svc_principal; /* サーバープリンシパル名 */ rpc_gss_service_t service; /* プライバシと完全性の enum */ } rpc_gss_rawcred_t;rpc_gss_principal_t() 構造体とその作成方法については、「クライアントプリンシパル名の生成」を参照してください。rpc_gss_rawcred_t にはクライアントとサーバーの両方のプリンシパル名が入っているため、rpc_gss_getcred() はこれら両方を戻すことができます。
例 8-5 は、サーバー側のディスパッチ手順の簡単な例です。この例では、サーバーが呼び出し側の資格を取得しています。この手順は、呼び出し側の UNIX 資格を取得します。そして、rpc_gss_rcred_t 引数中の機構、QOP、およびサービスのタイプを使用して、ユーザーの識別情報を確認します。
static void server_prog(struct svc_req *rqstp, SVCXPRT *xprt) { rpc_gss_ucred_t *ucred; rpc_gss_rawcred_t *rcred; if (rqst->rq_proq == NULLPROC) { svc_sendreply(xprt, xdr_void, NULL); return; } /* * 他の認証をすべて認証する。 */ */ switch (rqstp->rq_cred.oa_flavor) { case RPCSEC_GSS: /* * 資格情報を取得する。 */ rpc_gss_getcred(rqstp, &rcred, &ucred, NULL); /* * 自分の config ファイルを参照して、受信した * セキュリティパラメータによるアクセスがユーザーに * 許可されているかどうかを確認する。 */ if (!authenticate_user(ucred->uid, rcred->mechanism, rcred->qop, rcred->service)) { svcerr_weakauth(xprt); return; } break; /* ユーザーに許可する。 */ default: svcerr_weakauth(xprt); return; } /* switch 文の終了 */ switch (rqstp->rq_proq) { case SERV_PROC1: . . . } /* 通常の要求処理。応答を送信するなど。 */ return; } |
詳細は、rpc_gss_getcred(3N) のマニュアルページを参照してください。
例 8-5 では、rpc_gss_getcred() への最後の引数 (ここでは NULL) はユーザーが定義した cookie です。この値は、戻り時、サーバーがコンテキストを作成したときに指定した値になります。この cookie は 4 バイトの値であり、アプリケーションに適切な方法で使用できます (RPC ではこの値を使用しません)。たとえば、cookie をコンテキストインジケータを表す構造体へのポインタ (または、インデックス) とすることも可能です。つまり、サーバーは要求ごとにこの値を計算するのではなく、コンテキスト作成時にこの値を計算します。したがって、要求処理時間を短縮できます。
cookie を使用できるもう 1 つの場所はコールバックです。サーバーが (ユーザーが定義した) コールバックを指定してコンテキストが最初に使用されたときを知るには、rpc_gss_set_callback() 関数を使用します。コールバックが呼び出されるのは、コンテキストがデータ交換用に初めて使用されて、そのコンテキストが指定したプログラムとバージョン用に確立された後です。
次に、ユーザー定義のコールバックルーチンの形式を示します。
2 番目と 3 番目の引数 deleg と gss_context は GSS-API データ型であり、現在公開されていません。したがって、コールバック関数はこれらの引数を無視できます。簡単に言えば、プログラムが GSS-API 操作をコンテキストに実行したい (つまり、受け入れ基準をテストしたい) 場合は、deleg は委託されるピアの識別情報であり、gss_context は GSS-API コンテキストへのポインタとなります。cookie 引数については、上記を参照してください。
lock 引数は、rpc_gss_lock_t 構造体へのポインタです。
typedef struct { bool_t locked; rpc_gss_rawcred_t *raw_cred; } rpc_gss_lock_t;このパラメータを使用すると、サーバーは特定の QOP とサービスをセッションで実施できます。QOP とサービスは rpc_gss_rawcred_t 構造体 (例 8-5 を参照) にあります (サーバーでサービスと QOP の値を変更しないでください)。ユーザー定義のコールバックが呼び出されると、locked フィールドは FALSE に設定されます。サーバーが locked を TRUE に設定した場合、rpc_gss_rawcred_t 構造体にある値と一致する QOP とサービスの値を持つ要求だけが受け入れられます。
詳細は、rpc_gss_set_callback(3N) のマニュアルページを参照してください。
2 つの関数 rpc_gss_max_data_length() と rpc_gss_svc_max_data_length() は、データがセキュリティ測定で変換されてネットワークに送信される前までのデータのサイズを調べるときに使用します。つまり、セキュリティ変換 (暗号化など) を行うと、通常、転送されるデータのサイズが変わります (ほとんどの場合、大きくなります)。データが使用可能なサイズよりも大きくならないようにするために、上記 2 つの関数 (前者はクライアント側のバージョンで、後者はサーバー側のバージョン) は、指定した転送に対する変換前のデータの最大サイズを戻します。
詳細は、rpc_gss_max_data_length(3N) と rpc_gss_svc_max_data_length(3N) のマニュアルページを参照してください。
次に、インストールされているセキュリティシステムについての情報を取得するための関数を示します。
rpc_gss_get_mechanisms() は、インストールされているセキュリティ機構のリストを戻します。
rpc_gss_is_installed() は、指定した機構がインストールされているかどうかを調べます。
rpc_gss_get_mech_info() は、指定した機構に有効な QOP を戻します。
これらの関数を使用すると、プログラマはセキュリティパラメータを自分のアプリケーションにハードコーディングする必要がなくなります。すべての RPCSEC_GSS 関数のリストについては、表 8-1 と rpcsec_gss(3N) のマニュアルページを参照してください。
RPCSEC_GSS はいくつかのファイルに情報を格納します。
要求に関連するクライアントの資格を取り出すとき、サーバーはクライアントプリンシパル名 (rpc_gss_principal_t 構造体ポインタの形式) またはローカル UNIX 資格 (ユーザ ID) のどちらかを取得できます。NFS などのサービスでは、アクセスを許可するかどうかを決定するのにローカルの UNIX 資格が必要です。しかし、資格を必要としないサービスでは、プリンシパル名を rpc_gss_principal_t 構造体として直接独自のアクセス制御リストに格納します。
クライアントのネットワーク資格 (そのプリンシパル名) とローカル UNIX 資格は自動的にはマッピングされません。ローカルのセキュリティ管理者が明示的に設定する必要があります。
gsscred ファイルには、クライアントの UNIX とネットワーク (たとえば、Kerberos V5) の両方の資格が入っています。後者は rpc_gss_principal_t 構造体の Hex-ASCII 表記です。gsscred ファイルには XFN 経由でアクセスします。したがって、このテーブルは、XFN がサポートする files、NIS、NIS+ で、さらには将来の任意のネームサービスで実装できます。XFN 階層において、このテーブルは this_org_unit/service/gsscred で表示できます。gsscred テーブルを保守するには、gsscred ユーティリティを使用します。gsscred ユーティリティを使用すると、管理者はユーザーや機構を追加および削除できます。
便宜上、RPCSEC_GSS は文字列リテラルを使用して、機構と QOP (保護特性) パラメータを記述します。しかし、実際の機構自体では、機構をオブジェクト識別子として記述し、QOP を 32 ビット整数として記述する必要があります。さらに、機構ごとに、その機構のサービスを実装する共有ライブラリを指定する必要があります。
/etc/gss/mech ファイルは、システムにインストールされているすべての機構について、機構名 (ASCII)、機構の OID、その機構が提供するサービスを実装する共有ライブラリ、(オプションで) サービスを実装するカーネルモジュールなどの情報を格納します。次に、この行の例を示します。
kerberos_v5 1.2.840.113554.1.2.2 gl/mech_krb5.so gl_kmech_krb5 |
/etc/gss/qop ファイルは、インストールされているすべての機構に対して、各機構がサポートするすべての QOP を、ASCII 文字列とそれに対応する 32 ビット整数で格納します。
/etc/gss/mech と /etc/gss/qop は両方とも、初めてセキュリティ機構をシステムにインストールするときに作成されます。
多くのカーネル内 RPC ルーチンは文字列型でない値で機構と QOP を記述するため、このようなカーネル内 RPC ルーチンをアプリケーションで利用する場合は、rpc_gss_mech_to_oid() と rpc_gss_qop_to_num() 関数を使用して、これらのパラメータに対応する文字列型でない値を取得します。