SASL (Simple Authentication and Security Layer、簡易認証セキュリティー層) は、セキュリティーフレームワークの 1 つです。SASL (「ササル」と発音) は、接続ベースのプロトコルに対して認証サービスを提供するほか、オプションで整合性サービスと機密性サービスも提供します。この章で扱う内容は、次のとおりです。
SASL は、アプリケーションと共有ライブラリの開発者に対し、認証、データ整合性検査、および暗号化を行うための機構を提供します。SASL を使用すると、開発者は汎用的な API に基づいたコーディングを行えます。これにより、特定の機構への依存を回避できます。SASL は特に、プロトコル IMAP、SMTP、ACAP、および LDAP を使用するアプリケーションに適しています。というのも、これらのプロトコルはすべて SASL をサポートしているからです。SASLは RFC 2222 に記述されています。
SASL ライブラリは libsasl と呼ばれます。libsasl は、正しく記述された SASL コンシューマアプリケーションがシステム上で利用可能な任意の SASL プラグインを使用できるようにするためのフレームワークです。「プラグイン」という用語は、SASL にサービスを提供するオブジェクトを指すために使用されます。プラグインは libsasl の外部に存在します。SASL プラグインを使用すれば、認証およびセキュリティー保護、名前の標準化、および補助プロパティー (パスワードなど) の検索を行えます。暗号化アルゴリズムは libsasl 内にではなくプラグイン内に格納されます。
libsasl は、コンシューマ (アプリケーションとライブラリ) に対してアプリケーションプログラミングインタフェース (API) を提供します。サービスプロバイダインタフェース (SPI) は、プラグインが libsasl にサービスを提供するために用意されたものです。libsasl はネットワークやプロトコルを認識しません。したがって、クライアントとサーバー間でデータの送受信を行うのは、アプリケーションの責任です。
SASL はユーザーに対して 2 つの重要な識別子を使用します。「認証 ID」(authid) は、ユーザーを認証するためのユーザー ID です。認証 ID は、システムへのアクセスをユーザーに許可します。「承認 ID」 (userid) は、ユーザーが特定のオプションの使用を許可されているかどうかを検査する際に使用されます。
SASL クライアントアプリケーションと SASL サーバーアプリケーションは、共通の SASL 機構とセキュリティーレベルの折衝を行います。 通常の場合、SASL サーバーアプリケーションが、自身が受け入れ可能な認証機構のリストをクライアントに送信します。その後、SASL クライアントアプリケーションは、自身の要求にもっとも合う認証機構を決定できます。この時点から、両者で合意した認証機構に基づいて、「クライアントとサーバー間における一連の SASL 認証データの交換」として認証が実行されます。この交換は、認証に成功または失敗するか、あるいはクライアントかサーバーによって処理が中止されるまで継続されます。
認証処理中に、SASL 認証機構はセキュリティー層の折衝を行えます。セキュリティー層が選択された場合、SASL セッションが継続する限りその層を使用する必要があります。
クライアントアプリケーションとサーバーアプリケーションはそれぞれ、libsasl のローカルコピーに対して SASL API 経由で呼び出しを行います。libsasl は、SASL サービスプロバイダインタフェース (SPI) 経由で SASL 機構と通信します。
セキュリティー機構プラグインは、libsasl にセキュリティーサービスを提供します。セキュリティー機構が提供する一般的な機能のいくつかを、次に示します。
クライアント側での認証
サーバー側での認証
整合性 (転送データが変更されていないことの検査)
機密性 (転送データの暗号化と復号化)
「SSF」(セキュリティー強度係数) は、SASL 保護の強さを示します。機構がセキュリティー層をサポートしている場合、クライアントとサーバーは SSF の折衝を行います。SSF の値は、SASL 折衝開始前に指定されたセキュリティープロパティーに基づいて決められます。折衝で 0 以外の SSF が決定された場合、認証完了時にクライアントとサーバーの両方でその機構のセキュリティー層を使用する必要があります。SSF は次のような整数値で表現されます。
0 – 保護なし。
1 – 整合性検査のみ。
>1 – 認証、整合性、および機密性のサポート。その数値は暗号化の鍵の長さを表します。
機密性と整合性の処理はセキュリティー機構によって実行されます。libsasl はそれらの要求を調整する役割を果たします。
SASL クライアントは折衝時に、最大の SSF を持つ機構を選択します。ただし、実際に選択された SASL 機構はその後、それよりも低い SSF の折衝を行う可能性があります。
アプリケーションは libsasl API 経由で libsasl と通信します。libsasl はアプリケーションによって登録されたコールバックを介して追加情報の要求を行えます。アプリケーションがプラグインを直接呼び出すことはなく、必ず libsasl 経由でプラグインを呼び出します。プラグインは通常、libsasl フレームワークのプラグインを呼び出します。すると、フレームワークがアプリケーションのコールバックを呼び出します。SASL プラグインはアプリケーションを直接呼び出すことも可能です。ただしその際、アプリケーションは、プラグインからの呼び出しと libsasl からの呼び出しを区別できません。
コールバックは、次のようにさまざまな分野で役に立ちます。
libsasl はコールバックを使用することで、認証を完了するのに必要な情報を取得できます。
libsasl コンシューマアプリケーションはコールバックを使用することで、プラグインや構成データの検索パスを変更したり、ファイルを検証したり、さまざまなデフォルト動作を変更したりできます。
サーバーはコールバックを使用することで、承認ポリシーを変更したり、異なるパスワード検証方式を提供したり、パスワード変更情報を取得したりできます。
クライアントとサーバーはコールバックを使用することで、エラーメッセージの言語を指定できます。
アプリケーションが登録するコールバックには、 大域とセッションの 2 種類があります。さらに、libsasl では多数のコールバック ID が定義されており、それらの ID を使ってさまざまな種類のコールバックを登録できるようになっています。特定の種類のコールバックが登録されていない場合、libsasl はデフォルトの動作を実行します。
セッションコールバックは大域コールバックよりも優先されます。ある ID に対してセッションコールバックが指定された場合、そのセッションでは大域コールバックは呼び出されません。コールバックによっては、大域でなければなりません。これは、それらのコールバックがセッションの外側で呼び出されるためです。次のような処理は大域コールバックにする必要があります。
プラグイン読み込み時の検索パスの決定
プラグインの検証
構成データの検索
エラーメッセージのロギング
libsasl またはプラグインに関するその他の大域構成
特定の SASL コールバック ID の SASL コールバックとして、NULL コールバック関数を登録できます。NULL コールバック関数は、必要なデータを提供する準備がクライアント側に整っていることを示します。SASL コールバック ID には必ず、接頭辞 SASL_CB_ が付いています。
SASL が提供する次のコールバックは、クライアントまたはサーバーで使用できます。
SASL オプションを取得します。オプションを設定すると、libsasl(3LIB) とその関連プラグインの動作が変更されます。クライアントまたはサーバーで使用できます。
SASL プラグイン検索パスのコロン区切りリストを取得します。デフォルトのパスは次のとおりです。
32 ビット SPARC アーキテクチャー: /usr/lib/sasl
32 ビット x86 アーキテクチャー: /usr/lib/sasl
64 ビット SPARC アーキテクチャー: /usr/lib/sasl/sparcv9
x64 アーキテクチャー: /usr/lib/sasl/amd64
優先順に並んだ RFC 1766 言語コードのコンマ区切りリストを指定します。このリストは、クライアントおよびサーバーのエラーメッセージとクライアントのプロンプトで使用されます。デフォルトは i-default です。
SASL が提供する次のコールバックは、クライアント専用です。
クライアントのユーザー名を取得します。このユーザー名は承認 ID と同一です。LOGNAME 環境変数がデフォルトになります。
SASL が提供する次のコールバックは、サーバー専用です。
認証されたユーザーが指定されたユーザーの代理役として承認されているかどうかを検査します。このコールバックが登録されていない場合、認証されたユーザーと承認されるべきユーザーが同一でなければなりません。これらの ID が同一でない場合、認証が失敗します。標準でない承認ポリシーを処理するには、サーバーアプリケーションを使用してください。
最初の SASL ライブラリ初期化時に、サーバーとクライアントは必要な大域コールバックのすべてを宣言します。大域コールバックが利用可能なのは、SASL セッション初期化前と SASL セッション中です。セッション初期化前には、プラグインの読み込み、データのロギング、構成ファイルの読み取りなどの作業がコールバックによって実行されます。SASL セッションの開始時に、追加のコールバックを宣言できます。そうしたコールバックは、必要に応じて大域コールバックを上書きできます。
libsasl は、SASL 接続の「コンテキスト」内に、SASL クライアントと SASL サーバーの両方に対する各 SASL セッションの状態を格納します。各コンテキストは、同時に 1 つの認証および 1 つのセキュリティーセッションでしか使用できません。格納される状態としては、次のような情報があります。
サービス、名前情報およびアドレス情報、プロトコルフラグなどの接続情報
接続に固有のコールバック
SASL SSF の折衝に使われるセキュリティープロパティー
認証の状態とセキュリティー層の情報
次の図に、SASL ライフサイクル内に含まれる各種ステップを示します。クライアントの動作は図の左側に、サーバーの動作は右側に、それぞれ示してあります。その間に描かれた矢印は、外部接続経由でのクライアントとサーバー間の相互作用を示しています。
次からの節で、このライフサイクル内のステップについて説明します。
クライアントは sasl_client_init() を呼び出すことで libsasl をクライアント用に初期化します。サーバーは、sasl_server_init() を呼び出すことで libsasl をサーバー用に初期化します。
sasl_client_init() の実行時には、SASL クライアントプラグイン、クライアントの機構プラグイン、およびクライアントの標準化プラグインが読み込まれます。同様に、sasl_server_init() の呼び出し時には、SASL サーバープラグイン、サーバーの機構プラグイン、サーバーの標準化プラグイン、およびサーバーの auxprop プラグインが読み込まれます。sasl_client_init() の呼び出し後、追加のクライアントプラグインは、sasl_client_add_plugin() と sasl_canonuser_add_plugin() を使用して追加できます。サーバー側では、sasl_server_init() の呼び出し後、追加のサーバープラグインは、sasl_server_add_plugin()、sasl_canonuser_add_plugin()、および sasl_auxprop_add_plugin() を使用して追加できます。SASL 機構は Solaris ソフトウェアの次のディレクトリに格納されます。
32 ビット SPARC アーキテクチャー: /usr/lib/sasl
32 ビット x86 アーキテクチャー: /usr/lib/sasl
64 ビット SPARC アーキテクチャー: /usr/lib/sasl/sparcv9
x64 アーキテクチャー: /usr/lib/sasl/amd64
ただし、SASL_CB_GETPATH コールバックを使えば、このデフォルトの場所を変更できます。
この時点で、必要な大域コールバックのすべてが設定されています。SASL クライアントと SASL サーバーには、次のコールバックを含めることができます。
SASL_CB_GETOPT
SASL_CB_LOG
SASL_CB_GETPATH
SASL_CB_VERIFYFILE
さらに SASL サーバーには、SASL_CB_GETCONF コールバックも含めることができます。
サーバーとクライアントはプロトコル経由で接続を確立します。SASL による認証を行う場合、サーバーとクライアントは SASL 接続コンテキストを作成します。それには、sasl_server_new() と sasl_client_new() をそれぞれ使用します。SASL クライアントと SASL サーバーは、sasl_setprop() を使って機構に対するセキュリティー制約プロパティーを設定できます。これにより、SASL コンシューマアプリケーションは、指定された SASL 接続コンテキストの最小 SSF、最大 SSF、およびセキュリティープロパティーを決定できます。
#define SASL_SEC_NOPLAINTEXT 0x0001 #define SASL_SEC_NOACTIVE 0x0002 #define SASL_SEC_NODICTIONARY 0x0004 #define SASL_SEC_FORWARD_SECRECY 0x0008 #define SASL_SEC_NOANONYMOUS 0x0010 #define SASL_SEC_PASS_CREDENTIALS 0x0020 #define SASL_SEC_MUTUAL_AUTH 0x0040
認証とセキュリティー層は、クライアント/サーバー間のプロトコルや libsasl 以外の機構を使って提供してもかまいません。そうした場合、sasl_setprop() 経由で外部認証 ID や外部 SSF を設定できます。たとえば、プロトコルが SSL を使用してサーバーに対するクライアント認証を行う場合を考えます。この場合、外部認証 ID をクライアントの被認証者名として、外部 SSF を鍵のサイズとして、それぞれ使用できます。
サーバー側では、libsasl が、セキュリティープロパティーと外部 SSF に従って利用可能な SASL 機構を決定します。クライアントは、その利用可能な SASL 機構を SASL サーバーからプロトコル経由で取得します。
SASL サーバー側で SASL 接続コンテキストを作成するには、sasl_server_new() を呼び出す必要があります。すでに使われていない既存の SASL 接続コンテキストを再利用することも可能です。ただし、その場合は次のパラメータをリセットする必要があります。
#define SASL_DEFUSERREALM 3 /* default realm passed to server_new or set with setprop */ #define SASL_IPLOCALPORT 8 /* iplocalport string passed to server_new */ #define SASL_IPREMOTEPORT 9 /* ipremoteport string passed to server_new */ #define SASL_SERVICE 12 /* service passed to sasl_*_new */ #define SASL_SERVERFQDN 13 /* serverFQDN passed to sasl_*_new */
sasl_client_new() と sasl_server_new() に対するパラメータは、コールバックとプロトコルフラグ以外はすべて変更可能です。
また、サーバーとクライアントは、セキュリティーポリシーの確立や接続固有パラメータの設定も行えます。それには、sasl_setprop() を使って次のプロパティーを指定します。
#define SASL_SSF_EXTERNAL 100 /* external SSF active (sasl_ssf_t *) */ #define SASL_SEC_PROPS 101 /* sasl_security_properties_t */ #define SASL_AUTH_EXTERNAL 102 /* external authentication ID (const char *) */
SASL_SSF_EXTERNAL – 強度係数 (鍵のビット数) の設定用
SASL_SEC_PROPS – セキュリティーポリシーの定義用
SASL_AUTH_EXTERNAL – 外部認証 ID
サーバーは、sasl_listmech() を呼び出すことで、セキュリティーポリシーを満たす利用可能な SASL 機構のリストを取得できます。クライアントは通常、利用可能な機構リストをプロトコルに依存した方法でサーバーから取得できます。
SASL セッションの初期化を図示したのが、次の図です。この図と後続の図では、プロトコル経由でデータを転送した後の検査処理は、図を単純化する目的で省略しています。
認証時にクライアントとサーバーで実行されるステップの数は、使用されるセキュリティー機構ごとに異なります。SASL クライアントは、使用すべきセキュリティー機構のリストを指定して sasl_client_start() を呼び出します。このリストは通常、サーバーから送られてきます。libsasl は、利用可能な機構とクライアントのセキュリティーポリシーに基づいて、この SASL セッションに最適な機構を選択します。クライアントのセキュリティーポリシーは、許可される機構を制御します。そして、選択された機構が sasl_client_start() から返されます。クライアントのセキュリティー機構は、認証時に追加情報を必要とする場合があります。登録されたコールバック関数が NULL でない限り、libsasl はその指定されたコールバックを呼び出します。コールバック関数が NULL である場合、libsasl は、SASL_INTERACT と必要情報の要求を返します。SASL_INTERACT が返された場合、要求された情報を指定して sasl_client_start() を呼び出す必要があります。
sasl_client_start() から SASL_CONTINUE または SASL_OK が返された場合、クライアントは、選択された機構と結果の認証データを、サーバーに送信する必要があります。その他の値が返された場合、何らかのエラーが発生しています。たとえば、利用可能な機構が存在しない、などです。
サーバーは、クライアントによって選択された機構と、認証データを受信します。続いてサーバーは、sasl_server_start() を使ってこのセッション用に機構データを初期化します。また、sasl_server_start() は認証データの処理も行います。sasl_server_start() から SASL_CONTINUE または SASL_OK が返された場合、サーバーは認証データを送信します。sasl_server_start() からその他の値が返された場合、機構の受け入れに失敗した、認証に失敗した、など、何らかのエラーが発生しています。その認証は中止する必要があります。その SASL コンテキストは、解放するか、または再利用する必要があります。
認証プロセスのうち、以上の部分を図示したのが、次の図です。
サーバー側の sasl_server_start() 呼び出しから SASL_CONTINUE が返された場合、サーバーは必要な認証情報をすべて取得するために、クライアントとの通信を継続します。後続のステップ数は機構ごとに異なります。必要に応じて、クライアントは sasl_client_step() を呼び出すことで、サーバーからの認証データを処理し、応答を生成します。同様に、サーバーは sasl_server_step() を呼び出すことで、クライアントからの認証データを処理し、応答を生成できます。この交換は、認証が完了するか、エラーが発生するまで継続されます。SASL_OK が返された場合、それはクライアント側またはサーバー側での認証が正常に完了したことを意味します。他方の認証を完了させるために他方に送信すべき追加データが、SASL 機構内にまだ残っている可能性があります。サーバー側とクライアント側の両方で認証が完了すると、サーバーとクライアントは、互いのプロパティーを照会できるようになります。
次の図は、追加認証データを転送する際の、サーバーとクライアント間における相互作用を示したものです。
セキュリティー層の有無を検査するには、sasl_getprop(3SASL) 関数を使ってセキュリティー強度係数 (SSF) の値が 0 よりも大きいかどうかを確認します。セキュリティー層の折衝が行われていた場合、クライアントとサーバーは認証成功後に結果の SSF を使用する必要があります。クライアントとサーバー間のデータ交換は、認証の場合と似た方法で行われます。プロトコルによってクライアントまたはサーバーにデータが送信される前に、データに sasl_encode() が適用されます。受信側では、最後に、sasl_decode() によってデータが復号化されます。セキュリティー層の折衝がなされていなかった場合、その SASL 接続コンテキストは必要ありません。したがって、このコンテキストは破棄または再利用してかまいません。
SASL 接続コンテキストを解放する必要があるのは、そのセッションを再利用しない場合だけです。sasl_dispose() は、SASL 接続コンテキストと関連するすべてのリソースおよび機構を解放します。sasl_done() を呼び出す場合、その前に SASL 接続コンテキストを破棄しておく必要があります。SASL 接続に対するコンテキストリソースを解放することは、sasl_done() の責任ではありません。「libsasl のクリーンアップ」を参照してください。
SASL セッションが解放される際、すべての状態が解放される可能性がある旨が、関連する機構に通知されます。SASL セッションを解放する必要があるのは、そのセッションを再利用しない場合だけです。それ以外の場合、別のセッションがその SASL 状態を再利用できます。クライアントとサーバーのどちらも、sasl_dispose() を使って SASL 接続コンテキストを解放します。
このステップでは、SASL ライブラリとプラグインのすべてのリソースを解放します。クライアントとサーバーは sasl_done() を呼び出すことで、libsasl() のリソースを解放し、すべての SASL プラグインを読み込み解除します。sasl_done() は SASL 接続コンテキストを解放しません。アプリケーションが SASL クライアントでもあり、かつ SASL サーバーでもある場合、sasl_done() によって SASL クライアントと SASL サーバー双方のリソースが解放される点に注意してください。クライアント、サーバーのいずれかのリソースのみを解放することはできません。
ライブラリ内で sasl_done() を呼び出すべきではありません。アプリケーション内で sasl_done() を呼び出す際には、libsasl を使用している可能性のあるすべてのライブラリとの干渉を回避できるように、細心の注意を払う必要があります。
ここでは、クライアントアプリケーションとサーバーアプリケーション間における一般的な SASL セッションを示します。この例の実行手順は次のとおりです。
クライアントアプリケーションは、libsasl を初期化し、次の大域コールバックを設定します。
SASL_CB_GETREALM
SASL_CB_USER
SASL_CB_AUTHNAME
SASL_CB_PASS
SASL_CB_GETPATH
SASL_CB_LIST_END
サーバーアプリケーションは、libsasl を初期化し、次の大域コールバックを設定します。
SASL_CB_LOG
SASL_CB_LIST_END
クライアントは、SASL 接続コンテキストを作成し、セキュリティープロパティーを設定し、利用可能な機構のリストをサーバーに要求します。
サーバーは、SASL 接続コンテキストを作成し、セキュリティープロパティーを設定し、適切な SASL 機構のリストを取得し、それをクライアントに送信します。
クライアントは、利用可能な機構のリストを取得し、特定の機構を選択し、その選択した機構と認証データをサーバーに送信します。
続いて、クライアントとサーバーは、認証とセキュリティー層の折衝が完了するまで、SASL データを交換します。
認証が完了すると、クライアントとサーバーは、セキュリティー層の折衝が行われたどうかを判断します。クライアントはテストメッセージを符号化します。そのメッセージがサーバーに送信されます。サーバーは、認証ユーザーのユーザー名とそのユーザーのレルムも決定します。
サーバーは、符号化されたメッセージを受信、復号化、および出力します。
クライアントは、sasl_dispose() を呼び出してクライアントの SASL 接続コンテキストを解放します。続いてクライアントは、sasl_done() を呼び出して libsasl のリソースを解放します。
サーバーは、sasl_dispose() を呼び出してそのクライアントの接続コンテキストを解放します。
次に、クライアントとサーバー間のやり取りの様子を示します。libsasl への呼び出しが発生するたびにその呼び出し内容が表示されています。送信側および受信側によるそれぞれのデータ転送も表示されています。なお、データは符号化された形式で表示され、その先頭に転送元が示されています。 転送元がクライアントの場合は C:、サーバーの場合は S: と表示されます。両アプリケーションのソースコードについては、付録 D SASL ソースコード例を参照してください。
% doc-sample-client *** Calling sasl_client_init() to initialize libsasl for client use *** *** Calling sasl_client_new() to create client SASL connection context *** *** Calling sasl_setprop() to set sasl context security properties *** Waiting for mechanism list from server... |
% doc-sample-server digest-md5 *** Calling sasl_server_init() to initialize libsasl for server use *** *** Calling sasl_server_new() to create server SASL connection context *** *** Calling sasl_setprop() to set sasl context security properties *** Forcing use of mechanism digest-md5 Sending list of 1 mechanism(s) S: ZGlnZXN0LW1kNQ== |
S: ZGlnZXN0LW1kNQ== received 10 byte message got 'digest-md5' Choosing best mechanism from: digest-md5 *** Calling sasl_client_start() *** Using mechanism DIGEST-MD5 Sending initial response... C: RElHRVNULU1ENQ== Waiting for server reply... |
C: RElHRVNULU1ENQ== got 'DIGEST-MD5' *** Calling sasl_server_start() *** Sending response... S: bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\ sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\ QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\ XNz Waiting for client reply... |
S: bm9uY2U9IklicGxhRHJZNE4Z1gyVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM\ sbT0iam0xMTQxNDIiLHFvcD0iYXV0aCxhdXRoLWludCxhdXRoLWNvbmYiLGNpcGhlcj0ic\ QwLHJjNC01NixyYzQiLG1heGJ1Zj0yMDQ4LGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1k\ XNz received 171 byte message got 'nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\ realm="jm114142",qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,\ rc4",maxbuf=2048,charset=utf-8,algorithm=md5-sess' *** Calling sasl_client_step() *** Please enter your authorization name : zzzz Please enter your authentication name : zzzz Please enter your password : zz *** Calling sasl_client_step() *** Sending response... C: dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\ yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\ tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\ Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\ ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM= Waiting for server reply... |
C: dXNlcm5hbWU9Inp6enoiLHJlYWxtPSJqbTExNDE0MiIsbm9uY2U9IklicGxhRHJZNE4\ yVm5lQzl5MTZOYWxUOVcvanUrcmp5YmRqaHM9Iixjbm9uY2U9InlqZ2hMVmhjRFJMa0Fob\ tDS0p2WVUxMUM4V1NycjJVWm5IR2Vkclk9IixuYz0wMDAwMDAwMSxxb3A9YXV0aC1jb25m\ Ghlcj0icmM0IixtYXhidWY9MjA0OCxkaWdlc3QtdXJpPSJyY21kLyIscmVzcG9uc2U9OTY\ ODI1MmRmNzY4YTJjYzkxYjJjZDMyYTk0ZWM= got 'username="zzzz",realm="jm114142",\ nonce="IbplaDrY4N4szhgX2VneC9y16NalT9W/ju+rjybdjhs=",\ cnonce="yjghLVhcDRLkAhoirwKCKJvYU11C8WSrr2UZnHGedrY=", \ nc=00000001,qop=auth-conf,cipher="rc4",maxbuf=2048,digest-uri="rcmd/",\ response=966e978252df768a2cc91b2cd32a94ec' *** Calling sasl_server_step() *** Sending response... S: cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ== Waiting for client reply... |
S: cnNwYXV0aD0yYjEzMzRjYzU4NTE4MTEwOWM3OTdhMjUwYjkwMzk3OQ== received 40 byte message got 'rspauth=2b1334cc585181109c797a250b903979' *** Calling sasl_client_step() *** C: Negotiation complete *** Calling sasl_getprop() *** Username: zzzz SSF: 128 Waiting for encoded message... |
Waiting for client reply... C: got '' *** Calling sasl_server_step() *** Negotiation complete *** Calling sasl_getprop() to get username, realm, ssf *** Username: zzzz Realm: 22c38 SSF: 128 *** Calling sasl_encode() *** sending encrypted message 'srv message 1' S: AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA== |
S: AAAAHvArjnAvDFuMBqAAxkqdumzJB6VD1oajiwABAAAAAA== received 34 byte message got '' *** Calling sasl_decode() *** received decoded message 'srv message 1' *** Calling sasl_encode() *** sending encrypted message 'client message 1' C: AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA== *** Calling sasl_dispose() to release client SASL connection context *** *** Calling sasl_done() to release libsasl resources *** |
Waiting for encrypted message... C: AAAAIRdkTEMYOn9X4NXkxPc3OTFvAZUnLbZANqzn6gABAAAAAA== got '' *** Calling sasl_decode() *** received decoded message 'client message 1' *** Calling sasl_dispose() to release client SASL connection context *** |
ここでは、SASL アプリケーションに対して機構やその他のサービスを提供するプラグインの作成方法について説明します。
エクスポート規則のため、Solaris SASL サービスプロバイダインタフェース (SPI) は、Solaris 以外のクライアントおよびサーバー機構プラグイン用のセキュリティーレイヤーをサポートしません。このため、Solaris 以外のクライアントおよびサーバー機構プラグインは、整合性サービスまたは機密性サービスを提供できません。Solaris のクライアントおよびサーバー機構プラグインには、この制限はありません。
SASL サービスプロバイダインタフェース (SPI) を使うと、プラグインと libsasl ライブラリ間の通信が可能となります。SASL プラグインは通常、共有ライブラリとして実装されます。1 つの共有ライブラリには、各種の SASL プラグインを 1 つ以上格納できます。共有ライブラリ内のプラグインは、libsasl によって dlopen(3C) 関数で動的にオープンされます。
また、プラグインは、libsasl を呼び出す特定のアプリケーションに静的にバインドすることも可能です。こうした種類のプラグインを読み込むには、sasl_client_add_plugin() 関数または sasl_server_add_plugin() 関数を使用しますが、どちらを使用するかは、そのアプリケーションがクライアント、サーバーのいずれであるかによります。
Solaris オペレーティングシステム内の SASL プラグインは、次の要件を満たす必要があります。
共有ライブラリ内のプラグインは、有効な実行可能オブジェクトファイル (推奨のファイル拡張子は .so) 内に格納されている必要があります。
プラグインは検証可能な場所に収められている必要があります。プラグインの検証には SASL_CB_VERIFYFILE コールバックが使用されます。
プラグインは適切なエントリポイントを含んでいる必要があります。
SASL クライアントのプラグインのバージョンが、SASL サーバーの対応するプラグインのバージョンに一致している必要があります。
プラグインは正常に初期化できる必要があります。
プラグインのバイナリタイプが、libsasl のバイナリタイプに一致している必要があります。
SASL プラグインは次の 4 つのカテゴリに分類されます。
クライアント機構プラグイン
サーバー機構プラグイン
標準化プラグイン
auxprop プラグイン
sasl_client_init() 関数を呼び出すと、利用可能なすべてのクライアントプラグインが SASL クライアントに読み込まれます。 sasl_server_init() 関数を呼び出すと、サーバープラグイン、標準化プラグイン、および auxprop プラグインが SASL サーバーに読み込まれます。sasl_done() を呼び出すと、すべてのプラグインが読み込み解除されます。
libsasl はプラグイン検索時に、SASL_CB_GETPATH コールバック関数、デフォルトパスのいずれかを使用します。SASL_CB_GETPATH は、プラグイン検索ディレクトリのコロン区切りリストを返します。SASL コンシューマが SASL_CB_GETPATH コールバックを指定した場合、libsasl はそのコールバックから返されたパスを使って検索を行います。それ以外の場合、SASL コンシューマはバイナリタイプに対応するデフォルトパスを使用できます。
32 ビット SPARC アーキテクチャー: /usr/lib/sasl
32 ビット x86 アーキテクチャー: /usr/lib/sasl
64 ビット SPARC アーキテクチャー: /usr/lib/sasl/sparcv9
x64 アーキテクチャー: /usr/lib/sasl/amd64
libsasl は読み込みプロセスの一部として、サポートされている最新バージョンのプラグインを呼び出します。そのプラグインは、バージョンと自身を記述した構造体を返します。バージョンが一致した場合、libsasl はそのプラグインを読み込みます。現在のバージョン番号 (SASL_UTILS_VERSION) は 4 です。
特定のプラグインの初期化が完了すると、そのプラグインと libsasl との間のそれ以降の通信に必要となる構造体を確立する必要があります。プラグインは、sasl_utils_t 構造体を使って libsasl を呼び出します。libsasl は、次の構造体に含まれるエントリポイントを使ってプラグインと通信します。
sasl_out_params_t
sasl_client_params_t
sasl_server_params_t
sasl_client_plug_t
sasl_server_plug_t
sasl_canonuser_plug_t
sasl_auxprop_plug_t
これらの構造体のソースコードは、SASL ヘッダーファイル内に含まれています。構造体については、次の節で説明します。
libsasl とプラグイン間の通信は、次の構造体を使って実現されています。
sasl_utils_t – sasl_utils_t 構造体には、さまざまなユーティリティー関数と 3 つのコンテキスト情報が含まれています。
この構造体に含まれている各種ユーティリティー関数は、プラグイン開発者向けの簡易関数として機能します。それらの関数の多くは、libsasl の公開インタフェースへのポインタになっています。プラグインは、何らかの理由で SASL コンシューマになる必要がある場合を除き、libsasl を直接呼び出す必要はありません。
libsasl は sasl_utils_t 用に 3 つのコンテキストを作成します。
sasl_conn_t *conn
sasl_rand_t *rpool
void *getopt_context
sasl_utils_t 内の conn 変数は、たとえばプラグインの読み込み時のように、実際には特定の接続に関連付けられていない場合があります。それ以外の場合、conn は SASL コンシューマの SASL 接続コンテキストです。rpool 変数は乱数生成関数用です。getopt_context は、getopt() 関数で使用すべきコンテキストです。
sasl_getopt_t(3SASL)、sasl_log_t(3SASL)、および sasl_getcallback_t(3SASL)
sasl_out_params_t – libsasl は、sasl_out_params_t 構造体を作成し、それをクライアントまたはサーバーの mech_step() に渡します。この構造体を通じて libsasl に伝えられる情報は、 認証の状態、authid、authzid、maxbuf、折衝済みの ssf、およびデータの符号化/復号化に関する情報です。
sasl_client_params_t – sasl_client_params_t 構造体は、libsasl がクライアントの状態を特定の SASL クライアント機構に渡すために使用されます。この状態データの送信には、クライアント機構のエントリポイント mech_new()、mech_step()、および mech_idle() が使用されます。また、canon_user_client() エントリポイントにもクライアント状態を渡す必要があります。
sasl_server_params_t – sasl_server_params_t 構造体は、サーバー側において、sasl_client_params_t に似た機能を果たします。
クライアントプラグインは、クライアント側の SASL 折衝を管理するために使用されます。クライアントプラグインは通常、対応するサーバープラグインとともにパッケージ化されます。1 つのクライアントプラグインには、1 つ以上のクライアント側の SASL 機構が含まれています。各 SASL クライアント機構は、認証のサポートに加え、任意で整合性と機密性をサポートします。各機構が自身の機能について提供する情報は、次のとおりです。
SSF の最大値
セキュリティーフラグの最大値
プラグイン機能
プラグインを使用するためのコールバックとプロンプト ID
クライアントプラグインは sasl_client_plug_init() をエクスポートする必要があります。libsasl は sasl_client_plug_init() を呼び出すことで、プラグインをクライアント用に初期化します。プラグインは sasl_client_plug_t 構造体を返します。sasl_client_plug_t が提供する次のエントリポイントは、libsasl が機構を呼び出す際に使用されます。
mech_new() – クライアントは接続開始時に sasl_client_start() を呼び出しますが、この関数が mech_new() を使用します。mech_new() は機構に固有の初期化を実行します。必要に応じて、接続コンテキストが割り当てられます。
mech_step() – mech_step() は、sasl_client_start() と sasl_client_step() から呼び出されます。mech_new() が呼び出されたあと、mech_step() がクライアント側で認証を実行します。認証に成功した場合、mech_step() から SASL_OK が返されます。追加のデータが必要な場合、SASL_CONTINUE が返されます。認証に失敗した場合、SASL エラーコードが返されます。エラーが発生した場合、seterror() が呼び出されます。認証に成功した場合、mech_step() は、関連するセキュリティー層の情報とコールバックを含む sasl_out_params_t 構造体を返す必要があります。canon_user() 関数はこの構造体の一部です。canon_user() は、クライアントが認証 ID と承認 ID を受信した際に呼び出される必要があります。
mech_dispose() – mech_dispose() は、コンテキストが安全にクローズできる場合に呼び出されます。mech_dispose() は sasl_dispose() によって呼び出されます。
mech_free() – mech_free() は libsasl の終了時に呼び出されます。mech_free() によって、そのプラグインに対するすべての大域状態が解放されます。
サーバープラグインは、サーバー側の SASL 折衝を管理するために使用されます。サーバープラグインは通常、対応するクライアントプラグインとともにパッケージ化されます。1 つのサーバープラグインには、1 つ以上のサーバー側の SASL 機構が含まれています。各 SASL サーバー機構は、認証のサポートに加え、オプションで整合性と機密性をサポートします。各機構が自身の機能について提供する情報は、次のとおりです。
SSF の最大値
セキュリティーフラグの最大値
プラグイン機能
プラグインを使用するためのコールバックとプロンプト ID
サーバープラグインは sasl_server_plug_init() をエクスポートする必要があります。libsasl は sasl_server_plug_init() を呼び出すことで、プラグインをサーバー用に初期化します。プラグインは sasl_server_plug_t 構造体を返します。sasl_server_plug_t が提供する次のエントリポイントは、libsasl が機構を呼び出す際に使用されます。
mech_new() – サーバーは接続開始時に sasl_server_start() を呼び出しますが、この関数が mech_new() を使用します。mech_new() は機構に固有の初期化を実行します。mech_new() は必要に応じて接続コンテキストを割り当てます。
mech_step() – mech_step() は、sasl_server_start() と sasl_server_step() から呼び出されます。mech_new() が呼び出されたあと、mech_step() がサーバー側で認証を実行します。認証に成功した場合、mech_step() から SASL_OK が返されます。追加のデータが必要な場合、SASL_CONTINUE が返されます。認証に失敗した場合、SASL エラーコードが返されます。エラーが発生した場合、seterror() が呼び出されます。認証に成功した場合、mech_step() は、関連するセキュリティー層の情報とコールバックを含む sasl_out_params_t 構造体を返す必要があります。canon_user() 関数はこの構造体の一部です。canon_user() は、サーバーが認証 ID と承認 ID を受信した際に呼び出される必要があります。canon_user() 関数が呼び出されると、propctx が設定されます。認証の標準化前に、必要とされるすべての補助プロパティー要求が実行される必要があります。認証の標準化後に、承認 ID 検索が実行されます。
mech_step() 関数は SASL_OK を返す前に、sasl_out_params_t 内のすべての関連フィールドを設定する必要があります。これらのフィールドは次の機能を担っています。
doneflag – 交換が完了したことを示します
maxoutbuf – セキュリティー層の最大出力サイズを示します
mech_ssf – セキュリティー層に対して提供された SSF
encode() – sasl_encode()、sasl_encodev()、および sasl_decode() によって呼び出されます
decode() – sasl_encode()、sasl_encodev()、および sasl_decode() によって呼び出されます
encode_context() – sasl_encode()、sasl_encodev()、および sasl_decode() によって呼び出されます
decode_context() – sasl_encode()、sasl_encodev()、および sasl_decode() によって呼び出されます
mech_dispose() – mech_dispose() は、コンテキストが安全にクローズできる場合に呼び出されます。mech_dispose() は sasl_dispose() によって呼び出されます。
mech_free() – mech_free() は libsasl の終了時に呼び出されます。mech_free() によって、そのプラグインに対するすべての大域状態が解放されます。
setpass() はユーザーのパスワードを設定します。setpass() を使えば、機構は内部パスワードを持つことができます。
mech_avail() は sasl_listmech() によって呼び出され、指定されたユーザーが特定の機構を利用できるかどうかを検査します。mech_avail() は新しいコンテキストを作成できるため、mech_new() への呼び出しを回避できます。パフォーマンスに影響が出なければ、この方法でコンテキストを作成します。
標準化プラグインは、クライアント側とサーバー側の両方で、認証名と承認名に対する代替標準化サポートを提供します。標準化プラグインを読み込む際には、sasl_canonuser_plug_init() が使用されます。標準化プラグインは次の要件を満たす必要があります。
標準化名が出力バッファーにコピーされる必要があります。
同じ入力バッファーが出力バッファーとして使用できる必要があります。
標準化プラグインは、認証 ID、承認 ID のどちらか一方しか存在しない場合でも、正常に機能する必要があります。
ユーザー標準化プラグインは sasl_canonuser_init() 関数をエクスポートする必要があります。sasl_canonuser_init() 関数は、sasl_canonuser_plug_t を返すことで、必要なエントリポイントを確立する必要があります。ユーザー標準化プラグインは少なくとも、sasl_canonuser_plug_t 構造体のメンバー canon_user_client() または canon_user_server() のどちらか 1 つを実装している必要があります。
auxprop プラグインは、authid と authzid の両方に対する補助プロパティーを検索するためのサポートを、SASL サーバー側で提供します。たとえば、アプリケーションによっては、内部認証用としてユーザーパスワードを検索したい場合があります。sasl_auxprop_plug_init() 関数は auxprop プラグインを初期化する際に使用され、sasl_auxpropr_plug_t 構造体を返します。
auxprop プラグインを正しく実装するには、sasl_auxprop_plug_t 構造体の auxprop_lookup メンバーを実装する必要があります。auxprop_lookup() 関数はユーザー名の標準化後に、標準化されたユーザー名を指定して呼び出されます。続いてプラグインは、要求された補助プロパティーの取得に必要な任意の検索を実行できます。
Sun Microsystems, Inc. は現在、auxprop プラグインを提供していません。
ここでは、SASL プラグイン開発に関して、いくつかの追加の指針を提供します。
適切なエラーレポートは、認証エラーの原因究明時やその他のデバッグ時に役立ちます。プラグイン開発者には、sasl_utils_t 構造体の sasl_seterror() コールバック経由で特定の接続に関する詳細なエラー情報を提供することを強くお勧めします。
SASL におけるメモリー割り当ての一般規則は、「開発者自身が割り当てたメモリーは、それらが不要になった時点で漏れなく解放する」というものです。この規則に従えば、パフォーマンスと移植性が改善されるほか、メモリーリークも防止できます。
プラグイン機構は、クライアントとサーバー間で交わされる SASL 会話の順序を、次のフラグに基づいて設定することができます。
SASL_FEAT_WANT_CLIENT_FIRST – クライアント側が交換を開始します。
SASL_FEAT_WANT_SERVER_LAST – サーバーが最後のデータをクライアントに送信します。
どちらのフラグも設定されていない場合、機構プラグインは内部的に順序を設定します。その場合、機構は、送信すべきデータの有無を、クライアントとサーバーの両方で検査する必要があります。なお、「クライアントが最初に送信する」を選択できるのは、プロトコルが初期応答を許可する場合だけです。
「サーバーが最後に送信する」を選択した場合、ステップ関数が SASL_OK を返す際にプラグインが *serverout を設定する必要があります。「サーバーが最後に送信する」を決して使用しない機構は、*serverout に NULL を設定する必要があります。「サーバーが最後に送信する」を常に使用する機構は、*serverout に成功データを設定する必要があります。