45 カスタマイズされたパスワード・ポリシー・プラグインの構成

Oracle Internet Directoryは、プラグインを使用して、パスワード値のチェックを他のパスワード・ポリシー管理機能に追加します。このプラグインを使用すると、追加または変更されたパスワードが、指定された最小文字数以上であるかどうかなどを確認できます。個別の要件に合せて、パスワード値チェックをカスタマイズできます。

次の各トピックでは、カスタマイズされたパスワード・ポリシー・プラグインをインストール、構成および有効化する方法について説明します。

45.1 カスタマイズされたパスワード・ポリシー・プラグインの構成

パスワードを追加または変更する場合、カスタマイズされたパスワード値チェックが次のように処理されます。

  1. クライアントが、ldapaddリクエストまたはldapmodifyリクエストをディレクトリ・サーバーに送信します。

  2. ディレクトリ・サーバーは、追加または変更を行う前にパスワード値をプラグインに渡します。

  3. プラグインは次のように動作します

    1. エントリを解析

    2. クリア・テキストのuserpassword属性値を取得

    3. 指定したパスワード値チェックを実施

  4. パスワードが指定と一致する場合は、そのことがプラグインによってディレクトリ・サーバーに通知され、ディレクトリ・サーバーによって追加または変更が行われます。

    それ以外の場合は、次のいずれかのエラー・メッセージがプラグインによってディレクトリ・サーバーに送信され、その後、ディレクトリ・サーバーからクライアントに渡されます。

    ldap_add: UnKnown Error Encountered 
    ldap_add: additional info: PASSWORD POLICY VIOLATION:0000X, less than 8 chars 
    
    ldap_add: UnKnown Error Encountered 
    ldap_add: additional info: PASSWORD POLICY VIOLATION:0000X, contains dictionary word 
    

    同じロジックがPRE ldapmodifyプラグインにも適用されます。

パスワード・ポリシー・プラグインが実行できる値チェックには、次のような種類があります。

  • アルファベットの最大および最小文字数

  • 数字の最大文字数

  • 記号の最大および最小文字数

  • 連続した文字の最大文字数

  • 任意の文字の最大インスタンス数

  • 辞書の語句かどうかの確認

45.2 カスタマイズされたパスワード・ポリシー・プラグインの管理

この項の例では、PL/SQLプログラムpluginpkg.sqlを使用しています。

この項の例では、PL/SQLプログラムpluginpkg.sqlを使用しています。サンプルPL/SQLパッケージpluginpkg.sqlの内容。通常、このパッケージには次のものが含まれます。

  • プラグイン・モジュール: pre_addおよびpre_modify

  • パスワードが最小文字数要件の8文字を満たしていること、および4文字よりも長い辞書の語句を含んでいないことを確認する値チェック・ファンクションisGoodPwd

この例では、ユーザーが8文字未満のuserpassword値を入力すると、リクエストは拒否されます。同様に、ユーザー・パスワードを変更する際に、新しいパスワード値が8文字未満の場合はリクエストが拒否されます。また、userpasswordの値supersundayを使用してユーザー・パスワードを登録または変更しようとすると、superおよびsundayが辞書にあるため、そのパスワードは拒否されます。

辞書は、4文字を超える語句のリストで、最初はファイルwords.txtに格納されます。プラグインを実装する前に、データベース表を設定して語句を格納します。表を設定するには、create.sqlを使用します。内容は次のとおりです。

drop table mydic;
create table mydic (word varchar2(1024));
commit;
exit;

続いて、次のsqlldrコマンドを使用して語句を表に格納します。

sqlldr control=words.txt userid=ods/ods_password

この項では、次の項目について説明します。

45.2.1 PL/SQLプログラムのロードおよび登録

この項では、PL/SQLプログラムをロードおよび登録する手順を説明します。

スタンドアロンの値チェックPL/SQLプログラムを実装したら、次の手順を実行します。

  1. プラグイン・パッケージをデータベースにロードします。この例では、次のように入力します。
    sqlplus ods @pluginpkg.sql
    
  2. プラグインを登録します。この例では、次の内容のファイルpluginreg.datを使用します。
    ### add plugin ###
    dn: cn=pre_add_plugin,cn=plugin,cn=subconfigsubentry
    objectclass:orclPluginConfig
    objectclass:top
    orclpluginname:pwd_plugin
    orclplugintype:operational
    orclplugintiming:pre
    orclpluginldapoperation:ldapadd
    orclpluginenable:1
    orclpluginversion:1.0.1
    cn:pre_add_plugin
    orclpluginsubscriberdnlist:dc=com;o=IMC ,c=US
     
    ### modify plugin ###
    dn: cn=pre_mod_plugin,cn=plugin,cn=subconfigsubentry
    objectclass:orclPluginConfig
    objectclass:top
    orclpluginname:pwd_plugin
    orclplugintype:operational
    orclplugintiming:pre
    orclpluginldapoperation:ldapmodify
    orclpluginenable:1
    orclpluginversion:1.0.1
    cn:pre_mod_plugin
    orclpluginsubscriberdnlist:dc=com;o=IMC ,c=US
    orclpluginattributelist:userpassword
    

    このプラグインでは、ldapaddリクエストまたはldapmodifyリクエストを受け取った際に起動する2つのプラグイン・モジュールをディレクトリ・サーバーに認識させています。ターゲット・エントリがdc=comまたはo=IMC,c=US下の場合のみプラグインが起動するように、orclpluginsubscriberdnlist:dc=com;o=IMC,c=USを使用しています。

    このファイルをディレクトリに追加するには、次のとおり入力します。

    ldapadd -p portnum -h hostname -D cn=orcladmin -q -v -f pluginreg.dat

45.2.2 パスワード・ポリシー・プラグインのコード化

標準PL/SQL文字ファンクションを使用して、パスワード値を処理できます。

正規表現を行うPL/SQLプログラムをダウンロードします。値チェック・ファンクションとプラグイン・モジュールを統合することが重要です。

45.2.3 パスワード・ポリシー・プラグインのデバッグ

ディレクトリ・サーバー・プラグインを有効化すると、プラグインのプロセスと内容を調べることができます。

ディレクトリ・サーバー・プラグインのデバッグを設定するには、次のコマンドを実行します。

sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsu.pls

ディレクトリ・サーバー・プラグインのデバッグを有効にするには、次のコマンドを実行します。

sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdon.pls 

ディレクトリ・サーバー・プラグインのデバッグを無効にするには、次のコマンドを実行します。

sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdof.pls 

ディレクトリ・サーバー・プラグインのデバッグ・メッセージを表示するには、次のコマンドを実行します。

sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsh.pls

ディレクトリ・サーバー・プラグインのデバッグ・メッセージを削除するには、次のコマンドを実行します。

sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdde.pls

45.2.4 サンプルPL/SQLパッケージpluginpkg.sqlの内容

この例で使用するスクリプトpluginpkg.sqlの内容は次のとおりです。

CREATE OR REPLACE PACKAGE pwd_plugin AS
 
PROCEDURE pre_add (ldapplugincontext IN  ODS.plugincontext,
		   dn       IN  VARCHAR2,
		   entry    IN  ODS.entryobj,
		   rc       OUT INTEGER,
		   errormsg OUT VARCHAR2
		   );
 
PROCEDURE pre_modify (ldapplugincontext IN  ODS.plugincontext,
		      dn       IN  VARCHAR2,
		      mods     IN  ODS.modlist,
		      rc       OUT INTEGER,
		      errormsg OUT VARCHAR2
		      );
 
-- Function: isGoodPwd
-- Parameter: inpwd
-- Purpose: 1. check if the password is at least 
--          8 characters long
--          2. check if the password contains a
--          dictionary word (longer than 4 characters)
 
FUNCTION isGoodPwd(inpwd IN VARCHAR2)
  RETURN INTEGER;
 
 
END pwd_plugin;
/
  
show error
  
CREATE OR REPLACE PACKAGE BODY pwd_plugin AS
 
FUNCTION isGoodPwd(inpwd IN VARCHAR2)
  RETURN INTEGER
  IS
     i NUMBER;
     ret NUMBER DEFAULT 1;
     minpwdlen NUMBER DEFAULT 8;
     len       NUMBER DEFAULT 0;
     lcount    NUMBER DEFAULT 0;
     matched   VARCHAR2(1024) DEFAULT NULL;
 
     CURSOR c1 IS
     SELECT word FROM mydic WHERE length(word) > 4
     AND instr(lower(inpwd), lower(word), 1, 1) > 0;
 
BEGIN
   plg_debug( '=== begin of ISGOODPWD ===');
   plg_debug( 'password = ' || inpwd);
   len := LENGTH(inpwd);
   plg_debug( 'password length = ' || len);
   
   IF len < minpwdlen THEN
      RETURN 0;
    ELSE
      OPEN c1;
      LOOP
        FETCH c1 INTO matched;
        EXIT WHEN c1%NOTFOUND;
        lcount := lcount + 1;
      END LOOP;
      plg_debug( 'count = ' || lcount);
      IF lcount > 0 THEN
        RETURN 2;
      ELSE  
        RETURN ret;
      END IF;
   END IF;
 
   plg_debug( '=== end of ISGOODPWD ===');
 
EXCEPTION
   WHEN OTHERS THEN
      plg_debug( 'Exception in isGoodPwd(). Error code is ' || TO_CHAR(SQLCODE));
      plg_debug( '   ' || Sqlerrm);
      RETURN 0;
END;
 
 
PROCEDURE pre_add (ldapplugincontext IN  ODS.plugincontext,
		   dn       IN  VARCHAR2,
		   entry    IN  ODS.entryobj,
		   rc       OUT INTEGER,
		   errormsg OUT VARCHAR2
		   )
  IS
     inpwd VARCHAR2(256) DEFAULT NULL;
     ret   NUMBER        DEFAULT 1;
BEGIN
   plg_debug( '=== begin of PRE_ADD_PLUGIN ===');
   plg_debug( 'dn = ' || dn);
   
   plg_debug( 'entry obj ' || ':entryname = ' || entry.entryname);
   
   FOR l_counter1 IN 1..entry.attr.COUNT LOOP
      plg_debug( 'attrname[' || l_counter1 || '] = ' ||
		 entry.attr(l_counter1).attrname);      
      FOR l_counter2 IN 1..entry.attr(l_counter1).attrval.COUNT LOOP
	 plg_debug( entry.attr(l_counter1).attrname || 
		    '[' || l_counter1 || ']' ||
		    '.val[' || l_counter2 || '] = ' ||	   	
		    entry.attr(l_counter1).attrval(l_counter2));	     
      END LOOP;
      
      IF entry.attr(l_counter1).attrname = 'userpassword' THEN
	 inpwd := entry.attr(l_counter1).attrval(1);
	 -- assuming only one attr val for userpassword
      END IF;
      
   END LOOP;
   
   IF (inpwd IS NOT NULL) THEN
      ret := isGoodPwd(inpwd);
   END IF;
   
   IF (inpwd IS NULL OR ret = 0) THEN
      rc := 1;
      errormsg := 'PASSWORD POLICY VIOLATION:0000X, less than 8 chars';
      plg_debug( ' we got an invalid password, too short ');
    ELSIF (ret = 2) THEN
      rc := 1;
      errormsg := 'PASSWORD POLICY VIOLATION:0000X, contains dictionary word';
      plg_debug( ' we got an invalid password, dictionary word ');
    ELSE
      plg_debug( ' we got a good password ');
      rc := 0;
      errormsg := 'no pre_mod plguin error msg';
   END IF;
   
   plg_debug( '=== end of PRE_ADD_PLUGIN ===');
   
EXCEPTION
   WHEN OTHERS THEN
      plg_debug( 'Exception in PRE_ADD plugin. Error code is ' || TO_CHAR(SQLCODE));
      plg_debug( '   ' || Sqlerrm);
      rc := 1;
      errormsg := 'exception: pre_add plguin';
END;
 
PROCEDURE pre_modify (ldapplugincontext IN  ODS.plugincontext,
		      dn       IN  VARCHAR2,
		      mods     IN  ODS.modlist,
		      rc       OUT INTEGER,
		      errormsg OUT VARCHAR2
		      )
  IS
     old_passwd VARCHAR2(256) DEFAULT NULL;
     new_passwd VARCHAR2(256) DEFAULT NULL;
     ret        NUMBER        DEFAULT 1;
     
BEGIN
   plg_debug( '=== begin of PRE_MOD_PLUGIN ===');
   plg_debug( dn);
 
   FOR l_counter1 IN 1..mods.COUNT LOOP
      IF (mods(l_counter1).operation = 2) AND
	(mods(l_counter1).type = 'userpassword') THEN
	 
	 FOR l_counter2 IN 1..mods(l_counter1).vals.COUNT LOOP
	    new_passwd := mods(l_counter1).vals(l_counter2).val;
	 END LOOP;
      END IF;
      
      IF (mods(l_counter1).operation = 0) AND
	(mods(l_counter1).type = 'userpassword') THEN
	 
	 FOR l_counter2 IN 1..mods(l_counter1).vals.COUNT LOOP
	    new_passwd := mods(l_counter1).vals(l_counter2).val;
	 END LOOP;
      END IF;
      
      IF (mods(l_counter1).operation = 1) AND
	(mods(l_counter1).type = 'userpassword') THEN
	 
	 FOR l_counter2 IN 1..mods(l_counter1).vals.COUNT LOOP
	    old_passwd := mods(l_counter1).vals(l_counter2).val;
	 END LOOP;
      END IF;
   END LOOP;
 
   plg_debug(' new password: ' || new_passwd);
   plg_debug(' old password: ' || old_passwd);
        
   IF (new_passwd IS NOT NULL) THEN
      ret := isGoodPwd(new_passwd);
   END IF;
   
   IF (new_passwd IS NULL OR ret = 0) THEN
      rc := 1;
      errormsg := 'PASSWORD POLICY VIOLATION:0000X, less than 8 chars';
      plg_debug( ' we got an invalid password, too short ');
    ELSIF (ret = 2) THEN
      rc := 1;
      errormsg := 'PASSWORD POLICY VIOLATION:0000X, contains dictionary word';
      plg_debug( ' we got an invalid password, dictionary word ');
    ELSE
      plg_debug( ' we got a good password ');
      rc := 0;
      errormsg := 'no pre_mod plguin error msg';
   END IF;
   
   plg_debug( '=== end of PRE_MOD_PLUGIN ===');
   
EXCEPTION
   WHEN OTHERS THEN
      plg_debug( 'Exception in PRE_MODIFY plugin. Error code is ' || TO_CHAR(SQLCODE));
      plg_debug( '   ' || Sqlerrm);
      rc := 1;
      errormsg := 'exception: pre_mod plguin';
END;
 
END pwd_plugin;
/
show error
  
 
EXIT;