45 Configuring a Customized Password Policy Plug-In

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.

The following topics describe how to install, configure, and enable a customized password policy plug-in:

45.1 Configuring a Customized Password Policy Plug-in

When a user wants to add or modify a password, customized password value checking takes place as follows:

  1. The client sends the directory server either an ldapadd or ldapmodify request.

  2. Before the directory server makes the addition or modification, it passes the password value to the plug-in.

  3. The plug-in

    1. Parses the entry

    2. Captures the userpassword attribute value in clear text

    3. Implements whatever password value checking you have specified

  4. 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

45.2 Managing a Customized Password Policy Plug-in

The example in this section uses the PL/SQL program, pluginpkg.sql.

The example in this section uses the PL/SQL program, pluginpkg.sql. Sample PL/SQL Package pluginpkg.sql Contents. 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 the following topics:

45.2.1 Loading and Registering the PL/SQL Program

This section describes the procedure to load and register the PL/SQL program.

After you implement the standalone value checking PL/SQL program, do the following:.

  1. Load the plug-in package into the database. In this example, we enter:
    sqlplus ods @pluginpkg.sql
    
  2. 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: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
    

    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

45.2.2 Coding the Password Policy Plug-in

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.

45.2.3 Debugging the Password Policy Plug-in

First you enable the directory server plug-in to help 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

45.2.4 Sample PL/SQL Package pluginpkg.sql Contents

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;