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

Chapter 9 Writing Entry Store and Entry Fetch Plug-Ins

This chapter covers how to write plug-ins that modify how Directory Server operates when writing entries to and reading entries from the directory database.


Note –

You can use attribute encryption instead of writing an entry store and entry fetch plug-in. See Encrypting Attribute Values in Sun Java System Directory Server Enterprise Edition 6.1 Administration Guide.


This chapter covers the following topics:

Calling Entry Store and Entry Fetch Plug-Ins

This section describes when entry store and entry fetch plug-ins are called. This section also describes how entries are expected to be handled by the plug-in.

The server calls entry store plug-in functions before writing data to a directory database. It calls entry fetch plug-in functions after reading data from the directory database. Entry store and entry fetch plug-ins may therefore typically be used to encrypt and decrypt directory entries, or to perform auditing work.

Using LDIF String Parameters

Unlike other types of plug-ins, entry store and entry fetch plug-in functions do not take a parameter block as an argument. Instead, the functions take two parameters, the LDIF string representation of an entry and the length of the string. Directory Server uses the modified LDIF string after the plug-in returns successfully. An example prototype for an entry store plug-in function is as follows:

int my_entrystore_fn(char ** entry, unsigned int * length);

Plug-in functions can manipulate the string as necessary. Entry store and entry fetch plug-ins return zero, 0, on success. When the function returns, Directory Server expects entry and length to contain the modified versions of the parameters.

Locating the Entry Store and Entry Fetch Examples

The plug-in function examples in this chapter can be found in install-path/examples/testentry.c.

The following example shows the entry store scrambling function used in this chapter. This function is called by Directory Server before writing an entry to the database.


Example 9–1 Entry Fetch Scrambler (testentry.c)

#include "slapi-plugin.h"

#ifdef _WIN32
typedef unsigned int uint;
__declspec(dllexport)
#endif
int
testentry_scramble(char ** entry, uint * len)
{
    uint i;

    /* Scramble using bitwise exclusive-or on each character.     */
    for (i = 0; i < *len - 1; i++) { (*entry)[i] ^= 0xaa; }
    
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "testentry_scramble in test-entry plug-in",
        "Entry data scrambled.\n"
    );

    return 0;
}

The following example shows the entry fetch unscrambling function used in this chapter. The function is called by the server after reading an entry from the database.


Example 9–2 Entry Fetch UnScrambler (testentry.c)

#include "slapi-plugin.h"

#ifdef _WIN32
typedef unsigned int uint;
__declspec(dllexport)
#endif
int
testentry_unscramble(char ** entry, uint * len)
{
    uint i;

    /* Return now if the entry is not scrambled.                  */
    if (!strncmp(*entry, "dn:", 3)) { return 0; }
    
    /* Unscramble using bitwise exclusive-or on each character.   */
    for (i = 0; i < *len - 1; i++) { (*entry)[i] ^= 0xaa; }
    
    slapi_log_info_ex(
        SLAPI_LOG_INFO_AREA_PLUGIN,
        SLAPI_LOG_INFO_LEVEL_DEFAULT,
        SLAPI_LOG_NO_MSGID,
        SLAPI_LOG_NO_CONNID,
        SLAPI_LOG_NO_OPID,
        "testentry_unscramble in test-entry plug-in",
        "Entry data unscrambled.\n"
    );

    return 0;
}

Notice the symmetry between the two functions. The scrambling mask, 0xaa or 10101010 in binary, makes the transformation simple to understand but not secure. A secure encryption mechanism can be significantly more complicated.

Writing a Plug-In to Encrypt Entries

This section shows how to register entry stored entry fetch plug-ins. It demonstrates how to write a plug-in that scrambles directory entries that are written to the directory database. It also demonstrates how to unscramble directory entries that are read from the directory database.


Caution – Caution –

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


The following example demonstrates how entry store and entry fetch plug-ins are registered with Directory Server.

Registering Entry Store and Entry Fetch Plug-Ins

The following example demonstrates how entry store and entry fetch plug-ins are registered with Directory Server.


Example 9–3 Registering Entry Store and Entry Fetch Plug-Ins (testentry.c)

#include "slapi-plugin.h"

Slapi_PluginDesc entrypdesc = {
    "test-entry",                      /* plug-in identifier      */
    "Sun Microsystems, Inc.",          /* vendor name             */
    "6.0",                             /* plug-in revision number */
    "Sample entry store/fetch plug-in" /* plug-in description     */
};

int
testentry_init(Slapi_PBlock *pb)
{
    int rc = 0;                        /* 0 means success         */
    rc |= slapi_pblock_set(            /* Plug-in API version     */
        pb,
        SLAPI_PLUGIN_VERSION,
        SLAPI_PLUGIN_CURRENT_VERSION
    );
    rc |= slapi_pblock_set(            /* Plug-in description     */
        pb,
        SLAPI_PLUGIN_DESCRIPTION,
        (void *) &entrypdesc    );
    rc |= slapi_pblock_set(            /* Entry store function    */
        pb,
        SLAPI_PLUGIN_ENTRY_STORE_FUNC,  
        (void *) testentry_scramble
    );
    rc |= slapi_pblock_set(            /* Entry fetch function    */
        pb,
        SLAPI_PLUGIN_ENTRY_FETCH_FUNC,  
        (void *) testentry_unscramble
    );
    return rc;
}

Trying the Entry Store and Entry Fetch Examples

You demonstrate the plug-in by showing the difference between scrambled and unscrambled data in the database. You therefore do not enable the plug-in immediately. Instead, you add an entry to a new directory suffix before scrambling. You then observe the results in the database on disk. Next, you remove the entry and enable the scrambling plug-in. Then you add the same entry again. Finally, you observe the results after the entry has been scrambled.

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.

Looking for Strings in the Database Before Scrambling

Here, you add an entry for Quentin Cubbins to the example suffix before registering the entry store and fetch plug-in with Directory Server. You see that Quentin’s mail address is visible in the database that holds mail address attribute values. Quentin’s entry, quentin.ldif, appears as shown in the following example.


Example 9–4 LDIF Representation of an Entry

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: qcubbins@example.com
userPassword: qcubbins
secretary: uid=bjensen,ou=People,dc=example,dc=com

Add Quentin’s entry to the directory. For example, if the entry is in quentin.ldif, add the following:


$ ldapmodify -a -h localhost -p 1389 -f quentin.ldif \
 -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery

Now look for strings in the directory database file for the mail attribute values.


Example 9–5 Attribute Values in a Database File Before Scrambling


$ cd instance-path/db/example/
$ strings example_mail.db3 | grep example.com
=qcubbins@example.com
=agodiva@example.com
=hfuddnud@example.com
=pblinn@example.com
=scooper@example.com
=bcubbins@example.com
=yyorgens@example.com

Notice that Quentin’s mail address is clearly visible if a user gains access to the database files. If the value was a credit card number, security would have been an issue.

Looking for Strings in the Database After Scrambling

Here, you add an entry for Quentin Cubbins to the example suffix after registering the entry store and fetch plug-in with Directory Server. You see that Quentin’s mail address is no longer visible in the database that holds mail address attribute values.

Before loading the plug-in, delete Quentin’s entry:


$ ldapdelete -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery
 uid=qcubbins,ou=People,dc=example,dc=com

Next, configure Directory Server to load the plug-in as shown in the comments at the beginning of testentry.c, and then restart the server.

With the entry store-fetch plug-in active, add Quentin’s entry back into the directory:


$ ldapmodify -a -h localhost -p 1389 -f quentin.ldif \
-D uid=kvaughan,ou=people,dc=example,dc=com -w bribery

Now search again for strings in the directory database file for the mail attribute values.


Example 9–6 Attribute Values in a Database File After Scrambling


$ cd instance-path/db/example/
$ strings example_mail.db3 | grep example.com
=agodiva@example.com
=hfuddnud@example.com
=pblinn@example.com
=scooper@example.com
=bcubbins@example.com
=yyorgens@example.com

Notice that Quentin’s mail address value is now not visible in the directory database. Directory users who have appropriate access rights, anonymous in this simple example case, can still view the attribute during a search. The attribute and its value are emphasized in the following example.


Example 9–7 Unscrambled Search Results


$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=qcubbins
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: qcubbins@example.com
secretary: uid=bcubbins,ou=People,dc=example,dc=com

In this way, you see that entry store and entry fetch plug-ins affect only the way entries are stored, not the directory front end.