Sun Java logo     Previous      Contents      Index      Next     

Sun logo
Sun Java(TM) System Directory Server 5.2 2005Q1 Plug-In Developer's Guide 

Chapter 11
Writing Password Storage Scheme Plug-Ins

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

This chapter includes the following sections:


Calling Password Storage Scheme Plug-Ins

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

Two Types

Two types of password storage scheme plug-ins work with Directory Server, pwdstoragescheme and reverpwdstoragescheme. The former is one-way, in that once 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 latter is reversible, in that the plug-in allows Directory Server both to encode and to decode values. The reverpwdstoragescheme 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.


Pre-Installed 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. Refer to the Directory Server Administration Guide for instructions.

Affects Password Attribute Values

Password storage scheme plug-in functions act on password (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, and to compare incoming passwords to the encoded values. Which plug-in Directory Server invokes depends on the password storage scheme used for the entry in question.

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

Invoked 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, it 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, they represent only a part of a comprehensive password policy. Refer to the Directory Server 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 and allows Directory Server to compare stored passwords with passwords provided by a client application.


Caution

The example does not constitute a secure password storage scheme.


The source for the example plug-in referenced in this section is ServerRoot/plugins/slapd/slapi/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 our example encode function, xorenc(), is:

static char * xorenc(char * pwd);

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

By convention, we prefix the encoded password with the common name, of the password storage scheme, enclosed in braces, { and }. In other words, our example plug-in has cn: XOR.

Our name is declared in the example:

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

We return encoded strings prefixed with {XOR}, and register that name with Directory Server.

Code 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 we 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(), refer to the Directory Server Plug-in Developer's Reference .

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 0 if the input password matches the password from the directory. It returns 1 otherwise. The prototype for our example compare function, xorcmp(), is therefore:

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

Here, userpwd is the input password and 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.

Code 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 Directory Server strips the prefix from the password before passing it to the compare function. In other words, we need not account for {XOR} in this case.

Not all encoding algorithms have such a trivial compare function.

Registering the Plug-In

You must register four password storage scheme specific items with Directory Server: the storage scheme name (used for the prefix), the encode function, the compare function, and the decode function.

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

Code 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         */

    "5.2",                             /* 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;

}

Creating a Configuration Entry

Build the plug-in if you have not done so already.

The configuration entry for the plug-in resembles other entries under cn=config whose DN contains cn=Password Storage Schemes. Create an LDIF file similar to that of Code Example 11-4.

Code Example 11-4 Configuration Entry (testpwdstore.c

dn: cn=XOR,cn=Password Storage Schemes,cn=plugins,cn=config

objectclass: top

objectclass: nsSlapdPlugin

cn: XOR

nsslapd-pluginPath: <ServerRoot>/plugins/slapd/slapi/examples/<LibName>

nsslapd-pluginInitFunc: xor_init

nsslapd-pluginType: pwdstoragescheme

nsslapd-pluginEnabled: on

nsslapd-pluginId: xor-password-storage-scheme

nsslapd-pluginVersion: 5.2

nsslapd-pluginVendor: Sun Microsystems, Inc.

nsslapd-pluginDescription: Exclusive-or example (XOR)

Notice nsslapd-pluginEnabled must be set to on. Otherwise, no one can use the password storage scheme. Setting nsslapd-pluginEnabled to on does not suffice, however, for the plug-in to be used. In addition to enabling the plug-in, you must configure Directory Server to use the password storage scheme it provides.

Add the entry to the directory configuration. For example:

$ ldapmodify -a -p port -h host -D "cn=Directory Manager" -w password -f file.ldif

Here, file.ldif contains the plug-in configuration entry.

Restart Directory Server so it loads the plug-in at startup:

$ ServerRoot/slapd-serverID/restart-slapd

At this point, the XOR password storage scheme plug-in should be enabled for Directory Server, and visible in the server configuration.

Trying It Out

This section demonstrates the example plug-in for this chapter. Plug the XOR password storage scheme into Directory Server if you have not done so already.

Perform a Quick Test

Before we do anything involved, quickly check that Directory Server manages to call the plug-in encode function as expected. To perform this quick test, we use the pwdhash tool. The pwdhash tool can be used to have Directory Server encode a password, then display the result.

Code Example 11-5 Testing the Password Storage Scheme 

$ cd ServerRoot/bin/slapd/server

$ ./pwdhash -D ServerRoot/slapd-serverID -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, we can fix the plug-in library, then try pwdhash again without touching Directory Server. That is, if this quick test does not work for you, now is a good time to fix the example.

Add Example Users with Clear Text Passwords

In order to experiment with user passwords, we need some users with passwords. Here, we create a directory suffix, dc=example,dc=com, whose users we load from an LDIF file, ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif. The example user passwords appear in clear text in Example-Plugin.ldif.

Perform the following steps to load the example into the directory.

  1. Open the Directory Server Console.
  2. Use the console to create a new root suffix, dc=example,dc=com.
  3. Hint Configuration tab page. Select Data node. Object > New Root Suffix.

  4. Set the password storage scheme to No encryption (CLEAR).
  5. Hint Use the Password encryption drop-down list.

  6. Log in as cn=Directory Manager.
  7. Hint Console > Log in as New User...

  8. Import entries from ServerRoot/slapd-serverID/ldif/Example-Plugin.ldif.
  9. Hint Console > Import Databases...

Once the Example-Plugin.ldif entries have been imported, check that the user passwords appear in clear text. The console does not show passwords in clear text, so we use ldapsearch on the command line.

Code Example 11-6 Sample User Entry 

$ ldapsearch -L -p port -h host -D "cn=Directory Manager" -w password \

-b dc=example,dc=com uid=yyorgens

dn: uid=yyorgens,ou=People,dc=example,dc=com

mail: yyorgens@example.com

uid: yyorgens

secretary: uid=bcubbins,ou=People,dc=example,dc=com

givenName: Yolanda

objectClass: top

objectClass: person

objectClass: organizationalPerson

objectClass: inetOrgPerson

sn: Yorgenson

cn: Yolanda Yorgenson

userPassword: yyorgens

Notice that Yolanda Yorgensons's password is yyorgens.

Encode a Password with the XOR Scheme

Here we use the XOR scheme to encode a new password for Yolanda Yorgenson.

  1. As Directory Manager, set the password storage scheme for the suffix to Exclusive-or example (XOR).
  2. Hint Use the Password encryption drop-down list.

  3. Change Yolanda's password to foobar12.
  4. Hint Directory tab page. Select example/People. Double-click yyorgens.

  5. View Yolanda's newly encoded password.
  6. Code Example 11-7 User Entry after Password Change 

    $ ldapsearch -L -p port -h host -D "cn=Directory Manager" -w password \

    -b dc=example,dc=com uid=yyorgens

    dn: uid=yyorgens,ou=People,dc=example,dc=com

    mail: yyorgens@example.com

    uid: yyorgens

    secretary: uid=bcubbins,ou=People,dc=example,dc=com

    givenName: Yolanda

    objectClass: top

    objectClass: person

    objectClass: organizationalPerson

    objectClass: inetOrgPerson

    sn: Yorgenson

    cn: Yolanda Yorgenson

    userPassword: {XOR}ZKYY]EXN

Notice that Yolanda Yorgenson's password is XOR encoded.

Compare an XOR-Encoded Password

Yolanda has the right to search other entries under dc=example,dc=com. Here we search for Bartholomew Cubbins's entry as yyorgens.

Code Example 11-8 Binding with the New Password 

$ ldapsearch -L -p port -h host -b dc=example,dc=com \

-D "uid=yyorgens,ou=People,dc=example,dc=com" -w foobar12 \

uid=bcubbins

dn: uid=bcubbins,ou=People,dc=example,dc=com

mail: bcubbins@example.com

uid: bcubbins

facsimileTelephoneNumber: +1 234 567 8910

givenName: Bartholomew

objectClass: top

objectClass: person

objectClass: organizationalPerson

objectClass: inetOrgPerson

sn: Cubbins

cn: Bartholomew Cubbins

userPassword: bcubbins

We know Directory Server uses a plug-in to check Yolanda's password during the bind. In this case, Directory Server must have used the XOR plug-in, because we saw that Yolanda's password was XOR encoded. If the whole process appears to work, we can conclude that our compare function works, too.



Previous      Contents      Index      Next     


Copyright 2005 Sun Microsystems, Inc. All rights reserved.