45 カスタマイズされたパスワード・ポリシー・プラグインの構成
次の各トピックでは、カスタマイズされたパスワード・ポリシー・プラグインをインストール、構成および有効化する方法について説明します。
45.1 カスタマイズされたパスワード・ポリシー・プラグインの構成
パスワードを追加または変更する場合、カスタマイズされたパスワード値チェックが次のように処理されます。
-
クライアントが、ldapaddリクエストまたはldapmodifyリクエストをディレクトリ・サーバーに送信します。
-
ディレクトリ・サーバーは、追加または変更を行う前にパスワード値をプラグインに渡します。
-
プラグインは次のように動作します
-
エントリを解析
-
クリア・テキストの
userpassword
属性値を取得 -
指定したパスワード値チェックを実施
-
-
パスワードが指定と一致する場合は、そのことがプラグインによってディレクトリ・サーバーに通知され、ディレクトリ・サーバーによって追加または変更が行われます。
それ以外の場合は、次のいずれかのエラー・メッセージがプラグインによってディレクトリ・サーバーに送信され、その後、ディレクトリ・サーバーからクライアントに渡されます。
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プログラムを実装したら、次の手順を実行します。
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;