Sun Directory Server Enterprise Edition 7.0 Developer's Guide

Chapter 11 Writing Password Storage Scheme Plug-Ins

This chapter explains how to write plug-ins that allow you to modify how Directory Server stores password attribute values.

This chapter covers the following topics:

Calling Password Storage Scheme Plug-Ins

This section describes the circumstances in which Directory Server calls password storage scheme plug-ins. This section also describes how password values are expected to be handled by the plug-ins.

Types of Password Storage Scheme Plug-Ins

Two types of password storage scheme plug-ins work with Directory Server, pwdstoragescheme and reverpwdstoragescheme. The pwdstoragescheme type is one-way. After the server encodes and stores a password, the password is not decoded. The pwdstoragescheme type therefore includes plug-in functions only for encoding passwords to be stored and for comparing incoming passwords with encoded, stored passwords. The reverpwdstoragescheme type is reversible, in that the plug-in allows Directory Server to encode and decode values. The reversible type therefore includes encode, compare, and decode plug-in functions.


Note –

This chapter covers the one-way type pwdstoragescheme plug-ins.

The reversible type is for internal use only.


Preinstalled Schemes

Existing schemes delivered with Directory Server are provided as password storage scheme plug-ins. Search cn=config for entries whose DN contains cn=Password Storage Schemes. The default password storage scheme uses the Salted Secure Hashing Algorithm (SSHA).

You can change the password storage scheme used to encode user passwords. See Chapter 7, Directory Server Password Policy, in Sun Directory Server Enterprise Edition 7.0 Administration Guide for instructions.

Effect on Password Attribute Values

Password storage scheme plug-in functions act on userPassword attribute values. Directory Server registers password storage scheme plug-ins at startup. After startup, any registered, enabled password storage scheme plug-in can then be used to encode password values. The plug-ins can also be used to compare incoming passwords to the encoded values. Which plug-in Directory Server invokes depends on the password storage scheme that is used for the entry in question.

Invocation for Add and Modify Requests

Add and modify requests can imply that Directory Server encode an input password, and then store it in the directory. First, Directory Server determines the storage scheme for the password value. Next, it invokes the plug-in encode function for the appropriate scheme. The encode function returns the encoded password to Directory Server.

Invocation for Bind Requests

Bind requests imply that Directory Server compares an input password value to a stored password value. As for add and modify requests, Directory Server determines the storage scheme for the password value. Next, the server invokes the plug-in compare function for the appropriate scheme. The compare scheme returns an int that communicates to Directory Server whether the two passwords match as described in Comparing a Password.

Part of a Password Policy

Password storage scheme plug-ins typically do no more than encode passwords and compare input passwords with stored, encoded passwords. In other words, plug-ins represent only a part of a comprehensive password policy. Refer to the Sun Directory Server Enterprise Edition 7.0 Deployment Planning Guide for suggestions on designing secure directory services.

Writing a Password Storage Scheme Plug-In

This section demonstrates how to write a plug-in that encodes passwords. The plug-in also allows Directory Server to compare stored passwords with passwords provided by a client application.


Caution – Caution –

The examples in this chapter do not constitute a secure password storage scheme.


The source for the example plug-in referenced in this chapter is install-path/examples/testpwdstore.c. For encoding and comparing, the plug-in performs an exclusive or with 42 on each character of the password.

Encoding a Password

When Directory Server calls a password storage scheme plug-in encode function, it passes that function an input password char * and expects an encoded password char * in return. The prototype for the example encode function, xorenc(), is as follows:

static char * xorenc(char * pwd);

Allocate space for the encoded password with slapi_ch_malloc() rather than regular malloc(). Directory Server can then terminate with an “out of memory” message if allocation fails memory with slapi_ch_free().

By convention, you prefix the encoded password with the name of the password storage scheme, enclosed in braces, { and }. In other words, the example plug-in is called XOR.

The name is declared in the example:

static char * name = "XOR";   /* Storage scheme name */

You return encoded strings prefixed with {XOR}. You also register the name with Directory Server.


Example 11–1 Encoding a userPassword Value (testpwdstore.c)

#include "slapi-plugin.h"

static char * name           ="XOR";   /* Storage scheme name */

#define PREFIX_START '{'
#define PREFIX_END   '}'

static char *
xorenc(char * pwd)
{
    char * tmp    = NULL;              /* Used for encoding   */
    char * head   = NULL;              /* Encoded password    */
    char * cipher = NULL;              /* Prefix, then pwd    */
    int i, len;
  
    /* Allocate space to build the encoded password           */
    len = strlen(pwd);
    tmp = slapi_ch_malloc(len + 1);
    if (tmp == NULL) return NULL;

    memset(tmp, '\0', len + 1);
    head = tmp;

    /* Encode. This example is not secure by any means.       */
    for (i = 0; i < len; i++, pwd++, tmp++) *tmp = *pwd ^ 42;
  
    /* Add the prefix to the cipher                           */
    if (tmp != NULL) {
        cipher = slapi_ch_malloc(3 + strlen(name) + strlen(head));
        if (cipher != NULL) {
            sprintf(cipher,"%c%s%c%s",PREFIX_START,name,PREFIX_END,head);
        }
    }
    slapi_ch_free((void **) &head);
  
    return (cipher);                   /* Server frees cipher */
}

Notice that you free only memory allocated for temporary use. Directory Server frees memory for the char * returned, not the plug-in. For details on slapi_ch_malloc() and slapi_ch_free(), see Chapter 15, Function Reference, Part I.


Comparing a Password

When Directory Server calls a password storage scheme plug-in compare function, it passes that function an input password char * and a stored, encoded password char * from the directory. The compare function returns zero, 0, if the input password matches the password from the directory. The function returns 1 otherwise. The prototype for the example compare function, xorcmp(), is therefore as follows:

static int xorcmp(char * userpwd, char * dbpwd);

Here, userpwd is the input password. dbpwd is the password from the directory. The compare function must encode the input password to compare the result to the password from the directory.


Example 11–2 Comparing a userPassword Value (testpwdstore.c)

#include "slapi-plugin.h"

static int
xorcmp(char * userpwd, char * dbpwd)
{
    /* Check the correspondence of the two char by char       */       
    int i, len = strlen(userpwd);
    for (i = 0; i < len; i++) {
        if ((userpwd[i] ^ 42) != dbpwd[i])
            return 1;                  /* Different passwords */
    }
    return 0;                          /* Identical passwords */
}

Notice that Directory Server strips the prefix from the password before passing the value to the compare function. In other words, you need not account for {XOR} in this case.


Not all encoding algorithms have such a trivial compare function.

Registering the Password Storage Scheme Plug-In

You must register four password storage scheme specific items with Directory Server:

Notice that you provide no decoding function. In this case, Directory Server does not decode user passwords after they are stored.


Example 11–3 Registering a Password Storage Scheme Plug-In (testpwdstore.c)

#include "slapi-plugin.h"

static char * name           ="XOR";   /* Storage scheme name */

static Slapi_PluginDesc desc = {
    "xor-password-storage-scheme",     /* Plug-in identifier  */
    "Sun Microsystems, Inc.",          /* Vendor name         */
    "6.0",                             /* Revision number     */
    "Exclusive-or example (XOR)"       /* Plug-in description */
};

#ifdef _WIN32
__declspec(dllexport)
#endif
int
xor_init(Slapi_PBlock * pb)
{
    int rc = 0;                        /* 0 means success     */
    rc |= slapi_pblock_set(            /* Plug-in API version */
        pb,
        SLAPI_PLUGIN_VERSION,
        (void *) SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &desc
    );
    rc |= slapi_pblock_set(            /* Storage scheme name */
        pb,
        SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
        (void *) name
    );
    rc |= slapi_pblock_set(            /* Encode password     */
        pb,
        SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
        (void *) xorenc
    );
    rc |= slapi_pblock_set(            /* Compare password    */
        pb,
        SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
        (void *) xorcmp
    );
    rc |= slapi_pblock_set(            /* Never decode pwd    */
        pb,
        SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DEC_FN,
        NULL
    );
    return rc;
}

Setting Up the Password Storage Scheme Plug-In

Set up a directory instance ad build the plug-in if you have not done so already.

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.


     $ dsconf create-plugin -F custom-plugin-init-function -G custom-plugin-argument -H lib-path \
    -Y custom-plugin-type "Custom Plugin"
    $ dsconf enable-plugin "Custom Plugin"

    Hint For more information, use the commands specified in the plug-in source file.

  3. Restart Directory Server.


    $ dsadm restart instance-path
    

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/resources/ldif/Example.ldif.

  1. Create a new Directory Server instance.

    For example:


    $ dsadm create -h localhost -p 1389 /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 \
     install-path/resources/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 "install-path/resources/ldif/Example.ldif"
    ## Finished scanning file "install-path/resources/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.

Trying the Password Storage Scheme Example

This section demonstrates the example plug-in for this chapter.

Perform a Quick Test

Plug the XOR password storage scheme into Directory Server if you have not done so already.

Before you do anything else, quickly check that Directory Server calls the plug-in encode function as expected. To perform this quick test, use the pwdhash tool. The pwdhash tool has Directory Server encode a password, then display the result.


Example 11–4 Testing the Password Storage Scheme


$ pwdhash -D /local/ds -s XOR password
{XOR}ZKYY]EXN

Do not be concerned with the exact value of the resulting encoded password. The output should, however, start with {XOR}.


As Directory Server calls the encode function dynamically, you can fix the plug-in library. Then try pwdhash without doing anything to Directory Server. If this quick test does not work, fix the example.

ProcedureTo Encode a Password With the XOR Scheme

Here, you use the XOR scheme to encode a new password for Barbara Jensen.

  1. Change the password storage scheme for the suffix to XOR.


    $ dsconf set-server-prop -h localhost -p 1389 pwd-storage-scheme:XOR
  2. Change Barbara’s password to password.

  3. View Barbara’s newly encoded password.


    $ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=bjensen
    version: 1
    dn: uid=bjensen, ou=People, dc=example,dc=com
    cn: Barbara Jensen
    cn: Babs Jensen
    sn: Jensen
    givenName: Barbara
    objectClass: top
    objectClass: person
    objectClass: organizationalPerson
    objectClass: inetOrgPerson
    ou: Product Development
    ou: People
    l: Cupertino
    uid: bjensen
    mail: bjensen@example.com
    telephoneNumber: +1 408 555 1862
    facsimileTelephoneNumber: +1 408 555 1992
    roomNumber: 0209
    userPassword: {XOR}ZKYY]EXN

    Notice that Barbara’s password is XOR-encoded.

Compare an XOR-Encoded Password

Barbara has the right to search other entries under dc=example,dc=com. Here, you search for Kirsten Vaughan's entry as bjensen.


Example 11–5 Binding With the New Password


$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com
 -D uid=bjensen,ou=People,dc=example,dc=com -w password uid=kvaughan
version: 1
dn: uid=kvaughan, ou=People, dc=example,dc=com
cn: Kirsten Vaughan
sn: Vaughan
givenName: Kirsten
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
ou: Human Resources
ou: People
l: Sunnyvale
uid: kvaughan
mail: kvaughan@example.com
telephoneNumber: +1 408 555 5625
facsimileTelephoneNumber: +1 408 555 3372
roomNumber: 2871

You know that Directory Server uses a plug-in to check Barbara’s password during the bind. Thus, Directory Server must have used the XOR plug-in because you saw that Barbara’s password was XOR-encoded. If the whole process appears to work, you can conclude that the compare function works, too.