Oracle Solaris セキュリティーサービス開発ガイド

第 7 章 SASL を使用するアプリケーションの記述

SASL (Simple Authentication and Security Layer、簡易認証セキュリティー層) は、セキュリティーフレームワークの 1 つです。SASL (「ササル」と発音) は、接続ベースのプロトコルに対して認証サービスを提供するほか、オプションで整合性サービスと機密性サービスも提供します。この章で扱う内容は、次のとおりです。

簡易認証セキュリティー層 (SASL) の紹介

SASL は、アプリケーションと共有ライブラリの開発者に対し、認証、データ整合性検査、および暗号化を行うための機構を提供します。SASL を使用すると、開発者は汎用的な API に基づいたコーディングを行えます。これにより、特定の機構への依存を回避できます。SASL は特に、プロトコル IMAP、SMTP、ACAP、および LDAP を使用するアプリケーションに適しています。というのも、これらのプロトコルはすべて SASL をサポートしているからです。SASLは RFC 2222 に記述されています。

SASL ライブラリの基本

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 セッションが継続する限りその層を使用する必要があります。

SASL アーキテクチャー

次の図に、SASL の基本アーキテクチャーを示します。

図 7–1 SASL アーキテクチャー

クライアントとサーバーの関係において SASL の主要構成要素が協調動作する様子を示しています。

クライアントアプリケーションとサーバーアプリケーションはそれぞれ、libsasl のローカルコピーに対して SASL API 経由で呼び出しを行います。libsasl は、SASL サービスプロバイダインタフェース (SPI) 経由で SASL 機構と通信します。

セキュリティー機構

セキュリティー機構プラグインは、libsasl にセキュリティーサービスを提供します。セキュリティー機構が提供する一般的な機能のいくつかを、次に示します。

SASL セキュリティー強度係数 (SSF)

「SSF」(セキュリティー強度係数) は、SASL 保護の強さを示します。機構がセキュリティー層をサポートしている場合、クライアントとサーバーは SSF の折衝を行います。SSF の値は、SASL 折衝開始前に指定されたセキュリティープロパティーに基づいて決められます。折衝で 0 以外の SSF が決定された場合、認証完了時にクライアントとサーバーの両方でその機構のセキュリティー層を使用する必要があります。SSF は次のような整数値で表現されます。

機密性と整合性の処理はセキュリティー機構によって実行されます。libsasl はそれらの要求を調整する役割を果たします。


注 –

SASL クライアントは折衝時に、最大の SSF を持つ機構を選択します。ただし、実際に選択された SASL 機構はその後、それよりも低い SSF の折衝を行う可能性があります。


SASL における通信

アプリケーションは libsasl API 経由で libsasl と通信します。libsasl はアプリケーションによって登録されたコールバックを介して追加情報の要求を行えます。アプリケーションがプラグインを直接呼び出すことはなく、必ず libsasl 経由でプラグインを呼び出します。プラグインは通常、libsasl フレームワークのプラグインを呼び出します。すると、フレームワークがアプリケーションのコールバックを呼び出します。SASL プラグインはアプリケーションを直接呼び出すことも可能です。ただしその際、アプリケーションは、プラグインからの呼び出しと libsasl からの呼び出しを区別できません。

コールバックは、次のようにさまざまな分野で役に立ちます。

アプリケーションが登録するコールバックには、 大域とセッションの 2 種類があります。さらに、libsasl では多数のコールバック ID が定義されており、それらの ID を使ってさまざまな種類のコールバックを登録できるようになっています。特定の種類のコールバックが登録されていない場合、libsasl はデフォルトの動作を実行します。

セッションコールバックは大域コールバックよりも優先されます。ある ID に対してセッションコールバックが指定された場合、そのセッションでは大域コールバックは呼び出されません。コールバックによっては、大域でなければなりません。これは、それらのコールバックがセッションの外側で呼び出されるためです。次のような処理は大域コールバックにする必要があります。

特定の SASL コールバック ID の SASL コールバックとして、NULL コールバック関数を登録できます。NULL コールバック関数は、必要なデータを提供する準備がクライアント側に整っていることを示します。SASL コールバック ID には必ず、接頭辞 SASL_CB_ が付いています。

SASL が提供する次のコールバックは、クライアントまたはサーバーで使用できます。

SASL_CB_GETOPT

SASL オプションを取得します。オプションを設定すると、libsasl(3LIB) とその関連プラグインの動作が変更されます。クライアントまたはサーバーで使用できます。

SASL_CB_LOG

libsasl とそのプラグインに対するロギング機能を設定します。デフォルトの動作は「syslog の使用」です。

SASL_CB_GETPATH

SASL プラグイン検索パスのコロン区切りリストを取得します。デフォルトのパスは次のとおりです。

  • 32 ビット SPARC アーキテクチャー: /usr/lib/sasl

  • 32 ビット x86 アーキテクチャー: /usr/lib/sasl

  • 64 ビット SPARC アーキテクチャー: /usr/lib/sasl/sparcv9

  • x64 アーキテクチャー: /usr/lib/sasl/amd64

SASL_CB_GETCONF

SASL サーバーの構成ディレクトリへのパスを取得します。デフォルトは /etc/sasl です。

SASL_CB_LANGUAGE

優先順に並んだ RFC 1766 言語コードのコンマ区切りリストを指定します。このリストは、クライアントおよびサーバーのエラーメッセージとクライアントのプロンプトで使用されます。デフォルトは i-default です。

SASL_CB_VERIFYFILE

構成ファイルとプラグインファイルを検証します。

SASL が提供する次のコールバックは、クライアント専用です。

SASL_CB_USER

クライアントのユーザー名を取得します。このユーザー名は承認 ID と同一です。LOGNAME 環境変数がデフォルトになります。

SASL_CB_AUTHNAME

クライアントの認証名を取得します。

SASL_CB_PASS

クライアントのパスフレーズベースのパスワードを取得します。

SASL_CB_ECHOPROMPT

指定されたチャレンジプロンプトの結果を取得します。クライアントからの入力をエコーできます。

SASL_CB_NOECHOPROMPT

指定されたチャレンジプロンプトの結果を取得します。クライアントからの入力をエコーするべきではありません。

SASL_CB_GETREALM

認証に使用するレルムを設定します。

SASL が提供する次のコールバックは、サーバー専用です。

SASL_CB_PROXY_POLICY

認証されたユーザーが指定されたユーザーの代理役として承認されているかどうかを検査します。このコールバックが登録されていない場合、認証されたユーザーと承認されるべきユーザーが同一でなければなりません。これらの ID が同一でない場合、認証が失敗します。標準でない承認ポリシーを処理するには、サーバーアプリケーションを使用してください。

SASL_CB_SERVER_USERDB_CHECKPASS

呼び出し元から提供されたユーザーデータベースに対して平文パスワードを検証します。

SASL_CB_SERVER_USERDB_SETPASS

ユーザーデータベース内に平文パスワードを格納します。

SASL_CB_CANON_USER

アプリケーションから提供されたユーザー標準化関数を呼び出します。

最初の SASL ライブラリ初期化時に、サーバーとクライアントは必要な大域コールバックのすべてを宣言します。大域コールバックが利用可能なのは、SASL セッション初期化前と SASL セッション中です。セッション初期化前には、プラグインの読み込み、データのロギング、構成ファイルの読み取りなどの作業がコールバックによって実行されます。SASL セッションの開始時に、追加のコールバックを宣言できます。そうしたコールバックは、必要に応じて大域コールバックを上書きできます。

SASL 接続コンテキスト

libsasl は、SASL 接続の「コンテキスト」内に、SASL クライアントと SASL サーバーの両方に対する各 SASL セッションの状態を格納します。各コンテキストは、同時に 1 つの認証および 1 つのセキュリティーセッションでしか使用できません。格納される状態としては、次のような情報があります。

SASL サイクル内のステップ

次の図に、SASL ライフサイクル内に含まれる各種ステップを示します。クライアントの動作は図の左側に、サーバーの動作は右側に、それぞれ示してあります。その間に描かれた矢印は、外部接続経由でのクライアントとサーバー間の相互作用を示しています。

図 7–2 SASL ライフサイクル

クライアントとサーバーの両方における SASL ライフサイクルの各フェーズを示しています。

次からの節で、このライフサイクル内のステップについて説明します。

libsasl の初期化

クライアントは 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 ソフトウェアの次のディレクトリに格納されます。

ただし、SASL_CB_GETPATH コールバックを使えば、このデフォルトの場所を変更できます。

この時点で、必要な大域コールバックのすべてが設定されています。SASL クライアントと SASL サーバーには、次のコールバックを含めることができます。

さらに SASL サーバーには、SASL_CB_GETCONF コールバックも含めることができます。

SASL セッションの初期化

サーバーとクライアントはプロトコル経由で接続を確立します。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_listmech() を呼び出すことで、セキュリティーポリシーを満たす利用可能な SASL 機構のリストを取得できます。クライアントは通常、利用可能な機構リストをプロトコルに依存した方法でサーバーから取得できます。

SASL セッションの初期化を図示したのが、次の図です。この図と後続の図では、プロトコル経由でデータを転送した後の検査処理は、図を単純化する目的で省略しています。

図 7–3 SASL セッションの初期化

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 コンテキストは、解放するか、または再利用する必要があります。

認証プロセスのうち、以上の部分を図示したのが、次の図です。

図 7–4 SASL 認証: クライアントデータの送信

クライアントが認証データをサーバーに送信する際の、クライアントとサーバーが実行するステップを示しています。

サーバー側の sasl_server_start() 呼び出しから SASL_CONTINUE が返された場合、サーバーは必要な認証情報をすべて取得するために、クライアントとの通信を継続します。後続のステップ数は機構ごとに異なります。必要に応じて、クライアントは sasl_client_step() を呼び出すことで、サーバーからの認証データを処理し、応答を生成します。同様に、サーバーは sasl_server_step() を呼び出すことで、クライアントからの認証データを処理し、応答を生成できます。この交換は、認証が完了するか、エラーが発生するまで継続されます。SASL_OK が返された場合、それはクライアント側またはサーバー側での認証が正常に完了したことを意味します。他方の認証を完了させるために他方に送信すべき追加データが、SASL 機構内にまだ残っている可能性があります。サーバー側とクライアント側の両方で認証が完了すると、サーバーとクライアントは、互いのプロパティーを照会できるようになります。

次の図は、追加認証データを転送する際の、サーバーとクライアント間における相互作用を示したものです。

図 7–5 SASL 認証: サーバーデータの処理

サーバーがクライアントにデータを返す際に、クライアントとサーバーが実行するステップを示しています。

SASL の機密性と整合性

セキュリティー層の有無を検査するには、sasl_getprop(3SASL) 関数を使ってセキュリティー強度係数 (SSF) の値が 0 よりも大きいかどうかを確認します。セキュリティー層の折衝が行われていた場合、クライアントとサーバーは認証成功後に結果の SSF を使用する必要があります。クライアントとサーバー間のデータ交換は、認証の場合と似た方法で行われます。プロトコルによってクライアントまたはサーバーにデータが送信される前に、データに sasl_encode() が適用されます。受信側では、最後に、sasl_decode() によってデータが復号化されます。セキュリティー層の折衝がなされていなかった場合、その SASL 接続コンテキストは必要ありません。したがって、このコンテキストは破棄または再利用してかまいません。

SASL セッションの解放

SASL 接続コンテキストを解放する必要があるのは、そのセッションを再利用しない場合だけです。sasl_dispose() は、SASL 接続コンテキストと関連するすべてのリソースおよび機構を解放します。sasl_done() を呼び出す場合、その前に SASL 接続コンテキストを破棄しておく必要があります。SASL 接続に対するコンテキストリソースを解放することは、sasl_done() の責任ではありません。libsasl のクリーンアップ」を参照してください。

SASL セッションが解放される際、すべての状態が解放される可能性がある旨が、関連する機構に通知されます。SASL セッションを解放する必要があるのは、そのセッションを再利用しない場合だけです。それ以外の場合、別のセッションがその SASL 状態を再利用できます。クライアントとサーバーのどちらも、sasl_dispose() を使って SASL 接続コンテキストを解放します。

libsasl のクリーンアップ

このステップでは、SASL ライブラリとプラグインのすべてのリソースを解放します。クライアントとサーバーは sasl_done() を呼び出すことで、libsasl() のリソースを解放し、すべての SASL プラグインを読み込み解除します。sasl_done() は SASL 接続コンテキストを解放しません。アプリケーションが SASL クライアントでもあり、かつ SASL サーバーでもある場合、sasl_done() によって SASL クライアントと SASL サーバー双方のリソースが解放される点に注意してください。クライアント、サーバーのいずれかのリソースのみを解放することはできません。


注意 – 注意 –

ライブラリ内で sasl_done() を呼び出すべきではありません。アプリケーション内で sasl_done() を呼び出す際には、libsasl を使用している可能性のあるすべてのライブラリとの干渉を回避できるように、細心の注意を払う必要があります。


SASL の例

ここでは、クライアントアプリケーションとサーバーアプリケーション間における一般的な SASL セッションを示します。この例の実行手順は次のとおりです。

  1. クライアントアプリケーションは、libsasl を初期化し、次の大域コールバックを設定します。

    • SASL_CB_GETREALM

    • SASL_CB_USER

    • SASL_CB_AUTHNAME

    • SASL_CB_PASS

    • SASL_CB_GETPATH

    • SASL_CB_LIST_END

  2. サーバーアプリケーションは、libsasl を初期化し、次の大域コールバックを設定します。

    • SASL_CB_LOG

    • SASL_CB_LIST_END

  3. クライアントは、SASL 接続コンテキストを作成し、セキュリティープロパティーを設定し、利用可能な機構のリストをサーバーに要求します。

  4. サーバーは、SASL 接続コンテキストを作成し、セキュリティープロパティーを設定し、適切な SASL 機構のリストを取得し、それをクライアントに送信します。

  5. クライアントは、利用可能な機構のリストを取得し、特定の機構を選択し、その選択した機構と認証データをサーバーに送信します。

  6. 続いて、クライアントとサーバーは、認証とセキュリティー層の折衝が完了するまで、SASL データを交換します。

  7. 認証が完了すると、クライアントとサーバーは、セキュリティー層の折衝が行われたどうかを判断します。クライアントはテストメッセージを符号化します。そのメッセージがサーバーに送信されます。サーバーは、認証ユーザーのユーザー名とそのユーザーのレルムも決定します。

  8. サーバーは、符号化されたメッセージを受信、復号化、および出力します。

  9. クライアントは、sasl_dispose() を呼び出してクライアントの SASL 接続コンテキストを解放します。続いてクライアントは、sasl_done() を呼び出して libsasl のリソースを解放します。

  10. サーバーは、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

ここでは、SASL アプリケーションに対して機構やその他のサービスを提供するプラグインの作成方法について説明します。


注 –

エクスポート規則のため、Solaris SASL サービスプロバイダインタフェース (SPI) は、Solaris 以外のクライアントおよびサーバー機構プラグイン用のセキュリティーレイヤーをサポートしません。このため、Solaris 以外のクライアントおよびサーバー機構プラグインは、整合性サービスまたは機密性サービスを提供できません。Solaris のクライアントおよびサーバー機構プラグインには、この制限はありません。


SASL プラグインの概要

SASL サービスプロバイダインタフェース (SPI) を使うと、プラグインと libsasl ライブラリ間の通信が可能となります。SASL プラグインは通常、共有ライブラリとして実装されます。1 つの共有ライブラリには、各種の SASL プラグインを 1 つ以上格納できます。共有ライブラリ内のプラグインは、libsasl によって dlopen(3C) 関数で動的にオープンされます。

また、プラグインは、libsasl を呼び出す特定のアプリケーションに静的にバインドすることも可能です。こうした種類のプラグインを読み込むには、sasl_client_add_plugin() 関数または sasl_server_add_plugin() 関数を使用しますが、どちらを使用するかは、そのアプリケーションがクライアント、サーバーのいずれであるかによります。

Solaris オペレーティングシステム内の SASL プラグインは、次の要件を満たす必要があります。

SASL プラグインは次の 4 つのカテゴリに分類されます。

sasl_client_init() 関数を呼び出すと、利用可能なすべてのクライアントプラグインが SASL クライアントに読み込まれます。 sasl_server_init() 関数を呼び出すと、サーバープラグイン、標準化プラグイン、および auxprop プラグインが SASL サーバーに読み込まれます。sasl_done() を呼び出すと、すべてのプラグインが読み込み解除されます。

libsasl はプラグイン検索時に、SASL_CB_GETPATH コールバック関数、デフォルトパスのいずれかを使用します。SASL_CB_GETPATH は、プラグイン検索ディレクトリのコロン区切りリストを返します。SASL コンシューマが SASL_CB_GETPATH コールバックを指定した場合、libsasl はそのコールバックから返されたパスを使って検索を行います。それ以外の場合、SASL コンシューマはバイナリタイプに対応するデフォルトパスを使用できます。

libsasl は読み込みプロセスの一部として、サポートされている最新バージョンのプラグインを呼び出します。そのプラグインは、バージョンと自身を記述した構造体を返します。バージョンが一致した場合、libsasl はそのプラグインを読み込みます。現在のバージョン番号 (SASL_UTILS_VERSION) は 4 です。

特定のプラグインの初期化が完了すると、そのプラグインと libsasl との間のそれ以降の通信に必要となる構造体を確立する必要があります。プラグインは、sasl_utils_t 構造体を使って libsasl を呼び出します。libsasl は、次の構造体に含まれるエントリポイントを使ってプラグインと通信します。

これらの構造体のソースコードは、SASL ヘッダーファイル内に含まれています。構造体については、次の節で説明します。

SASL プラグインに関する重要な構造体

libsasl とプラグイン間の通信は、次の構造体を使って実現されています。

クライアントプラグイン

クライアントプラグインは、クライアント側の SASL 折衝を管理するために使用されます。クライアントプラグインは通常、対応するサーバープラグインとともにパッケージ化されます。1 つのクライアントプラグインには、1 つ以上のクライアント側の SASL 機構が含まれています。各 SASL クライアント機構は、認証のサポートに加え、任意で整合性と機密性をサポートします。各機構が自身の機能について提供する情報は、次のとおりです。

クライアントプラグインは sasl_client_plug_init() をエクスポートする必要があります。libsaslsasl_client_plug_init() を呼び出すことで、プラグインをクライアント用に初期化します。プラグインは sasl_client_plug_t 構造体を返します。sasl_client_plug_t が提供する次のエントリポイントは、libsasl が機構を呼び出す際に使用されます。

サーバープラグイン

サーバープラグインは、サーバー側の SASL 折衝を管理するために使用されます。サーバープラグインは通常、対応するクライアントプラグインとともにパッケージ化されます。1 つのサーバープラグインには、1 つ以上のサーバー側の SASL 機構が含まれています。各 SASL サーバー機構は、認証のサポートに加え、オプションで整合性と機密性をサポートします。各機構が自身の機能について提供する情報は、次のとおりです。

サーバープラグインは sasl_server_plug_init() をエクスポートする必要があります。libsaslsasl_server_plug_init() を呼び出すことで、プラグインをサーバー用に初期化します。プラグインは sasl_server_plug_t 構造体を返します。sasl_server_plug_t が提供する次のエントリポイントは、libsasl が機構を呼び出す際に使用されます。

ユーザー標準化プラグイン

標準化プラグインは、クライアント側とサーバー側の両方で、認証名と承認名に対する代替標準化サポートを提供します。標準化プラグインを読み込む際には、sasl_canonuser_plug_init() が使用されます。標準化プラグインは次の要件を満たす必要があります。

ユーザー標準化プラグインは sasl_canonuser_init() 関数をエクスポートする必要があります。sasl_canonuser_init() 関数は、sasl_canonuser_plug_t を返すことで、必要なエントリポイントを確立する必要があります。ユーザー標準化プラグインは少なくとも、sasl_canonuser_plug_t 構造体のメンバー canon_user_client() または canon_user_server() のどちらか 1 つを実装している必要があります。

補助プロパティー (auxprop) プラグイン

auxprop プラグインは、authidauthzid の両方に対する補助プロパティーを検索するためのサポートを、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 プラグイン開発に関して、いくつかの追加の指針を提供します。

SASL プラグインにおけるエラーレポート

適切なエラーレポートは、認証エラーの原因究明時やその他のデバッグ時に役立ちます。プラグイン開発者には、sasl_utils_t 構造体の sasl_seterror() コールバック経由で特定の接続に関する詳細なエラー情報を提供することを強くお勧めします。

SASL プラグインにおけるメモリー割り当て

SASL におけるメモリー割り当ての一般規則は、「開発者自身が割り当てたメモリーは、それらが不要になった時点で漏れなく解放する」というものです。この規則に従えば、パフォーマンスと移植性が改善されるほか、メモリーリークも防止できます。

SASL 折衝順序の設定

プラグイン機構は、クライアントとサーバー間で交わされる SASL 会話の順序を、次のフラグに基づいて設定することができます。

どちらのフラグも設定されていない場合、機構プラグインは内部的に順序を設定します。その場合、機構は、送信すべきデータの有無を、クライアントとサーバーの両方で検査する必要があります。なお、「クライアントが最初に送信する」を選択できるのは、プロトコルが初期応答を許可する場合だけです。

「サーバーが最後に送信する」を選択した場合、ステップ関数が SASL_OK を返す際にプラグインが *serverout を設定する必要があります。「サーバーが最後に送信する」を決して使用しない機構は、*serverout に NULL を設定する必要があります。「サーバーが最後に送信する」を常に使用する機構は、*serverout に成功データを設定する必要があります。