This chapter explains how to write a plug-in that adds to, bypasses, or replaces the authentication mechanisms that Directory Server supports. The chapter demonstrates the use of existing mechanisms for authentication. You must adapt the code examples to meet your particular authentication requirements.
The examples alone do not provide secure authentication methods.
This chapter covers the following topics:
This section identifies which authentication methods are available. This section also describes how Directory Server handles authentication to identify clients. Consider the Directory Server model described in this section when writing plug-ins to modify the mechanism.
Directory Server supports the two authentication methods described in RFC 4511. One method is simple authentication, which is rendered more secure through the use of Secure Socket Layer (SSL) for transport. The other method is SASL, whose technology is further described in RFC 2222, Simple Authentication and Security Layer (SASL). Through SASL, Directory Server supports Kerberos authentication to the LDAP server and the Directory System Agent (DSA, an X.500 term) as described in RFC 1777, Lightweight Directory Access Protocol.
For LDAP clients, Directory Server keeps track of client identity through the DN the client used to connect. The server also keeps track through the authentication method and external credentials that the client uses to connect. The parameter block holds the relevant client connection information. The DN can be accessed through the SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE parameters to slapi_pblock_set() and slapi_pblock_get().
For DSML clients that connect over HTTP, Directory Server performs identity mapping for the bind. As a result, plug-ins have the same view of the client bind, regardless of the front—end protocol.
Before Directory Server calls a preoperation bind plug-in, Directory Server completes authentication for anonymous binds, binds by the Directory Manager, and binds by replication users before calling preoperation bind functions. Thus, the server completes the bind without calling the plug-in.
For SASL authentication mechanisms, preoperation and postoperation bind functions can be called several times during processing of a single authentication request.
In fact, multiple LDAP bind operations can be used to implement the authentication mechanism, as is the case for DIGEST-MD5, for example.
To process the bind, Directory Server, does the following:
Parses the bind request
Determines the authentication method
Determines whether the bind DN is handled locally
Adds request information to the parameter block
Determines whether to handle the bind in the front end or to call preoperation bind plug-in functions
Performs the bind or not, using information about the bind DN entry from the server back end
Following is a description of each action:
Parsing the bind request. When a bind request arrives, Directory Server retrieves the DN, the authentication method used, and any credentials sent, such as a password. The processing can involve a mapping if the client accesses the server by using DSML over HTTP. If the request calls for SASL authentication, LDAP_AUTH_SASL, Directory Server retrieves the SASL mechanism. Next, Directory Server normalizes the DN that is retrieved from the request. The server also retrieves any LDAP v3 controls that are included in the request.
Determining the authentication method. In some cases, the method is simple authentication, but the DN or credentials are empty or missing. Directory Server then assumes that the client is binding anonymously, sets SLAPI_CONN_DN to NULL and SLAPI_CONN_AUTHTYPE to SLAPD_AUTH_NONE, and sends an LDAP_SUCCESS result to the client. If the method is SASL authentication, Directory Server first determines whether the mechanism is supported. If not, the server sends an LDAP_AUTH_METHOD_NOT_SUPPORTED result to the client.
Determining whether the DN is handled locally. If the DN is not stored in the local instance, but Directory Server refers clients by default, the server sends the client an LDAP_REFERRAL result. Otherwise, the server sends an LDAP_NO_SUCH_OBJECT result to the client.
Adding request information to the parameter block.Directory Server puts bind request information into the parameter block:
SLAPI_BIND_TARGET to the DN retrieved from the request
SLAPI_BIND_METHOD to the authentication method requested
SLAPI_BIND_CREDENTIALS to the credentials retrieved from the request
SLAPI_BIND_MECHANISM to the name of the SASL mechanism, if applicable
Determining whether to handle the Bind or call a plug-in.Directory Server authenticates bind requests from the directory superuser or the replication manager, sending an LDAP_SUCCESS result on success or an LDAP_INVALID_CREDENTIALS result on failure.
After completing all this work, the server calls preoperation bind functions.
Directory Server does not call preoperation bind functions for bind requests from the directory manager, the replication manager, nor for anonymous binds.
Performing the back-end bind.When preoperation bind functions return 0, Directory Server completes the bind operation. The server checks the bind against the information in the directory database. The server then sets SLAPI_CONN_DN and SLAPI_CONN_AUTHTYPE appropriately. If necessary, Directory Server sends password-related result controls to the client. The server always sends an LDAP_SUCCESS result on success or a non zero result on failure.
A preoperation bind function can modify Directory Server authentication in one of two ways. The plug-in either completely bypasses the comparison of incoming authentication information to authentication information stored in the directory database or implements a custom SASL mechanism.
Some plug-ins bypass the comparison of authentication information in the client request to authentication information in the directory. Such plug-ins return nonzero values. A value of 1 prevents the server from completing the bind after the preoperation function returns. Use this approach when you store all authentication information outside the directory, without mapping authentication identities through LDAP or the plug-in API. In addition to the other validation of the plug-in, you must verify that the plug-in works well with server access control mechanisms.
Refer to Developing a Simple Authentication Plug-In for an example.
If the plug-in implements a custom SASL mechanism, clients that use that mechanism must support it as well.
Refer to Developing a SASL Authentication Plug-In for a plug-in example.
This section shows how a preoperation bind plug-in can use the plug-in API to authenticate a user. The example used in this section obtains the appropriate bind information from the parameter block. The example then handles the authentication if the request is for LDAP_AUTH_SIMPLE, but allows the server to continue the bind if the authentication succeeds.
Notice that some binds are performed by the front end before preoperation bind functions are called.
The following example shows a code excerpt from the source file install-path/examples/testbind.c.
#include "slapi-plugin.h" int test_bind(Slapi_PBlock * pb) { char * dn; /* Target DN */ int method; /* Authentication method */ struct berval * credentials; /* Client SASL credentials */ Slapi_DN * sdn = NULL; /* DN used in internal srch */ char * attrs[2] = { /* Look at userPassword only */ SLAPI_USERPWD_ATTR, NULL }; Slapi_Entry * entry = NULL; /* Entry returned by srch */ Slapi_Attr * attr = NULL; /* Pwd attr in entry found */ int is_repl; /* Is this replication? */ int is_intl; /* Is this an internal op? */ int connId, opId, rc = 0; long msgId; /* Obtain the bind information from the parameter block. */ rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method); rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &credentials); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId); rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId); rc |= slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl); rc |= slapi_pblock_get(pb, SLAPI_IS_INTERNAL_OPERATION, &is_intl); if (rc != 0) { 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, "test_bind in test-bind plug-in", "Could not get parameters for bind operation (error %d).\n", rc ); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return (LDAP_OPERATIONS_ERROR);/* Server aborts bind here. */ } /* The following code handles simple authentication, where a * user offers a bind DN and a password for authentication. * * Handling simple authentication is a matter of finding the * entry corresponding to the bind DN sent in the request, * then if the entry is found, checking whether the password * sent in the request matches a value found on the * userPassword attribute of the entry. */ /* Avoid interfering with replication or internal operations. */ if (!is_repl && !is_intl) switch (method) { case LDAP_AUTH_SIMPLE: /* Find the entry specified by the bind DN... */ sdn = slapi_sdn_new_dn_byref(dn); rc |= slapi_search_internal_get_entry( sdn, attrs, &entry, plugin_id ); slapi_sdn_free(&sdn); if (rc != 0 || entry == NULL) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Could not find entry: %s\n", dn ); rc = LDAP_NO_SUCH_OBJECT; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); return (rc); } else { /* ...check credentials against the userpassword... */ Slapi_Value * credval; /* Value of credentials */ Slapi_ValueSet * pwvalues; /* Password attribute values */ rc |= slapi_entry_attr_find( entry, SLAPI_USERPWD_ATTR, &attr ); if (attr == NULL) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Entry %s has no userpassword.\n", dn ); rc = LDAP_INAPPROPRIATE_AUTH; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); return (rc); } rc |= slapi_attr_get_valueset( attr, &pwvalues ); if (rc != 0 || slapi_valueset_count(pwvalues) == 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Entry %s has no %s attribute values.\n", dn, SLAPI_USERPWD_ATTR ); rc = LDAP_INAPPROPRIATE_AUTH; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); slapi_valueset_free(pwvalues); return (rc); } credval = slapi_value_new_berval(credentials); rc = slapi_pw_find_valueset(pwvalues, credval); slapi_value_free(&credval); slapi_valueset_free(pwvalues); if (rc != 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Credentials are not correct.\n" ); rc = LDAP_INVALID_CREDENTIALS; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); return (rc); } } /* ...if successful, set authentication for the connection. */ if (rc != 0) return (rc); rc |=slapi_pblock_set(pb, SLAPI_CONN_DN, slapi_ch_strdup(dn)); rc |=slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE); if (rc != 0) { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Failed to set connection info.\n" ); rc = LDAP_OPERATIONS_ERROR; slapi_send_ldap_result(pb, rc, NULL, NULL, 0, NULL); return (rc); } else { slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Authenticated: %s\n", dn ); /* Now that authentication succeeded, the plug-in * returns a value greater than 0, even though the * authentication has been successful. A return * code > 0 tells the server not to continue * processing the bind. A return code of 0, such * as LDAP_SUCCESS tells the server to continue * processing the operation. */ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); rc = 1; } break; /* This plug-in supports only simple authentication. */ case LDAP_AUTH_NONE: /* Anonymous binds are handled by the front-end before * pre-bind plug-in functions are called, so this * part of the code should never be reached. */ case LDAP_AUTH_SASL: default: slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "test_bind in test-bind plug-in", "Plug-in does not handle auth. method: %d\n", method ); rc = 0; /* Let server handle bind. */ break; } return (rc); /* Server stops processing * the bind if rc > 0. */ }
The bulk of the code processes the LDAP_AUTH_SIMPLE case. In the simple authentication case, the plug-in uses the DN and the password to authenticate the user binding through plug-in API calls.
The plug-in demonstration works by turning on informational logging for plug-ins. You read the log messages written by the plug-in at different stages in its operation. Before using the plug-in, load a few example users and data because you cannot demonstrate the functionality while binding as a directory superuser. without calling the preoperation bind functions.
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.
Create a new Directory Server instance.
For example:
$ dsadm create /local/ds Choose the Directory Manager password: Confirm the Directory Manager password: $ |
Start the new Directory Server instance.
For example:
$ dsadm start /local/ds Server started: pid=4705 $ |
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 $ |
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). $ |
You can use Directory Service Control Center to perform this task. For more information, see the Directory Service Control Center online help.
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.
Build the plug-in.
Hint Use install-path/examples/Makefile or install-path/examples/Makefile64.
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.
Restart Directory Server.
$ dsadm restart instance-path |
The example suffix contains a number of people. If you look up the entry for one of those people, Barbara Jensen, either anonymously or as Directory Manager, the test_bind() plug-in function is never called. The plug-in therefore never logs informational messages to the errors log.
Run a search that bypasses the plug-in.
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com uid=bjensen sn version: 1 dn: uid=bjensen, ou=People, dc=example,dc=com sn: Jensen $ grep test_bind /local/ds/logs/errors $ |
Notice that the server bypasses preoperation bind plug-ins when special users request a bind.
Check what happens in the errors log when you bind as Barbara Jensen.
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \ -D uid=bjensen,ou=people,dc=example,dc=com -w hifalutin uid=bjensen sn version: 1 dn: uid=bjensen, ou=People, dc=example,dc=com sn: Jensen $ grep test_bind /local/ds/logs/errors [04/Jan/2006:11:34:31 +0100] - INFORMATION - test_bind in test-bind plug-in - conn=4 op=0 msgId=1 - Authenticated: uid=bjensen,ou=people,dc=example,dc=com $ |
See what happens when you bind as Barbara Jensen, but get the password wrong.
$ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \ -D uid=bjensen,ou=people,dc=example,dc=com -w bogus uid=bjensen sn ldap_simple_bind: Invalid credentials $ grep test_bind /local/ds/logs/errors | grep -i credentials [04/Jan/2006:11:36:07 +0100] - INFORMATION - test_bind in test-bind plug-in - conn=5 op=0 msgId=1 - Credentials are not correct. $ |
Here, the LDAP result is interpreted correctly by the command-line client. The plug-in message to the same effect is written to the errors log.
Delete Barbara's password, then try again.
$ ldapmodify -h localhost -p 1389 \ -D uid=kvaughan,ou=people,dc=example,dc=com -w bribery dn: uid=bjensen,ou=people,dc=example,dc=com changetype: modify delete: userpassword modifying entry uid=bjensen,ou=people,dc=example,dc=com ^D $ ldapsearch -h localhost -p 1389 -b dc=example,dc=com \ -D uid=bjensen,ou=people,dc=example,dc=com -w - uid=bjensen sn Enter bind password: ldap_simple_bind: Inappropriate authentication $ grep test_bind /local/ds/logs/errors | grep -i password [04/Jan/2006:11:41:25 +0100] - INFORMATION - test_bind in test-bind plug-in - conn=8 op=0 msgId=1 - Entry uid=bjensen,ou=people,dc=example,dc=com has no userpassword. $ |
Here, the LDAP result is displayed correctly by the command-line client. The plug-in message will provide more information about what went wrong during Barbara’s attempt to bind, no userpassword attribute values.
This section shows how a pre-bind operation plug-in can implement a custom Simple Authentication and Security Layer (SASL) mechanism. This mechanism enables mutual authentication without affecting existing authentication mechanisms.
The example plug-in responds to SASL bind requests with a mechanism, my_sasl_mechanism. Binds with this mechanism always succeed. The example is intended only to illustrate how a SASL authentication plug-in works, not to provide a secure mechanism for use in production.
This overall SASL section covers the examples install-path/examples/testsaslbind.c and install-path/examples/clients/saslclient.c.
Before using the plug-in function as described here, set up an example suffix. The setup is described in Seeing the Plug-In Work. Register the plug-in as described in the comments at the beginning of the sample file.
Register the SASL mechanism by using the same function that registers the plug-in, slapi_register_supported_saslmechanism(). The following example shows the function that registers both the plug-in and the SASL mechanism.
#include "slapi-plugin.h" Slapi_PluginDesc saslpdesc = { "test-saslbind", /* plug-in identifier */ "Sun Microsystems, Inc.", /* vendor name */ "6.0", /* plug-in revision number */ "Sample SASL pre-bind plug-in" /* plug-in description */ }; #define TEST_MECHANISM "my_sasl_mechanism" #define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM /* Register the plug-in with the server. */ #ifdef _WIN32 __declspec(dllexport) #endif int testsasl_init(Slapi_PBlock * pb) { int rc = 0; /* 0 means success */ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_CURRENT_VERSION ); rc |= slapi_pblock_set( /* Plug-in description */ pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &saslpdesc ); rc |= slapi_pblock_set( /* Pre-op bind SASL function */ pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *) testsasl_bind ); /* Register the SASL mechanism. */ slapi_register_supported_saslmechanism(TEST_MECHANISM); return (rc); }
Refer to Part II, Directory Server Plug-In API Reference for details on using slapi_register_supported_saslmechanism().
The plug-in function testsasl_bind() first obtains the client DN, method, SASL mechanism, and credentials. If the client does not ask to use the TEST_MECHANISM, the function returns 0 so the server can process the bind. The following example shows how the processing is done.
#include "slapi-plugin.h" #define TEST_MECHANISM "my_sasl_mechanism" #define TEST_AUTHMETHOD SLAPD_AUTH_SASL TEST_MECHANISM int testsasl_bind(Slapi_PBlock * pb) { char * dn; /* Target DN */ int method; /* Authentication method */ char * mechanism; /* SASL mechanism */ struct berval * credentials; /* SASL client credentials */ struct berval svrcreds; /* SASL server credentials */ int connId, opId, rc = 0; long msgId; rc |= slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn); rc |= slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method); rc |= slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &credentials); rc |= slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mechanism); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_MSGID, &msgId); rc |= slapi_pblock_get(pb, SLAPI_CONN_ID, &connId); rc |= slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opId); if (rc == 0) { if (mechanism == NULL || strcmp(mechanism, TEST_MECHANISM) != 0) return(rc); /* Client binding another way. */ } else { /* slapi_pblock_get() failed! */ return(rc); } /* Using the SASL mechanism that always succeeds, set conn. info. */ rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, slapi_ch_strdup(dn)); rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, TEST_AUTHMETHOD); if (rc != 0) { /* Failed to set conn. info! */ slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testsasl_bind in test-saslbind plug-in", "slapi_pblock_set() for connection information failed.\n" ); slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL); return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs. */ } /* Set server credentials. */ svrcreds.bv_val = "my credentials"; svrcreds.bv_len = sizeof("my credentials") - 1; rc |= slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &svrcreds); if (rc != 0) { /* Failed to set credentials! */ slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testsasl_bind in test-saslbind plug-in", "slapi_pblock_set() for server credentials failed.\n" ); rc |= slapi_pblock_set(pb, SLAPI_CONN_DN, NULL); rc |= slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE); return(LDAP_OPERATIONS_ERROR); /* Server tries other mechs. */ } /* Send credentials to client. */ slapi_log_info_ex( SLAPI_LOG_INFO_AREA_PLUGIN, SLAPI_LOG_INFO_LEVEL_DEFAULT, msgId, connId, opId, "testsasl_bind in test-saslbind plug-in", "Authenticated: %s\n", dn ); slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL); return 1; /* Server stops processing the * bind if the plug-in returns * a value greater than 0. */ }
The preceding example sets the DN for the client connection and the authentication method, as Directory Server does for a bind. To set SLAPI_CONN_DN and SLAPI_CONN_AUTHMETHOD is the key step of the bind. Without these values in the parameter block, Directory Server handles the client request as if the bind were anonymous. Thus, the client is left with access rights that correspond to an anonymous bind. Next, according to the SASL model, Directory Server sends credentials to the client as shown.
To test the plug-in, you need a client that uses my_sasl_mechanism to bind. The example discussed here uses this mechanism and the client code, saslclient.c, which is delivered with Directory Server.
The client authenticates by using the example SASL mechanism for a synchronous bind to Directory Server. The following example shows how authentication proceeds on the client side for Ted Morris.
#include "ldap.h" /* Use the fake SASL mechanism. */ #define MECH "my_sasl_mechanism" /* Global variables for client connection information. * You may set these here or on the command line. */ static char * host = "localhost"; /* Server hostname */ static int port = 389; /* Server port */ /* Load <install-path>/ldif/Example.ldif * before trying the plug-in with this default user. */ static char * user = "uid=tmorris,ou=people,dc=example,dc=com"; /* New value for userPassword */ static char * npwd = "23skidoo"; /* Check for host, port, user and new password as arguments. */ int get_user_args(int argc, char ** argv); int main(int argc, char ** argv) { LDAP * ld; /* Handle to LDAP connection */ LDAPMod modPW, * mods[2]; /* For modifying the password */ char * vals[2]; /* Value of modified password */ struct berval cred; /* Client bind credentials */ struct berval * srvCred; /* Server bind credentials */ int ldapVersion; /* Use default hostname, server port, user, and new password * unless they are provided as arguments on the command line. */ if (get_user_args(argc, argv) != 0) return 1; /* Usage error */ /* Get a handle to an LDAP connection. */ printf("Getting the handle to the LDAP connection...\n"); if ((ld = ldap_init(host, port)) == NULL) { perror("ldap_init"); return 1; } /* By default, the LDAP version is set to 2. */ printf("Setting the version to LDAP v3...\n"); ldapVersion = LDAP_VERSION3; ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion); /* Authenticate using the example SASL mechanism. */ printf("Bind DN is %s...\n", user); printf("Binding to the server using %s...\n", MECH); cred.bv_val = "magic"; cred.bv_len = sizeof("magic") - 1; if (ldap_sasl_bind_s(ld, user, MECH, &cred, NULL, NULL, &srvCred)) { ldap_perror(ld, "ldap_sasl_bind_s"); return 1; } /* Display the credentials returned by the server. */ printf("Server credentials: %s\n", srvCred->bv_val); /* Modify the user's password. */ printf("Modifying the password...\n"); modPW.mod_op = LDAP_MOD_REPLACE; modPW.mod_type = "userpassword"; vals[0] = npwd; vals[1] = NULL; modPW.mod_values = vals; mods[0] = &modPW; mods[1] = NULL; if (ldap_modify_ext_s(ld, user, mods, NULL, NULL)) { ldap_perror(ld, "ldap_modify_ext_s"); return 1; } /* Finish up. */ ldap_unbind(ld); printf("Modification was successful.\n"); return 0; }
The client changes Ted's password by binding to Directory Server, then requesting a modification to the userPassword attribute value.
After activating the plug-in in the server, compile the client code, saslclient.c. Next, run the client to perform the bind and the password modification.
$ ./saslclient Using the following connection info: host: localhost port: 389 bind DN: uid=tmorris,ou=people,dc=example,dc=com new pwd: 23skidoo Getting the handle to the LDAP connection... Setting the version to LDAP v3... Bind DN is uid=tmorris,ou=people,dc=example,dc=com... Binding to the server using my_sasl_mechanism... Server credentials: my credentials Modifying the password... Modification was successful. $ |
On the Directory Server side, the message showing that the plug-in has authenticated the client is in the errors log.
$ grep tmorris /local/ds/logs/errors | grep -i sasl [04/Jan/2006:12:05:30 +0100] - INFORMATION - testsasl_bind in test-saslbind plug-in - conn=12 op=0 msgId=1 - Authenticated: uid=tmorris,ou=people,dc=example,dc=com |