Use OpenLDAP Clients In 389


Introduction

389 uses the Mozilla LDAP C SDK. The OpenLDAP API is similar, but there are a number of important differences. This page lists the differences and the plan for resolving them in order to use the OpenLDAP API with 389 directory server, admin server, adminutil, etc.

LDAP protocol

This includes APIs that deal with LDAP protocol requests and responses, such as ldap_search_*, ldap_result, etc. These are mostly the same. There are a few differences such as ldap_initialize, ldap_url_parse.

LDAP API

OpenLDAP supports the BER functions needed to construct and parse the control and extop values. All of the protocol API functions support request and response controls, and extended operation requests and responses. There are a few #defines which are different.

Renamed defines

Missing defines

OpenLDAP does not define these:

Missing functions

OpenLDAP has deprecated these:

Missing files

MozLDAP has ldap_ssl.h, this file does not exist and is not needed in OpenLDAP.

Sort API

The server side sort API is slightly different. MozLDAP declares this structure:

typedef struct LDAPsortkey {    /* structure for a sort-key */    
       char *  sk_attrtype;    
       char *  sk_matchruleoid;    
       int     sk_reverseorder;    
} LDAPsortkey;    

But OpenLDAP uses this instead:

typedef struct ldapsortkey {    
       char *  attributeType;    
       char *  orderingRule;    
       int     reverseOrder;    
} LDAPSortKey;    

We can just typedef LDAPSortkey LDAPsortkey if HAVE_OPENLDAP

URL parsing

The LDAPURLDesc is slightly different. OpenLDAP does not have a lud_options field or a LDAP_URL_OPT_SECURE flag. Instead, OpenLDAP has a lud_scheme field which will have a value of “ldap”, “ldaps”, or “ldapi”.

OpenLDAP does not return LDAP_URL_ERR_NODN if the DN is missing, it will return LDAP_URL_ERR_MEM. Unfortunately, LDAP_URL_ERR_MEM is also returned for other errors. It is up to the caller to determine if there was a problem with the DN by looking at the returned structure.

The slapi function

int slapi_ldap_url_parse(const char *url, LDAPURLDesc **ludpp, int require_dn, int *secure)    

is used to parse URLs. If require_dn is set, the url must have a DN or the parse function will return an error. If the secure flag is passed, the function will return secure == TRUE if the url is for a secure protocol (i.e. begins with ldaps:).

ldap_sasl_interactive_bind_ext_s()

OpenLDAP does not have ldap_sasl_interactive_bind_ext_s(), only ldap_sasl_interactive_bind_s(). The only difference is that ldap_sasl_interactive_bind_ext_s() has a LDAPControl ***rctrl parameter which is used to return the response controls from the server. The response controls are useful because without them there is no way to get information about password policy. We could just copy ldap_sasl_interactive_bind_s() from OpenLDAP into slapi and merge in the _ext changes to make it ldap_sasl_interactive_bind_ext_s(). We could attempt to get ldap_sasl_interactive_bind_ext_s() into OpenLDAP. I suspect some resistance due to the response controls parameter, which is sort of a hack, in that it returns the response controls from all of the intermediate ldap_sasl_bind responses.

ldap_get_lderrno()

OpenLDAP does not have this function. The way to get the LDAP error code, matched dn, and error message is to use ldap_parse_result().

ldap_create_proxyauth_control()

OpenLDAP does not have this function. We can copy it into slapi, and submit the function for inclusion in OpenLDAP.

ldap_str2charray()

Use str2charray() in charray.c - export this and str2charray_ext() to slapi as slapi_X.

ldap_create_filter()

referint.c - use PR_smprintf or similar - need to normalize origDN first?

ldap_init()

This is deprecated - have to use ldap_initialize() (converting host:port to an LDAP URL string) or ldap_create() (then set the host:port afterwards).

ldap_simple_bind_s()

This is deprecated - use ldap_sasl_bind_s() with LDAP_SASL_SIMPLE as the mech instead.

ldap_set_rebind_proc()

The callback functions take different parameters.

Mozilla LDAP:

typedef int (LDAP_CALL LDAP_CALLBACK LDAP_REBINDPROC_CALLBACK)( LDAP *ld,
        char **dnp, char **passwdp, int *authmethodp, int freeit, void *arg);

LDAP_API(void) LDAP_CALL ldap_set_rebind_proc( LDAP *ld,
        LDAP_REBINDPROC_CALLBACK *rebindproc, void *arg );

OpenLDAP:

typedef int (LDAP_REBIND_PROC) LDAP_P((
        LDAP *ld, LDAP_CONST char *url,
        ber_tag_t request, ber_int_t msgid,
        void *params ));

LDAP_F( int ) ldap_set_rebind_proc LDAP_P((
        LDAP *ld,
        LDAP_REBIND_PROC *rebind_proc,
        void *params ));

ldap_X deprecated in favor of ldap_X_ext

Most of the ldap_X[_s] functions have been deprecated e.g. ldap_unbind(), ldap_add_s(), etc. The _ext versions should be used instead e.g. ldap_unbind -> ldap_unbind_ext.

LDAP BER

OpenLDAP provides BER codecs such as ber_printf, ber_scanf, and many others. In general, OpenLDAP provides a superset of the functionality of MozLDAP, so there should be few difficulties here.

ber_len_t

MozLDAP defines this as an int, but OpenLDAP defines this as a long. We will have to check all places where this is used to make sure we do not have any 64-bit issues.

If you see this error, include lber.h before ldif.h:

/usr/local/include/ldif.h:56: error: expected declaration specifiers or '...' before 'ber_len_t'

ber_get_next_buffer_ext()

OpenLDAP does not provide this function - use ber_get_next() instead. The openldap_read_function will actually “read” from the buffer filled in by connection_read - the openldap_write_function will write to the PRFD. This is why the private data used by the sockbuf IO functions use the Connection* instead of just the PRFD.

LBER_OVERFLOW

If the max incoming BER length is greater than the max, MozLDAP returns LBER_OVERFLOW. OpenLDAP returns LBER_DEFAULT and sets sock_error(ERANGE). Unfortunately, ERANGE is used for other cases as well (e.g. tag specified but length == 0). So it will be a little harder to do max ber detection portably.

LDIF

MozLDAP provides a public API for parsing LDIF files. OpenLDAP has almost the same functions (ldif.c), but they are private to the library (liblutil). A patch has been submitted to expose the LDIF functionality to the public (http://www.openldap.org/its/index.cgi/Incoming?id=6194). There are still some differences. The biggest one is that OpenLDAP has no way to generate LDIF files that are not wrapped. OpenLDAP assumes that since the LDIF is defined as a wrapped format, everyone who uses LDIF must wrap/unwrap. There are a number of places where MozLDAP (ldapsearch -T) or 389 (db2ldif -U) generate unwrapped LDIF. ldif_sput() has a type parameter that takes a bitfield - we could add a LDIF_PUT_NOWRAP option which could be OR’d with the type parameter, and change ldif_sput() to allow unwrapped LDIF output. The initial code in SLAPI will implement the nowrap option as a wrapper around the OpenLDAP function, and will unwrap the LDIF output by ldif_sput(). Since the string length will decrease with unwrapping, the function can just do the unwrap in place. slapi_ldif_put_type_and_value_with_options() is the new wrapper function.

These are different

OpenLDAP does not define these:

UTF-8

MozLDAP provides a public API for doing string manipulation of UTF-8 encoded strings (ldap_utf8len(), ldap_utf8strtok_r(), ldap_utf8isalnum(), etc.) OpenLDAP has most of these same functions, but the names are slightly different (e.g. ldap_utf8_next()), and the interface is private to the library (utf-8.c).

For the directory server, we have two options

The fastest would be the first option - it would be trivial to make these available via SLAPI. I don’t know if there is any motivation in the OpenLDAP community to expose these APIs. A new file - utf8.c - has been added to SLAPI. This is only compiled when using OpenLDAP.

Outside of the server, the only places these are used are adminutil and dsgw. Since both of these also use ICU, and ICU has UTF-8 string functions, one option would be to just use ICU:

The ldap_utf8isX functions take a char *, but the u_isX functions take a UChar32. In most cases these functions are used when iterating through a string (e.g. using isspace to trim leading and trailing spaces). These cases will work well when using U8_NEXT and U8_PREV, since those also return the UTF-8 char as a UChar32.

There are several macros which just wrap the corresponding function. We could just define these in the adminutil and dsgw header files.

Crypto

TLS/SSL

OpenLDAP CVS HEAD (2.4.17) has support for MozNSS crypto. This works for apps that use the library as well as standalone apps like ldapsearch, etc. and OpenLDAP in server mode. There are some caveats:

Last modified on 7 August 2014