この章では、Oracle Internet Directoryでプラグインを使用して、パスワード値のチェックを他のパスワード・ポリシー管理機能に追加する方法について説明します。具体的には、カスタマイズされたパスワード・ポリシー・プラグインをインストール、構成および有効化する方法について説明します。
このプラグインを使用すると、追加または変更されたパスワードが、指定された最小文字数以上であるかどうかなどを確認できます。個別の要件に合せて、パスワード値チェックをカスタマイズできます。
この章の内容は次のとおりです。
パスワードを追加または変更する場合、カスタマイズされたパスワード値チェックが次のように処理されます。
クライアントが、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プラグインにも適用されます。
パスワード・ポリシー・プラグインが実行できる値チェックには、次のような種類があります。
アルファベットの最大および最小文字数
数字の最大文字数
記号の最大および最小文字数
連続した文字の最大文字数
任意の文字の最大インスタンス数
辞書の語句かどうかの確認
この例は、PL/SQLプログラムpluginpkg.sqlを使用しています。第44.2.4項「サンプル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
この項の項目は次のとおりです。
スタンドアロンの値チェックPL/SQLプログラムを実装したら、次の手順を実行します。
プラグイン・パッケージをデータベースにロードします。この例では、次のように入力します。
sqlplus ods @pluginpkg.sql
プラグインを登録します。この例では、次の内容のファイル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
標準PL/SQL文字ファンクションを使用して、パスワード値を処理できます。正規表現を行うPL/SQLプログラムをダウンロードします。値チェック・ファンクションとプラグイン・モジュールを統合することが重要です。
ディレクトリ・サーバー・プラグインを設定すると、プラグインのプロセスと内容を調べることができます。
ディレクトリ・サーバー・プラグインのデバッグを設定するには、次のコマンドを実行します。
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
この例で使用するスクリプト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;