Sun Java System Directory Server Enterprise Edition 6.2 Developer's Guide

Chapter 13 Writing Password Quality Check Plug-Ins

Directory Server is delivered with a configurable password quality check plug-in. The plug-in allows you to guard against users who change their password to a value that is susceptible to typical English-language dictionary attacks. You might decide however to extend password quality checking further. This chapter shows how to write custom plug-ins that check password quality when the password is modified.

This chapter covers the following topics:

How Directory Server Uses Password Quality Check Plug-Ins

This section explains how Directory Server password policy configurations affect password quality checks. This section also explains what Directory Server expects your password quality check plug-in to do.

Password Policy to Check Password Quality

One aspect of password policy involves refusing passwords that do not meet your quality criteria. When a user submits a password value, that value is to be stored on her entry in the userPassword(5dsat) attribute. You might want the server to ensure the password value is not easy to guess or to discover. You might also want the server to log a warning when a weak password is accepted, or even to refuse weak passwords. Determining what you want is part of setting up a password policy.

The password policy entry attribute that governs to what extent the server checks password quality is pwdCheckQuality(5dsat). When this attribute is set to cause the server to check password quality, the server can call plug-ins to do so. This chapter shows how to write a plug-in that checks password quality.

For a more extensive explanation of password policy configuration, see Chapter 7, Directory Server Password Policy, in Sun Java System Directory Server Enterprise Edition 6.2 Administration Guide.

Whether to Implement a Password Check Plug-In

If you can achieve your password policy requirements by using the strong password check plug-in provided with Directory Server, do not write your own plug-in.

The strong password check plug-in provided with Directory Server can be configured for your deployment, checking the password quality aspects that you consider essential. You can configure the following:

These configuration settings are in addition to other checks. Such checks govern password length, whether the password matches common attributes on the entry, and so forth.

In many cases, you can avoid writing your own password check plug-in.

What a Password Check Plug-In Must Do

If you decide to implement your own password check plug-in, it must be of type passwordcheck. Your plug-in must implement at least the password checking function, SLAPI_PLUGIN_PASSWDCHECK_FN. This function verifies that all passwords supplied in the parameter block array, SLAPI_PASSWDCHECK_VALS, satisfy the quality check. The SLAPI_PASSWDCHECK_VALS may contain more than one password value.

The function must return the following values, depending on the password values to check.

-1

Return this value when you must send a failure result to the client application for a reason other than quality check failure. You might return this value for example when the server is busy loading a large dictionary file. The server then would not be ready to check password quality.

When you return this value, the server aborts the add or modify operation in progress.

0

Return this value when all passwords in SLAPI_PASSWDCHECK_VALS satisfy the password quality check.

>0

Return a positive integer when at least one of the passwords in SLAPI_PASSWDCHECK_VALS fails the password quality check.

When you return a positive integer, pass an error message back through SLAPI_RESULT_TEXT in the parameter block with slapi_pblock_set(). Allocate space for the message using slapi_ch_malloc(). The server takes responsibility to free the memory allocated for the message.

The message string must start with invalid password syntax: , exactly as it is given here.

The server sends the client application a result of LDAP_CONSTRAINT_VIOLATION, 19. If, however, passwordRootdnMayBypassModsChecks(5dsat) is set to allow password administrators, including the directory root DN user, to modify passwords irrespective of password policy checks, the server nevertheless accepts modifications by that user.

In the simple case used in this chapter, the work is minimal. For real—world cases, the work might however be significant, particularly if you choose to implement your own dictionary check. If you implement your own dictionary check, the plug-in might cache the dictionary content from a large file. In this case, load the dictionary as part of a plug-in start function rather than part of plug-in initialization. You thus avoid blocking startup of the server while the cache is constructed.

Writing a Custom Password Quality Check Plug-In

This section demonstrates how to write a plug-in that performs a simple password quality check.

Only code excerpts are shown in this chapter. The complete code example can be found where you installed Directory Server, install-path/ds6/examples/pwdcheck.c.

Checking Password Values

When Directory Server receives a request to add or modify a userPassword value, the server calls the registered passwordcheck plug-in. The server passes one or more values as a set of Slapi_Value structures in the parameter block. You can retrieve these values with slapi_pblock_get().

#include "slapi-plugin.h"

static int
check_pwd(Slapi_PBlock * pb)
{
    Slapi_Value ** pwdvals = NULL;
    slapi_pblock_get(pb, SLAPI_PASSWDCHECK_VALS, &pwdvals;);
}

Your code must then return zero, 0, when password values are acceptable. Your code must return nonzero when password values are unacceptable. In the simple case where bad password values are only those equal to secret12, the code is a quick strcmp.

#include "slapi-plugin.h"

/* Reject password values equal to secret12.               */
static int
check_pwd(Slapi_PBlock * pb)
{
    Slapi_Value ** pwdvals = NULL;

…

    /* Do not check values if none exist. */
    if (pwdvals == NULL) return 0;

    for (i=0 ; pwdvals[i] != NULL; i++) {
        const char * password = slapi_value_get_string(pwdvals[i]);
        if (strcmp("secret12", password) == 0) {
            slapi_pblock_set(pb, SLAPI_RESULT_TEXT,
               slapi_ch_strdup("invalid password syntax: Bad password!"));
            return 1;
        }
    }
    return 0;
}

Here, the code has Directory Server reject a password only when its value is secret12.

Initializing the Password Check Plug-In

Password check plug-ins register a password checking function, SLAPI_PLUGIN_PASSWDCHECK_FN, during initialization with the server.

#include "slapi-plugin.h"

int
pwdcheck_init(Slapi_PBlock * pb)
{
    int rc = 0;
    rc |=   slapi_pblock_set(
                pb,
                SLAPI_PLUGIN_VERSION,
                SLAPI_PLUGIN_CURRENT_VERSION
            );

    rc |=   slapi_pblock_set(
                pb,
                SLAPI_PLUGIN_DESCRIPTION,
                (void *) &pwd_desc; /* See the code for pwd_desc. */
            );

    rc |=   slapi_pblock_set(
                pb,
                SLAPI_PLUGIN_PASSWDCHECK_FN,
                (void *) check_pwd
            );
    return rc;
}

You can implement your own dictionary check that caches the dictionary content from a large file. In this case, load the dictionary as part of a plug-in start function rather than part of plug-in initialization. Thus, you avoid blocking startup of the server while the cache is constructed.

Testing the Password Check Plug-In

You test the plug-in using sample data delivered with Directory Server You use command-line tools to setup and register the plug-in.

ProcedureTo Set Up an Example Suffix

If you have not done so already, set up a directory instance with a suffix, dc=example,dc=com, containing data loaded from a sample LDIF file, install-path/ds6/ldif/Example.ldif.

  1. Create a new Directory Server instance.

    For example:


    $ dsadm create /local/ds
    Choose the Directory Manager password:
    Confirm the Directory Manager password:
    $ 
  2. Start the new Directory Server instance.

    For example:


    $ dsadm start /local/ds
    Server started: pid=4705
    $ 
  3. Create a suffix called dc=example,dc=com.

    For example, with long lines folded for the printed page:


    $ dsconf create-suffix -h localhost -p 1389 dc=example,dc=com
    Enter "cn=directory manager" password: 
    Certificate "CN=defaultCert, CN=hostname:1636" presented by the
     server is not trusted.
    Type "Y" to accept, "y" to accept just once,
     "n" to refuse, "d" for more details: Y
    $ 
  4. Load the sample LDIF.

    For example, with long lines folded for the printed page:


    $ dsconf import -h localhost -p 1389 \
     /opt/SUNWdsee/ds6/ldif/Example.ldif dc=example,dc=com
    Enter "cn=directory manager" password:  
    New data will override existing data of the suffix
     "dc=example,dc=com".
    Initialization will have to be performed on replicated suffixes. 
    Do you want to continue [y/n] ? y
    
    ## Index buffering enabled with bucket size 16
    ## Beginning import job...
    ## Processing file "/opt/SUNWdsee/ds6/ldif/Example.ldif"
    ## Finished scanning file "/opt/SUNWdsee/ds6/ldif/Example.ldif" (160 entries)
    ## Workers finished; cleaning up...
    ## Workers cleaned up.
    ## Cleaning up producer thread...
    ## Indexing complete.
    ## Starting numsubordinates attribute generation.
     This may take a while, please wait for further activity reports.
    ## Numsubordinates attribute generation complete. Flushing caches...
    ## Closing files...
    ## Import complete. Processed 160 entries in 5 seconds.
     (32.00 entries/sec)
    
    Task completed (slapd exit code: 0).
    $ 
See Also

You can use Directory Service Control Center to perform this task. For more information, see the Directory Service Control Center online help.

ProcedureTo Register the Plug-In

If you have not already done so, build the example plug-in library and activate both plug-in informational logging and the example plug-in.

  1. Build the plug-in.

    Hint Use install-path/examples/Makefile or install-path/examples/Makefile64.

  2. Configure Directory Server to log plug-in informational messages and load the plug-in.

    Hint Use the commands specified in the comments at the outset of the plug-in source file.

  3. Restart Directory Server.


    $ dsadm restart instance-path
    

ProcedureTo Use the Password Check Plug-In

Before You Begin

Populate the suffix dc=example,dc=com with sample data. Also, register the plug-in with Directory Server.

  1. Enforce password quality checking so Directory Server calls your password check plug-in.


    $ dsconf set-server-prop -h localhost -p 1389 \
     pwd-check-enabled:on pwd-strong-check-enabled:off
  2. Enable logging of informational messages.


    $ dsconf set-log-prop -h localhost -p 1389 error level:err-plugins
  3. Prepare an entry that tests your password quality check.


    $ cat quentin.ldif
    dn: uid=qcubbins,ou=People,dc=example,dc=com
    objectclass: top
    objectclass: person
    objectclass: organizationalPerson
    objectclass: inetOrgPerson
    uid: qcubbins
    givenName: Quentin
    sn: Cubbins
    cn: Quentin Cubbins
    mail: quentin.cubbins@example.com
    userPassword: secret12
    
  4. Add the entry to the directory.


    $ ldapmodify -a -D uid=kvaughan,ou=people,dc=example,dc=com \
    -w bribery -h localhost -p 1389 -f quentin.ldif
    
    adding new entry uid=qcubbins,ou=People,dc=example,dc=com
    ldap_add_s: Constraint violation
  5. Check the errors log for further information.


    $ grep secret12 /local/ds/logs/errors
    [16/Feb/2006:18:13:06 +0100] - INFORMATION - 
    Sample password check plug-in - conn=0 op=1 msgId=2 -  
    Invalid password: secret12

    The example log message as shown has been wrapped for readability in the printed version of this document.