ヘッダーをスキップ
Oracle® Fusion Middleware Oracle Directory Server Enterprise Edition開発者ガイド
11g リリース1 (11.1.1.7.0)
B72440-01
  目次へ移動
目次
索引へ移動
索引

前
 
次
 

6 プラグインを使用した認証処理

この章では、Directory Serverでサポートされている認証メカニズムに追加したり、認証メカニズムをバイパスまたは置き換えるプラグインを作成する方法について説明します。この章では、認証の既存メカニズムの使用例を示します。特定の認証要件が満たされるように、コード例を調整する必要があります。


注意:

これらの例のみでは、セキュアな認証方式は提供されません


この章の内容は、次のとおりです。

6.1 認証の仕組み

この項では、使用可能な認証方式を示します。また、この項では、クライアントを識別するために、Directory Serverによってどのように認証が処理されるかについても説明します。メカニズムを変更するプラグインを作成するときには、この項で説明するDirectory Serverモデルを考慮してください。

6.1.1 標準方式のサポート

Directory Serverでは、RFC 4511 (http://www.ietf.org/rfc/rfc4511.txt)で説明されている2つの認証方式がサポートされます。1つの方式は簡易認証であり、トランスポートにSecure Socket Layer (SSL)を使用することによって、セキュリティを強化します。もう1つの方式はSASLであり、このテクノロジについては、RFC 2222 (http://www.ietf.org/rfc/rfc2222.txt)の『Simple Authentication and Security Layer (SASL)』で詳しく説明しています。SASLを介して、Directory Serverでは、RFC 1777 (http://www.ietf.org/rfc/rfc1777.txt)の『Lightweight Directory Access Protocol』で説明されているように、LDAPサーバーおよびDirectory System Agent (DSA、X.500用語)についてKerberos認証がサポートされます。

6.1.2 バインド時のクライアント識別

LDAPクライアントの場合、Directory Serverでは、クライアントが接続に使用するDNを介してクライアント・アイデンティティを追跡します。また、サーバーでは、認証方式およびクライアントが接続に使用する外部資格証明を介した追跡も実行します。パラメータ・ブロックにより、関連するクライアント接続情報が保持されます。DNには、slapi_pblock_set()およびslapi_pblock_get()SLAPI_CONN_DNおよびSLAPI_CONN_AUTHTYPEパラメータを介してアクセスできます。

HTTPを経由して接続するDSMLクライアントの場合、Directory Serverにより、バインドを行うためのアイデンティティ・マッピングが実行されます。結果として、フロントエンド・プロトコルに関係なく、プラグインでは同じクライアント・バインドとしてみなされます。

6.1.3 Directory Serverでのバインド処理

Directory Serverでは、事前操作バインド・プラグインおよび事前操作バインド関数をコールする前に、匿名バインド、ディレクトリ・マネージャによるバインドおよびレプリケーション・ユーザーによるバインドについての認証を完了します。これにより、プラグインをコールすることなくバインドが完了します。


注意:

SASL認証メカニズムの場合は、1つの認証リクエストを実行する間に、事前操作および事後操作のバインド関数を複数回コールできます。

たとえば、DIGEST-MD5の場合は、複数のLDAPバインド操作を使用して認証メカニズムを実装できます。


6.1.3.1 Directory Serverによるバインドの処理方法

Directory Serverでのバインド処理では、次の処理が実行されます。

  1. バインド・リクエストを解析します。

  2. 認証方式を決定します。

  3. バインドDNをローカルに処理するかどうかを決定します。

  4. リクエスト情報をパラメータ・ブロックに追加します。

  5. フロントエンドでバインドを処理するか、または事前操作バインド・プラグイン関数をコールするかを決定します。

  6. バインドを実行するかどうかに関係なく、サーバーのバックエンドからのバインドDNエントリに関する情報を使用します。

各アクションの説明は次のとおりです。

  • バインド・リクエストを解析します。バインド・リクエストが到着すると、Directory Serverによって、DN、使用する認証方式および送信されたすべての資格情報(パスワードなど)が取得されます。クライアントがHTTP経由のDSMLを使用してサーバーにアクセスする場合、処理にマッピングが含まれる可能性があります。リクエストがSASL認証LDAP_AUTH_SASLを必要とする場合、Directory ServerによってSASLメカニズムが取得されます。次に、Directory Serverによって、リクエストから取得されたDNが正規化されます。また、サーバーによって、リクエストに含まれるすべてのLDAP v3コントロールも取得されます。

  • 認証方式を決定します。場合によっては、方式は簡易認証であるものの、DNまたは資格証明が空であるか、欠落していることがあります。次に、Directory Serverでは、クライアントが匿名でバインドすることを想定して、SLAPI_CONN_DNNULLに、SLAPI_CONN_AUTHTYPESLAPD_AUTH_NONEに設定し、結果LDAP_SUCCESSをクライアントに送信します。方式がSASL認証である場合、Directory Serverでは、まず、メカニズムがサポートされるかどうかを判別します。サポートされない場合、サーバーにより、結果LDAP_AUTH_METHOD_NOT_SUPPORTEDがクライアントに送信されます。

  • DNをローカルに処理するかどうかを決定します。DNがローカル・インスタンスに格納されていないものの、デフォルトでDirectory Serverがクライアントを参照している場合、サーバーによって、結果LDAP_REFERRALがクライアントに送信されます。それ以外の場合、サーバーによって、結果LDAP_NO_SUCH_OBJECTがクライアントに送信されます。

  • パラメータ・ブロックにリクエスト情報を追加します。Directory Serverによって、バインド・リクエスト情報が次のパラメータ・ブロックに含められます。

    • SLAPI_BIND_TARGETにはリクエストから取得されたDN

    • SLAPI_BIND_METHODには要求された認証方式

    • SLAPI_BIND_CREDENTIALSにはリクエストから取得された資格情報

    • SLAPI_BIND_MECHANISMにはSASLメカニズム名(適用可能な場合)

  • バインドを処理するか、またはプラグインをコールするかを決定します。Directory Serverによって、ディレクトリのスーパーユーザーまたはレプリケーション・マネージャからのバインド・リクエストが認証され、成功した場合には結果LDAP_SUCCESSが、失敗した場合には結果LDAP_INVALID_CREDENTIALSが送信されます。

    この作業がすべて完了した後、サーバーによって、事前操作バインド関数がコールされます。

    ディレクトリ・マネージャやレプリケーション・マネージャからのバインド・リクエストまたは匿名バインドに対して、Directory Serverによって事前操作バインド関数がコールされることはありません。

  • バックエンド・バインドを実行します。事前操作バインド関数から0が戻されると、Directory Serverではバインド操作を完了します。サーバーでは、バインドに関するディレクトリ・データベース内の情報を確認します。その後、サーバーでは、SLAPI_CONN_DNおよびSLAPI_CONN_AUTHTYPEを適切に設定します。必要に応じて、Directory Serverでは、パスワード関連の結果のコントロールをクライアントに送信します。サーバーは、通常、成功した場合には結果LDAP_SUCCESSを送信し、失敗した場合にはゼロ以外の結果を送信します。

6.2 プラグインによる認証の変更方法

事前操作バインド関数を使用すると、2つのうちいずれかの方法で、Directory Server認証を変更できます。プラグインでは、受信した認証情報とディレクトリ・データベースに格納された認証情報との比較を完全にバイパスするか、またはカスタムSASLメカニズムを実装します。

6.2.1 認証のバイパス

一部のプラグインでは、クライアント・リクエストの認証情報とディレクトリの認証情報との比較をバイパスします。このようなプラグインは、ゼロ以外の値を戻します。値1が戻されると、サーバーでは、事前操作関数の復帰後にバインドを完了できなくなります。LDAPまたはプラグインAPIを介して認証アイデンティティをマッピングすることなく、すべての認証情報をディレクトリの外部に格納する場合には、この方法を使用します。プラグインのその他の検証に加えて、サーバーのアクセス制御メカニズムでプラグインが正常に動作することを検証する必要があります。

例は、「簡易認証プラグインの開発」を参照してください。

6.2.2 カスタムSASLメカニズムの使用

プラグインにカスタムSASLメカニズムが実装されている場合、このメカニズムを使用するクライアントもこのメカニズムをサポートしている必要があります。

プラグインの例は、「SASL認証プラグインの開発」を参照してください。

6.3 簡易認証プラグインの開発

この項では、事前操作バインド・プラグインでのプラグインAPIを使用したユーザーの認証方法について説明します。この項で使用されている例では、パラメータ・ブロックから適切なバインド情報を取得します。次に、この例では、リクエストがLDAP_AUTH_SIMPLE用である場合は認証が処理されますが、認証が成功すると、サーバーはバインドを続行できます。

一部のバインドは、事前操作バインド関数がコールされる前にフロントエンドで実行されます。

6.3.1 簡易認証の例

次の例は、ソース・ファイルinstall-path/examples/testbind.cから抜粋したコードを示しています。

例6-1 事前操作バインド関数(testbind.c)

#include "slapi-plugin.h"

int
test_bind(Slapi_PBlock * pb)
{
    char          *  dn;               /* Target DN                 */
    int              method;           /* Authentication method     */
    struct berval *  credentials;      /* Client SASL credentials   */
    Slapi_DN      *  sdn      = NULL;  /* DN used in internal srch  */
    char          *  attrs[2] = {      /* Look at userPassword only */
                                SLAPI_USERPWD_ATTR,
                                NULL
                     };
    Slapi_Entry   *  entry    = NULL;  /* Entry returned by srch    */
    Slapi_Attr    *  attr     = NULL;  /* Pwd attr in entry found   */
    int              is_repl;          /* Is this replication?      */
    int              is_intl;          /* Is this an internal op?   */
    int              connId, opId, rc = 0;
    long             msgId;
        
    /* Obtain the bind information from the parameter block.        */
    rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET,             &dn);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD,             &method);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS,        &credentials);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID,         &msgId);
    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,                 &connId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,            &opId);
    rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);
    rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION,   &is_intl);

    if (rc != 0) {
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            SLAPI_LOG_NO_MSGID,
            SLAPI_LOG_NO_CONNID,
            SLAPI_LOG_NO_OPID,
            "test_bind in test-bind plug-in",
            "Could not get parameters for bind operation (error %d).\n", rc
        );
        slapi_send_ldap_result(
            pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
        return (LDAP_OPERATIONS_ERROR);/* Server aborts bind here.  */
    }

    /* The following code handles simple authentication, where a
     * user offers a bind DN and a password for authentication.
     *
     * Handling simple authentication is a matter of finding the
     * entry corresponding to the bind DN sent in the request,
     * then if the entry is found, checking whether the password
     * sent in the request matches a value found on the
     * userPassword attribute of the entry.                         */

    /* Avoid interfering with replication or internal operations.   */
    if (!is_repl && !is_intl) switch (method) {
    case LDAP_AUTH_SIMPLE:

        /* Find the entry specified by the bind DN...               */
        sdn = slapi_sdn_new_dn_byref(dn);
        rc |= slapi_search_internal_get_entry(
            sdn,
            attrs,
            &entry,
            plugin_id
            );
        slapi_sdn_free(&sdn);

        if (rc != 0 || entry == NULL) {
            slapi_log_info_ex(
                SLAPI_LOG_INFO_AREA_PLUGIN,
                SLAPI_LOG_INFO_LEVEL_DEFAULT,
                msgId,
                connId,
                opId,
                "test_bind in test-bind plug-in",
                "Could not find entry: %s\n", dn
            );
            rc = LDAP_NO_SUCH_OBJECT;
            slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
            return (rc);
        } else {
        /* ...check credentials against the userpassword...         */
            Slapi_Value    *  credval; /* Value of credentials      */
            Slapi_ValueSet * pwvalues; /* Password attribute values */
                        
            rc |= slapi_entry_attr_find(
                entry,
                SLAPI_USERPWD_ATTR,
                &attr
            );

            if (attr == NULL) {

                slapi_log_info_ex(
                    SLAPI_LOG_INFO_AREA_PLUGIN,
                    SLAPI_LOG_INFO_LEVEL_DEFAULT,
                    msgId,
                    connId,
                    opId,
                    "test_bind in test-bind plug-in",
                    "Entry %s has no userpassword.\n",
                    dn
                );
                rc = LDAP_INAPPROPRIATE_AUTH;
                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
                return (rc);
            }

            rc |= slapi_attr_get_valueset(
                attr,
                &pwvalues
            );
            if (rc != 0 || slapi_valueset_count(pwvalues) == 0) {
                slapi_log_info_ex(
                    SLAPI_LOG_INFO_AREA_PLUGIN,
                    SLAPI_LOG_INFO_LEVEL_DEFAULT,
                    msgId,
                    connId,
                    opId,
                    "test_bind in test-bind plug-in",
                    "Entry %s has no %s attribute values.\n",
                    dn, SLAPI_USERPWD_ATTR
                );
                rc = LDAP_INAPPROPRIATE_AUTH;
                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
                                slapi_valueset_free(pwvalues);
                                return (rc);
            }

            credval = slapi_value_new_berval(credentials);

            rc = slapi_pw_find_valueset(pwvalues, credval);

            slapi_value_free(&credval);
                        slapi_valueset_free(pwvalues);

            if (rc != 0) {
                slapi_log_info_ex(
                    SLAPI_LOG_INFO_AREA_PLUGIN,
                    SLAPI_LOG_INFO_LEVEL_DEFAULT,
                    msgId,
                    connId,
                    opId,
                    "test_bind in test-bind plug-in",
                    "Credentials are not correct.\n"
                );
                rc = LDAP_INVALID_CREDENTIALS;
                slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
                return (rc);
            }
        }

        /* ...if successful, set authentication for the connection. */
        if (rc != 0) return (rc);
        rc |=slapi_pblock_set(pb, SLAPI_CONN_DN, slapi_ch_strdup(dn));
        rc |=slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE);
        if (rc != 0) {
            slapi_log_info_ex(
                SLAPI_LOG_INFO_AREA_PLUGIN,
                SLAPI_LOG_INFO_LEVEL_DEFAULT,
                msgId,
                connId,
                opId,
                "test_bind in test-bind plug-in",
                "Failed to set connection info.\n"
            );
            rc = LDAP_OPERATIONS_ERROR;
            slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
            return (rc);
        } else {
            slapi_log_info_ex(
                SLAPI_LOG_INFO_AREA_PLUGIN,
                SLAPI_LOG_INFO_LEVEL_DEFAULT,
                msgId,
                connId,
                opId,
                "test_bind in test-bind plug-in",
                "Authenticated: %s\n", dn
            );

            /* Now that authentication succeeded, the plug-in
             * returns a value greater than 0, even though the
             * authentication has been successful. A return
             * code> 0 tells the server not to continue
             * processing the bind. A return code of 0, such
             * as LDAP_SUCCESS tells the server to continue
             * processing the operation.                            */
            slapi_send_ldap_result(
                pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
            rc = 1;
        }
        break;

    /* This plug-in supports only simple authentication.            */
    case LDAP_AUTH_NONE:
        /* Anonymous binds are handled by the front-end before
         * pre-bind plug-in functions are called, so this
         * part of the code should never be reached.                */
    case LDAP_AUTH_SASL:
    default:
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
            SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "test_bind in test-bind plug-in",
            "Plug-in does not handle auth. method: %d\n", method
        );
        rc = 0;                        /* Let server handle bind.   */
        break;
    }

    return (rc);                       /* Server stops processing
                                        * the bind if rc> 0.       */
}

コードの大部分では、LDAP_AUTH_SIMPLEのケースが処理されます。簡易認証の場合、プラグインではDNおよびパスワードを使用し、プラグインAPIコールを介してバインドするユーザーを認証します。

6.3.2 プラグインの動作の表示

プラグインのデモは、プラグインに関する情報のロギングをオンにすることによって機能します。この操作の様々なステージで、プラグインによって書き込まれたログ・メッセージを参照します。事前操作バインド関数をコールしないで、ディレクトリのスーパーユーザーとしてバインドしている間は、機能のデモを行うことができないため、プラグインを使用する前に、いくつかのサンプル・ユーザーおよびサンプル・データをロードします。

6.3.2.1 サンプル・サフィックスを設定するには

実行済でない場合には、サフィックスdc=example,dc=comを使用して、サンプルのLDIFファイルinstall-path/resources/ldif/Example.ldifからロードされたデータを含むディレクトリ・インスタンスを設定します。

  1. 新規Directory Serverインスタンスを作成します。

    次に例を示します。

    $ dsadm create -h localhost -p 1389 /local/ds
    Choose the Directory Manager password:
    Confirm the Directory Manager password:
    $ 
    
  2. 新規Directory Serverインスタンスを開始します。

    次に例を示します。

    $ dsadm start /local/ds
    Server started: pid=4705
    $ 
    
  3. サフィックスdc=example,dc=comを作成します。

    たとえば、長い行は印刷ページに合わせて折り返します。

    $ dsconf create-suffix -h localhost -p 1389 dc=example,dc=com
    Enter "cn=directory manager" password: 
    Certificate "CN=defaultCert, CN=hostname:1636" presented by the
     server is not trusted.
    Type "Y" to accept, "y" to accept just once,
     "n" to refuse, "d" for more details: Y
    $ 
    
  4. サンプルLDIFをロードします。

    たとえば、長い行は印刷ページに合わせて折り返します。

    $ dsconf import -h localhost -p 1389 \
     install-path/resources/ldif/Example.ldif dc=example,dc=com
    Enter "cn=directory manager" password:  
    New data will override existing data of the suffix
     "dc=example,dc=com".
    Initialization will have to be performed on replicated suffixes. 
    Do you want to continue [y/n] ? y
    
    ## Index buffering enabled with bucket size 16
    ## Beginning import job...
    ## Processing file "install-path/resources/ldif/Example.ldif"
    ## Finished scanning file "install-path/resources/ldif/Example.ldif" (160 entries)
    ## Workers finished; cleaning up...
    ## Workers cleaned up.
    ## Cleaning up producer thread...
    ## Indexing complete.
    ## Starting numsubordinates attribute generation.
     This may take a while, please wait for further activity reports.
    ## Numsubordinates attribute generation complete. Flushing caches...
    ## Closing files...
    ## Import complete. Processed 160 entries in 5 seconds.
     (32.00 entries/sec)
    
    Task completed (slapd exit code: 0).
    $ 
    

関連項目

Directory Service Control Centerを使用して、このタスクを実行できます。

6.3.2.2 プラグインを登録するには

実行済でない場合には、サンプルのプラグイン・ライブラリを作成し、プラグイン情報のロギングとサンプル・プラグインの両方をアクティブ化します。

  1. プラグインを構築します。

    ヒント: install-path/examples/Makefileまたはinstall-path/examples/Makefile64を使用します。

  2. プラグイン情報メッセージがログに記録されるようにDirectory Serverを構成し、プラグインをロードします。

     $ dsconf create-plugin -F custom-plugin-init-function -G custom-plugin-argument -H lib-path \
    -Y custom-plugin-type "Custom Plugin"
    $ dsconf enable-plugin "Custom Plugin"
    

    ヒント: 詳細は、プラグインのソース・ファイルに指定されたコマンドを使用して参照してください。

  3. Directory Serverを再起動します。

    $ dsadm restart instance-path
    

6.3.2.3 プラグインをバイパスするには

サンプル・サフィックスには、数多くのユーザーが含まれています。匿名で、またはディレクトリ・マネージャとして、これらのユーザーのうちの1人Barbara Jensenのエントリを検索する場合、test_bind()プラグインはコールされません。このため、プラグインによって、情報メッセージがerrorsログに記録されることはありません。

プラグインをバイパスする検索を実行します。

$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=bjensen sn 
version: 1
dn: uid=bjensen, ou=People, dc=example,dc=com
sn: Jensen
$ grep test_bind /local/ds/logs/errors
$

特別なユーザーがバインドを要求すると、サーバーでは、事前操作バインド・プラグインをバイパスします。

6.3.2.4 Example.comユーザーとしてバインドするには

  1. Barbara Jensenとしてバインドする場合にerrorsログに記録される内容を確認します。

    $ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \
     -D uid=bjensen,ou=people,dc=example,dc=com -w hifalutin uid=bjensen sn
    version: 1
    dn: uid=bjensen, ou=People, dc=example,dc=com
    sn: Jensen
    $ grep test_bind /local/ds/logs/errors
    [04/Jan/2006:11:34:31 +0100] - INFORMATION -
     test_bind in test-bind plug-in
     - conn=4 op=0 msgId=1 - 
     Authenticated: uid=bjensen,ou=people,dc=example,dc=com
    $ 
    
  2. Barbara Jensenとしてバインドした場合の記録内容を確認したものの、パスワードが不正であると表示されます。

    $ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \
     -D uid=bjensen,ou=people,dc=example,dc=com -w bogus uid=bjensen sn
    ldap_simple_bind: Invalid credentials
    $ grep test_bind /local/ds/logs/errors | grep -i credentials
    [04/Jan/2006:11:36:07 +0100] - INFORMATION -
     test_bind in test-bind plug-in
     - conn=5 op=0 msgId=1 -  Credentials are not correct.
    $ 
    

    ここで、LDAP結果がコマンドライン・クライアントによって適切に解釈されます。同じ結果についてのプラグイン・メッセージが、errorsログに書き込まれます。

  3. Barbaraのパスワードを削除して、再試行します。

    $ ldapmodify -h localhost -p 1389 \
     -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery
    dn: uid=bjensen,ou=people,dc=example,dc=com
    changetype: modify
    delete: userpassword
    modifying entry uid=bjensen,ou=people,dc=example,dc=com
    ^D
    $ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \
     -D uid=bjensen,ou=people,dc=example,dc=com -w - uid=bjensen sn
    Enter bind password: 
    ldap_simple_bind: Inappropriate authentication
    $ grep test_bind /local/ds/logs/errors | grep -i password
    [04/Jan/2006:11:41:25 +0100] - INFORMATION -
     test_bind in test-bind plug-in
     - conn=8 op=0 msgId=1 - 
     Entry uid=bjensen,ou=people,dc=example,dc=com has no userpassword.
    $ 
    

    ここで、LDAP結果がコマンドライン・クライアントによって適切に表示されます。プラグイン・メッセージに、Barbaraのバインド時の問題の詳細no userpassword attribute valuesが表示されます。

6.4 SASL認証プラグインの開発

この項では、バインド前操作プラグインによるカスタムSimple Authentication and Security Layer (SASL)メカニズムの実装方法について説明します。このメカニズムを使用すると、既存の認証メカニズムに影響を及ぼすことなく、相互認証を行うことができます。

サンプル・プラグインは、メカニズムmy_sasl_mechanismを使用してSASLバインド・リクエストに対応します。通常、このメカニズムによるバインドは成功します。この例は、SASL認証プラグインがどのように機能するかを示すことのみを目的としており、本番で使用するためのセキュアなメカニズムを示すものではありません。

6.4.1 SASLの例

このSASLの項全体では、例install-path/examples/testsaslbind.cおよびinstall-path/examples/clients/saslclient.cについて説明します。

ここでの説明に従ってプラグイン関数を使用する前に、サンプル・サフィックスを設定します。この設定は、「プラグインの動作の表示」を参照してください。サンプル・ファイルの冒頭にあるコメントの説明に従って、プラグインを登録します。

6.4.2 SASLメカニズムの登録

プラグインを登録する場合と同じ関数slapi_register_supported_saslmechanism()を使用して、SASLメカニズムを登録します。次の例は、プラグインとSASLメカニズムの両方を登録する関数を示しています。

例6-2 カスタムSASLプラグインの登録(testsaslbind.c)

#include "slapi-plugin.h"

Slapi_PluginDesc saslpdesc = {
    "test-saslbind",                   /* plug-in identifier          */
    "Oracle Corporation (test)",       /* vendor name                 */
    "11.1.1.3.0",                      /* plug-in revision number     */
    "Sample SASL pre-bind plug-in"     /* plug-in description         */
};

#define TEST_MECHANISM  "my_sasl_mechanism"
#define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM

/* Register the plug-in with the server.                              */
#ifdef _WIN32
__declspec(dllexport)
#endif
int
testsasl_init(Slapi_PBlock * pb)
{
    int rc = 0;                        /* 0 means success             */
    rc |= slapi_pblock_set(
        pb,
        SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description         */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &saslpdesc
    );
    rc |= slapi_pblock_set(            /* Pre-op bind SASL function   */
        pb,
        SLAPI_PLUGIN_PRE_BIND_FN,
        (void *) testsasl_bind
    );
            
    /* Register the SASL mechanism.                                   */
    slapi_register_supported_saslmechanism(TEST_MECHANISM);
    return (rc);
}

slapi_register_supported_saslmechanism ()の使用の詳細は、「プラグインAPIリファレンス」を参照してください。

プラグイン関数testsasl_bind()は、最初にクライアントDN、メソッド、SASLメカニズムおよび資格証明を取得します。クライアントからTEST_MECHANISMの使用を求められない場合、この関数は0を戻し、これにより、サーバーでバインドを処理できます。次の例は、どのように処理が実行されるかを示しています。

例6-3 my_sasl_mechanismバインドの処理(testsaslbind.c)

#include "slapi-plugin.h"

#define TEST_MECHANISM  "my_sasl_mechanism"
#define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM

int
testsasl_bind(Slapi_PBlock * pb)
{
    char          * dn;                /* Target DN                   */
    int             method;            /* Authentication method       */
    char          * mechanism;         /* SASL mechanism              */
    struct berval * credentials;       /* SASL client credentials     */
    struct berval   svrcreds;          /* SASL server credentials     */
    int             connId, opId, rc = 0;
    long            msgId;
        
    rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET,        &dn);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD,        &method);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS,   &credentials);
    rc |= slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mechanism);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID,    &msgId);
    rc |= slapi_pblock_get(pb, SLAPI_CONN_ID,            &connId);
    rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID,       &opId);
    if (rc == 0) {
        if (mechanism == NULL || strcmp(mechanism, TEST_MECHANISM) != 0)
            return(rc);                /* Client binding another way. */
    } else {                           /* slapi_pblock_get() failed!  */
        return(rc);
    }

    /* Using the SASL mechanism that always succeeds, set conn. info. */
    rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         slapi_ch_strdup(dn));
    rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, TEST_AUTHMETHOD);
    if (rc != 0) {                     /* Failed to set conn. info!   */
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testsasl_bind in test-saslbind plug-in",
            "slapi_pblock_set() for connection information failed.\n"
        );
        slapi_send_ldap_result(
            pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */
    }

    /* Set server credentials.                                        */
    svrcreds.bv_val = "my credentials";
    svrcreds.bv_len = sizeof("my credentials") - 1;

    rc |= slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &svrcreds);
    if (rc != 0) {                     /* Failed to set credentials!  */
        slapi_log_info_ex(
            SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
            msgId,
            connId,
            opId,
            "testsasl_bind in test-saslbind plug-in",
            "slapi_pblock_set() for server credentials failed.\n"
        );
        rc |= slapi_pblock_set(pb, SLAPI_CONN_DN,         NULL);
        rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE);
        return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs.   */
    }
    
    /* Send credentials to client.                                    */
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        msgId,
        connId,
        opId,
        "testsasl_bind in test-saslbind plug-in",
        "Authenticated: %s\n", dn
    );
    slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
    return 1;                          /* Server stops processing the
                                        * bind if the plug-in returns
                                        * a value greater than 0.     */
}

前述の例では、Directory Serverでバインド用に設定されるように、クライアント接続用のDNおよび認証方式を設定しています。SLAPI_CONN_DNおよびSLAPI_CONN_AUTHMETHODの設定は、バインドの主要な手順です。パラメータ・ブロックのこれらの値を使用しない場合、Directory Serverでは、バインドが匿名であるときのようにクライアント・リクエストを処理します。これにより、クライアントには、匿名バインドに対応するアクセス権が残されます。その後、SASLモデルに従って、次に示すように、Directory Serverからクライアントに資格証明が送信されます。

6.4.3 SASLクライアントの開発

プラグインをテストするには、my_sasl_mechanismを使用してバインドを行うクライアントが必要です。ここで説明する例では、このメカニズムおよびDirectory Serverに付属のクライアント・コードsaslclient.cを使用します。

クライアントでは、Directory Serverへの同期バインドのためのサンプルSASLメカニズムを使用することによって認証します。次の例では、Ted Morrisの認証がクライアント側でどのように行われるかを示しています。

例6-4 my_sasl_mechanismを使用したクライアント(clients/saslclient.c)

#include "ldap.h"

/* Use the fake SASL mechanism.                                      */
#define MECH "my_sasl_mechanism"

/* Global variables for client connection information.
 * You may set these here or on the command line.                    */
static char * host = "localhost";      /* Server hostname            */
static int    port = 389;              /* Server port                */
/* Load <install-path>/ldif/Example.ldif
 * before trying the plug-in with this default user.                 */
static char * user = "uid=tmorris,ou=people,dc=example,dc=com";
/* New value for userPassword                                        */
static char * npwd = "23skidoo";

/* Check for host, port, user and new password as arguments.         */
int get_user_args(int argc, char ** argv);

int
main(int argc, char ** argv)
{
    LDAP          * ld;                /* Handle to LDAP connection  */
    LDAPMod         modPW, * mods[2];  /* For modifying the password */
    char          * vals[2];           /* Value of modified password */
    struct berval   cred;              /* Client bind credentials    */
    struct berval * srvCred;           /* Server bind credentials    */
    int             ldapVersion;

    /* Use default hostname, server port, user, and new password
     * unless they are provided as arguments on the command line.    */
    if (get_user_args(argc, argv) != 0) return 1; /* Usage error     */
    
    /* Get a handle to an LDAP connection.                           */
    printf("Getting the handle to the LDAP connection...\n");
    if ((ld = ldap_init(host, port)) == NULL) {
        perror("ldap_init");
        return 1;
    }

    /* By default, the LDAP version is set to 2.                     */
    printf("Setting the version to LDAP v3...\n");
    ldapVersion = LDAP_VERSION3;
    ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);

    /* Authenticate using the example SASL mechanism.                */
    printf("Bind DN is %s...\n", user);
    printf("Binding to the server using %s...\n", MECH);
    cred.bv_val = "magic";
    cred.bv_len = sizeof("magic") - 1;
    if (ldap_sasl_bind_s(ld, user, MECH, &cred, NULL, NULL, &srvCred)) {
        ldap_perror(ld, "ldap_sasl_bind_s");
        return 1;
    }

    /* Display the credentials returned by the server.               */
    printf("Server credentials: %s\n", srvCred->bv_val);

    /* Modify the user's password.                                   */
    printf("Modifying the password...\n");
    modPW.mod_op     = LDAP_MOD_REPLACE;
    modPW.mod_type   = "userpassword";
    vals[0]          = npwd;
    vals[1]          = NULL;
    modPW.mod_values = vals;

    mods[0] = &modPW; mods[1] = NULL;

    if (ldap_modify_ext_s(ld, user, mods, NULL, NULL)) {
        ldap_perror(ld, "ldap_modify_ext_s");
        return 1;
    }

    /* Finish up.                                                    */
    ldap_unbind(ld);
    printf("Modification was successful.\n");
    return 0;
}

6.4.4 SASLクライアントの試行

クライアントにより、Directory ServerへのバインドによってTedのパスワードが変更された後、userPassword属性値の変更が要求されます。

サーバーでプラグインをアクティブ化した後、クライアント・コードsaslclient.cをコンパイルします。次に、クライアントを実行して、バインドおよびパスワード変更を行います。

$ ./saslclient 
Using the following connection info:
    host:    localhost
    port:    389
    bind DN: uid=tmorris,ou=people,dc=example,dc=com
    new pwd: 23skidoo
Getting the handle to the LDAP connection...
Setting the version to LDAP v3...
Bind DN is uid=tmorris,ou=people,dc=example,dc=com...
Binding to the server using my_sasl_mechanism...
Server credentials: my credentials
Modifying the password...
Modification was successful.
$ 

Directory Server側では、クライアントがプラグインによって認証されたことを示すメッセージは、errorsログに含められます。

$ grep tmorris /local/ds/logs/errors | grep -i sasl 
[04/Jan/2006:12:05:30 +0100] - INFORMATION
 - testsasl_bind in test-saslbind plug-in
 - conn=12 op=0 msgId=1 - 
 Authenticated: uid=tmorris,ou=people,dc=example,dc=com