ユーザー・セキュリティ資格証明をOracle Internet Directory以外のリポジトリ(データベースや他のLDAPディレクトリなど)に格納し、Oracleコンポーネントに対するユーザー認証に使用できます。資格証明をOracle Internet Directoryに格納する必要はなく、常に同期化させる必要はありません。外部リポジトリに格納された資格証明によるユーザー認証を、外部認証と呼びます。
この章の項目は次のとおりです。
|
注意: この章で言及するOracle Single Sign-Onはすべて、Oracle Single Sign-On 10g(10.1.4.3.0)以上のことです。 |
Oracle Internet Directoryに格納されたセキュリティ資格証明に基づく認証を、ネイティブ認証と呼びます。ユーザーがセキュリティ資格証明を入力すると、ディレクトリ・サーバーはOracle Internet Directoryに格納されている資格証明とそれを比較します。資格証明が一致すると、ディレクトリ・サーバーはユーザーを認証します。
一方、Oracle Internet Directory以外のディレクトリに格納されたセキュリティ資格証明に基づく認証を外部認証と呼びます。ユーザーがセキュリティ資格証明を入力すると、ディレクトリ・サーバーは他のディレクトリに格納されている資格証明とそれを比較します。この比較は次のものを使用して行われます。
この例は、PL/SQLプログラムoidexaup.sqlを使用しています。このプログラムについては、第45.4項「PL/SQLパッケージoidexaup.sqlの作成」で説明します。このパッケージを使用して、外部認証プラグインPL/SQLパッケージをインストールします。次の項目が含まれます。
2つのプラグイン(when_compare_replaceおよびwhen_modify_replace)
ユーティリティ・ファンクション(get_nickname)
統合されたパッケージはプラグイン・パッケージOIDEXTAUTHです。これは、デプロイメントの要件に応じて変更できるテンプレートとしても機能します。
外部認証プラグインをインストールおよび構成し、有効にする手順は、次のとおりです。
スタンドアロンの外部認証PL/SQLプログラムを実装します。たとえば、ユーザー名とパスワードで認証する場合は、この2つのパラメータを取るPL/SQLプログラムを使用する必要があります。
サンプル・コードoidexaup.sqlで、auth_externalはプログラム・パッケージ名、authenticate_userは認証を行う関数です。次の手順に進む前に、このスタンドアロン・プログラムが適切に動作することを確認します。
スタンドアロンのプログラムをプラグイン・モジュールに登録します。
プラグイン・パッケージをデータベースにロードします。この例では、次のように入力します。
sqlplus ods/odspwd @oidexaup.sql
プラグインを登録します。プラグインの起動に必要な情報をディレクトリ・サーバーに提供するLDIFファイルを作成し、アップロードすることにより登録します。
この例では、次の内容のファイルoidexauth.ldifを使用します。
dn: cn=whencompare,cn=plugin,cn=subconfigsubentry objectclass:orclPluginConfig objectclass:top orclpluginname:oidextauth orclplugintype:configuration orclplugintiming:when orclpluginldapoperation:ldapcompare orclpluginenable:1 orclpluginversion:1.0.1 orclPluginIsReplace:1 cn:whencompare orclpluginsubscriberdnlist:dc=com;o=IMC,c=US orclpluginattributelist:userpassword orclpluginrequestgroup:$prgdn dn: cn=whenmodify,cn=plugin,cn=subconfigsubentry objectclass:orclPluginConfig objectclass:top orclpluginname:oidextauth orclplugintype:configuration orclplugintiming:when orclpluginldapoperation:ldapmodify orclpluginenable:1 orclpluginversion:1.0.1 orclPluginIsReplace:1 cn:whenmodify orclpluginsubscriberdnlist:dc=com;o=IMC,c=US orclpluginattributelist:userpassword orclpluginrequestgroup:$prgdn
このファイルでは、ldapcompareリクエストまたはldapmodifyリクエストがあった際に2つのプラグインが起動することをディレクトリ・サーバーに通知します。
ターゲット・エントリがdc=comまたはo=IMC,c=US下の場合のみプラグインが起動するように、orclpluginsubscriberdnlist:dc=com;o=IMC,c=USを使用します。
$prgdnを、プラグイン・リクエスト・グループ識別名に置換します。これはオプションの推奨セキュリティ機能です。Oracle Single Sign-Onとの統合には、この値が必須フィールドです。入力されたグループのメンバーのみがプラグインを起動できます。複数のグループの入力が可能です。エントリを区切るには、セミコロンを使用します。
推奨デフォルトは、cn=OracleUserSecurityAdmins,cn=Groups,cn=OracleContextおよびcn=OracleDASAdminGroup,cn=Groups,cn=OracleContext,o=default_subscriber,dc=comです。Oracle Single Sign-On Serverは、最初のグループのメンバーです。また、デプロイメント環境に合せてo=default_subscriberを正しい値に置換してください。
このファイルをディレクトリに追加するには、次のとおり入力します。
ldapadd -p portnum -h hostname -D cn=orcladmin -q -v \
-f oidexauth.ldif
これで、すべての準備が完了しました。ldapcompareコマンドライン・ツールを使用すると、Oracle Single Sign-Onからユーザーを認証する前にプラグインおよび認証プログラムが適切に動作していることを確認できます。
この例では、ユーザー・パスワードを外部変更するためのプラグイン・コードも提供されています。
ディレクトリ・サーバー・プラグインを設定すると、プラグインのプロセスと内容を調べることができます。
ディレクトリ・サーバー・プラグインのデバッグを設定するには、次のコマンドを実行します。
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsu.sql
ディレクトリ・サーバー・プラグインのデバッグを有効にするには、次のコマンドを実行します。
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdon.sql
ディレクトリ・サーバー・プラグインのデバッグを無効にするには、次のコマンドを実行します。
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdof.sql
ディレクトリ・サーバー・プラグインのデバッグ・メッセージを表示するには、次のコマンドを実行します。
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsh.sql
ディレクトリ・サーバー・プラグインのデバッグ・メッセージを削除するには、次のコマンドを実行します。
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdde.sql
この例で使用するスクリプトoidexaup.sqlの内容は次のとおりです。
CREATE OR REPLACE PACKAGE OIDEXTAUTH AS
PROCEDURE when_compare_replace (ldapplugincontext IN ODS.plugincontext,
result OUT INTEGER,
dn IN VARCHAR2,
attrname IN VARCHAR2,
attrval IN VARCHAR2,
rc OUT INTEGER,
errormsg OUT VARCHAR2
);
PROCEDURE when_modify_replace (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2,
mods IN ODS.modlist,
rc OUT INTEGER,
errormsg OUT VARCHAR2
);
FUNCTION get_nickname (dn IN VARCHAR2,
my_session IN DBMS_LDAP.session)
RETURN VARCHAR2;
END OIDEXTAUTH;
/
SHOW ERROR
CREATE OR REPLACE PACKAGE BODY OIDEXTAUTH AS
-- We use this function to convert the dn to nickname.
-- When OID server receives the ldapcompare request, it
-- only has the dn information. We need to use DBMS_LDAP_UTL
-- package to find out the nickname attribute value of
-- the entry.
FUNCTION get_nickname (dn IN VARCHAR2,
my_session IN DBMS_LDAP.session)
RETURN VARCHAR2
IS
my_pset_coll DBMS_LDAP_UTL.PROPERTY_SET_COLLECTION;
my_property_names DBMS_LDAP.STRING_COLLECTION;
my_property_values DBMS_LDAP.STRING_COLLECTION;
user_handle DBMS_LDAP_UTL.HANDLE;
user_id VARCHAR2(2000);
user_type PLS_INTEGER;
user_nickname VARCHAR2(256) DEFAULT NULL;
my_attrs DBMS_LDAP.STRING_COLLECTION;
retval PLS_INTEGER;
BEGIN
plg_debug( '=== Beginning of get_nickname() === ');
user_type := DBMS_LDAP_UTL.TYPE_DN;
user_id := dn;
retval := DBMS_LDAP_UTL.create_user_handle(user_handle, user_type, user_id);
plg_debug('create_user_handle() Returns ' || To_char(retval));
retval := DBMS_LDAP_UTL.get_user_properties(my_session,
user_handle,
my_attrs,
DBMS_LDAP_UTL.NICKNAME_PROPERTY,
my_pset_coll);
plg_debug( 'get_user_properties() Returns ' || To_char(retval));
IF my_pset_coll.COUNT > 0 THEN
FOR i IN my_pset_coll.first .. my_pset_coll.last LOOP
retval := DBMS_LDAP_UTL.get_property_names(my_pset_coll(i),
my_property_names);
IF my_property_names.COUNT > 0 THEN
FOR j IN my_property_names.first .. my_property_names.last LOOP
retval := DBMS_LDAP_UTL.get_property_values(my_pset_coll(i),
my_property_names(j),
my_property_values);
IF my_property_values.COUNT > 0 THEN
FOR k IN my_property_values.FIRST..my_property_values.LAST LOOP
user_nickname := my_property_values(k);
plg_debug( 'user nickname = ' || user_nickname);
END LOOP;
END IF;
END LOOP;
END IF; -- IF my_property_names.count > 0
END LOOP;
END IF; -- If my_pset_coll.count > 0
plg_debug( 'got user_nickname: ' || user_nickname);
-- Free my_properties
IF my_pset_coll.count > 0 then
DBMS_LDAP_UTL.free_propertyset_collection(my_pset_coll);
END IF;
DBMS_LDAP_UTL.free_handle(user_handle);
RETURN user_nickname;
EXCEPTION
WHEN OTHERS THEN
plg_debug('Exception in get_nickname. Error code is ' || to_char(sqlcode));
plg_debug(' ' || Sqlerrm);
RETURN NULL;
END;
PROCEDURE when_compare_replace (ldapplugincontext IN ODS.plugincontext,
result OUT INTEGER,
dn IN VARCHAR2,
attrname IN VARCHAR2,
attrval IN VARCHAR2,
rc OUT INTEGER,
errormsg OUT VARCHAR2
)
IS
retval pls_integer;
lresult BOOLEAN;
my_session DBMS_LDAP.session;
my_property_names DBMS_LDAP.STRING_COLLECTION;
my_property_values DBMS_LDAP.STRING_COLLECTION;
my_attrs DBMS_LDAP.STRING_COLLECTION;
my_pset_coll DBMS_LDAP_UTL.PROPERTY_SET_COLLECTION;
user_handle DBMS_LDAP_UTL.HANDLE;
user_id VARCHAR2(2000);
user_type PLS_INTEGER;
user_nickname VARCHAR2(60);
remote_dn VARCHAR2(256);
i PLS_INTEGER;
j PLS_INTEGER;
k PLS_INTEGER;
BEGIN
plg_debug( '=== Begin of WHEN-COMPARE-REPLACE plug-in');
plg_debug( 'DN = ' || dn);
plg_debug( 'Attr = ' || attrname);
--plg_debug( 'Attrval = ' || attrval);
DBMS_LDAP.USE_EXCEPTION := FALSE;
errormsg := 'No error msg';
rc := 0;
-- converting dn to nickname
my_session := LDAP_PLUGIN.init(ldapplugincontext);
plg_debug( 'ldap_session =' || RAWTOHEX(SUBSTR(my_session,1,8)));
retval := LDAP_PLUGIN.simple_bind_s(ldapplugincontext, my_session);
plg_debug( 'simple_bind_res =' || TO_CHAR(retval));
user_nickname := get_nickname(dn, my_session);
plg_debug( 'user_nickname =' || user_nickname);
-- unbind from the directory
retval := DBMS_LDAP.unbind_s(my_session);
plg_debug( 'unbind_res Returns ' || To_char(retval));
IF (user_nickname IS NULL) THEN
result := 32;
errormsg := 'Can''t find the nickname';
plg_debug( 'Can''t find the nickname');
RETURN;
END IF;
plg_debug( '=== Now go to extauth ');
BEGIN
retval := auth_external.authenticate_user(user_nickname, attrval);
plg_debug( 'auth_external.authenticate_user() returns = ' || 'True');
result := 6; -- compare result is TRUE
EXCEPTION
WHEN OTHERS THEN
result := 5; -- compare result is FALSE
plg_debug( 'auth_external.authenticate_user() returns = ' || 'False');
RETURN;
END;
plg_debug( '=== End of WHEN-COMPARE-REPLACE plug-in');
EXCEPTION
WHEN OTHERS THEN
rc := 1;
errormsg := 'Exception: when_compare_replace plugin';
plg_debug( 'EXCEPTION: ' || retval);
plg_debug('Exception in when_compare. Error code is ' || to_char(sqlcode));
plg_debug(' ' || Sqlerrm);
END;
PROCEDURE when_modify_replace (ldapplugincontext IN ODS.plugincontext,
dn IN VARCHAR2,
mods IN ODS.modlist,
rc OUT INTEGER,
errormsg OUT VARCHAR2
)
IS
retval pls_integer;
lresult BOOLEAN;
my_session DBMS_LDAP.SESSION;
my_property_names DBMS_LDAP.STRING_COLLECTION;
my_property_values DBMS_LDAP.STRING_COLLECTION;
my_attrs DBMS_LDAP.STRING_COLLECTION;
my_modval DBMS_LDAP.BERVAL_COLLECTION;
my_pset_coll DBMS_LDAP_UTL.PROPERTY_SET_COLLECTION;
user_handle DBMS_LDAP_UTL.HANDLE;
l_mod_array RAW(32);
user_id VARCHAR2(2000);
user_type PLS_INTEGER;
user_nickname VARCHAR2(2000);
old_passwd VARCHAR2(60) DEFAULT NULL;
new_passwd VARCHAR2(60) DEFAULT NULL;
remote_dn VARCHAR2(256);
i PLS_INTEGER;
j PLS_INTEGER;
k PLS_INTEGER;
BEGIN
plg_debug( '=== Begin of WHEN-MODIFY-REPLACE plug-in');
DBMS_LDAP.USE_EXCEPTION := FALSE;
user_type := DBMS_LDAP_UTL.TYPE_DN;
user_id := dn;
-- converting dn to nickname
my_session := LDAP_PLUGIN.init(ldapplugincontext);
plg_debug( 'ldap_session =' || RAWTOHEX(SUBSTR(my_session,1,8)));
retval := LDAP_PLUGIN.simple_bind_s(ldapplugincontext, my_session);
plg_debug( 'simple_bind_res =' || TO_CHAR(retval));
user_nickname := get_nickname(dn, my_session);
plg_debug( 'user_nickname =' || user_nickname);
-- unbind from the directory
retval := DBMS_LDAP.unbind_s(my_session);
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;
IF new_passwd IS NOT NULL AND old_passwd IS NOT NULL THEN
BEGIN
auth_external.change_passwd(user_nickname, old_passwd, new_passwd);
EXCEPTION
WHEN OTHERS THEN
rc := 1;
plg_debug( 'auth_external.change_passwd() raised exception.');
errormsg := 'auth_external.change_passwd() raised exception.';
RETURN;
END;
ELSIF new_passwd IS NOT NULL AND old_passwd IS NULL THEN
BEGIN
auth_external.reset_passwd(user_nickname, new_passwd);
EXCEPTION
WHEN OTHERS THEN
plg_debug( 'auth_external.reset_passwd() raised exception.');
rc := 1;
errormsg := 'auth_external.reset_passwd() raised exception.';
RETURN;
END;
ELSE
rc := 1;
errormsg := 'PLG_Exception. Not enough info to change passwd.';
END IF;
plg_debug( 'external change password succeed');
rc := 0;
errormsg := 'No when_mod_replace plguin error msg';
retval := DBMS_LDAP.unbind_s(my_session);
plg_debug( 'End of WHEN-MODIFY-REPLACE');
--COMMIT;
EXCEPTION
WHEN others THEN
rc := 1;
errormsg := 'PLG_Exception: when_modify_replace plguin';
plg_debug('Exception in when_modify. Error code is ' || to_char(sqlcode));
plg_debug(' ' || Sqlerrm);
END;
END OIDEXTAUTH;
/
SHOW ERRORS
--list
EXIT;