Oracle Internet Directory uses plug-ins to add password value checking to its other password policy management capabilities. These plug-ins enable you to verify that, for example, a new or modified password has the specified minimum length. You can customize password value checking to meet your own requirements.
This chapter contains these topics:
Introduction to Configuring a Customized Password Policy Plug-in
Installing, Configuring, and Enabling a Customized Password Policy Plug-in
When a user wants to add or modify a password, customized password value checking takes place as follows:
The client sends the directory server either an ldapadd or ldapmodify request.
Before the directory server makes the addition or modification, it passes the password value to the plug-in.
The plug-in
Parses the entry
Captures the userpassword attribute value in clear text
Implements whatever password value checking you have specified
If the password meets the specification, then the plug-in notifies the directory server accordingly, and the directory server makes the addition or modification.
Otherwise, the plug-in sends one of the following error messages to the directory server, which, in turn, passes it to the client.
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
The same logic applies to the PRE ldapmodify plug-in.
The various kinds of value checks that a password policy plug-in could perform include:
Minimum and maximum number of alphabetic characters
Maximum number of numeric characters
Minimum and maximum number of punctuation characters
Maximum number of consecutive characters
Maximum number of instances of any character
Whether it is a dictionary word
This example uses the PL/SQL program, pluginpkg.sql. "Contents of Sample PL/SQL Package pluginpkg.sql" describes this program. In general, this package contains:
Two plug-in modules: pre_add and pre_modify
One value checking function, isGoodPwd, which verifies that a password meets the minimum length requirement of eight characters and that it does not contain a dictionary word that is longer than four characters
Thus, in this example, if you try to add a user with the userpassword value less than eight characters, then the request is rejected. Similarly, if you try to modify a user password, and the new password value is less than eight characters, then the request is rejected. Also, if you try to add or modify a user with the userpassword supersunday, the password is rejected because super and sunday are dictionary words.
The dictionary is a list of words longer than four characters, initially stored in a file called words.txt. Before we implement the plug-in, we set up a database table and store the words into the table. To set up the table we use create.sql, which has the following contents:
drop table mydic; create table mydic (word varchar2(1024)); commit; exit;
Then we load the words into the table using the sqlldr command:
sqlldr control=words.txt userid=ods/ods_password
This section contains these topics:
After you implement the standalone value checking PL/SQL program, do the following:.
Load the plug-in package into the database. In this example, we enter:
sqlplus ods @pluginpkg.sql
Register the plug-in. This example uses a file named pluginreg.dat, which contains the following:
### add plugin ### dn: cn=pre_add_plugin,cn=plugin,cn=subconfigsubentry objectclass:orclPluginConfig objectclass:top orclpluginname:pwd_plugin orclplugintype:configuration 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:configuration orclplugintiming:pre orclpluginldapoperation:ldapmodify orclpluginenable:1 orclpluginversion:1.0.1 cn:pre_mod_plugin orclpluginsubscriberdnlist:dc=com;o=IMC ,c=US orclpluginattributelist:userpassword
Note that, in this plug-in, we let the directory server know that there are two plug-in modules to invoke when it receives ldapadd or ldapmodify requests. We use orclpluginsubscriberdnlist:dc=com;o=IMC,c=US so that the plug-in is invoked ONLY if the target entry is under dc=com or o=IMC,c=US.
To add this file to the directory, enter the following:
ldapadd -p portnum -h hostname -D cn=orcladmin -q -v -f pluginreg.dat
You can use standard PL/SQL character functions to process the password value. Download any PL/SQL program that can do regular expression. The important thing is to integrate the value checking functions with your plug-in modules.
Turn on the directory server plug-in to help you examine the process and content of plug-ins.
To setup the directory server plug-in debugging, execute the following command:
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsu.pls
To enable directory server plug-in debugging, execute the following command:
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdon.pls
To disable directory server plug-in debugging, execute the following command:
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdof.pls
To show directory server plug-in debugging messages, execute the following command:
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdsh.pls
To delete directory server plug-in debugging messages, execute the following command:
sqlplus ods @$ORACLE_HOME/ldap/admin/oidspdde.pls
The script pluginpkg.sql, as used in this example, contains the following:
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;