RFC 1823

The LDAP Application Program Interface

πŸ“… published August 1995, from ietf

The LDAP Application Program Interface πŸ’–

Okay bestie, let's get into RFC 1823 πŸŽ€ It's by T. Howes and M. Smith from the University of Michigan, dated August 1995. It's filed as Informational, which means it's sharing info with the Internet community, not an official standard 🌸 The header doesn't list anything it obsoletes or updates, so it's a fresh start ✨


Status of This Memo πŸ“œ

This memo gives information to the Internet community 🌈 It does not specify an Internet standard of any kind, and distribution of it is unlimited πŸ’…

1. Introduction ✨

Okay so this document defines a C language application program interface (API) for the lightweight directory access protocol (LDAP) πŸ¦‹ The LDAP API is designed to be powerful but still simple to use, and it gives you compatible synchronous and asynchronous interfaces so it can suit a wide variety of apps πŸ’œ

It gives a quick overview of the LDAP model first, then explains how an application uses the API to get LDAP info. After that come the detailed API calls, and an appendix with example code to show it all in action 🧚

2. Overview of the LDAP Model 🌸

LDAP is the lightweight directory access protocol, described in [2] and [7] πŸ“– It can be a lightweight frontend to the X.500 directory [1], or it can run as a stand-alone service. Either way, it's based on a client-server model where a client makes a TCP connection to an LDAP server, then sends requests and receives responses over it πŸ”₯

The LDAP information model is built around the entry, which holds info about some object (like a person) πŸŽ€ Entries are made of attributes, and each attribute has a type and one or more values. Every attribute also has a syntax that decides what kinds of values are allowed (ASCII characters, a jpeg photograph, etc.) and how those values behave during directory operations (like whether case matters during comparisons) πŸ’…

Entries are organized in a tree, usually based on political, geographical, and organizational boundaries 🌳 Each entry is uniquely named relative to its siblings by its relative distinguished name (RDN), which is made of one or more distinguished attribute values from the entry. At most one value from each attribute MAY be used in the RDN πŸ™ˆ For example, Babs Jensen's entry might be named with the "Barbara Jensen" value from the commonName attribute πŸ¦‹

A globally unique name for an entry, called a distinguished name or DN, is built by joining the sequence of RDNs from the root of the tree down to the entry 🌈 So if Babs worked at the University of Michigan, her U-M entry's DN might be "cn=Barbara Jensen, o=University of Michigan, c=US". The DN format used by LDAP is defined in [4] 🧚

LDAP supports operations to authenticate, search for and retrieve info, modify info, and add and delete entries from the tree πŸ’– The next sections cover how the API is used, plus detailed descriptions of the calls that make all of this happen ✨

3. Overview of LDAP API Use πŸŽ€

An application generally uses the LDAP API in four simple steps 🫢

  • Open a connection to an LDAP server. The ldap_open() call returns a handle to the connection, and you can have multiple connections open at once πŸ¦‹
  • Authenticate to the LDAP server and/or the X.500 DSA. The ldap_bind() call and friends support a variety of authentication methods πŸ”
  • Perform some LDAP operations and get some results. ldap_search() and friends return results, which you can parse with ldap_result2error(), ldap_first_entry(), ldap_next_entry(), etc. πŸ“
  • Close the connection. The ldap_unbind() call closes it πŸ‘‹

Operations can be done synchronously or asynchronously 🌊 Synchronous calls end in _s. For example, a synchronous search is done with ldap_search_s(), and an asynchronous search is started with ldap_search() πŸ’œ All synchronous routines return an indication of how the operation turned out (like the constant LDAP_SUCCESS or some other error code) ✨ Asynchronous routines return the message id of the operation they started, and you can use that id in later ldap_result() calls to get the result(s) 🧚 You can abandon an asynchronous operation by calling ldap_abandon() πŸ™…β€β™€οΈ

Results and errors come back in an opaque structure called LDAPMessage. There are routines to parse that structure, step through the entries and attributes returned, and so on. There are also routines to interpret errors, and the next sections describe all of them πŸ’…

4. Calls for Performing LDAP Operations πŸ”₯

This section breaks down each LDAP operation API call in detail πŸ’– Every call takes a "connection handle," which is a pointer to an LDAP structure holding per-connection info πŸŽ€ Lots of routines return results in an LDAPMessage structure, and those structures (plus a few others) get described as needed below 🌸

4.1. Opening a connection πŸ¦‹

ldap_open() opens a connection to the LDAP server 🌈

          typedef struct ldap {
                  /* ... opaque parameters ... */
                  int     ld_deref;
                  int     ld_timelimit;
                  int     ld_sizelimit;
                  int     ld_errno;
                  char    *ld_matched;
                  char    *ld_error;
                  /* ... opaque parameters ... */
          } LDAP;

          LDAP *ldap_open( char *hostname, int portno );

Here are the parameters 🧚

  • hostname contains a space-separated list of hostnames or dotted strings representing the IP address of hosts running an LDAP server to connect to. The hosts are tried in the order listed, and it stops with the first one that gets a successful connection πŸ’œ
  • portno contains the TCP port number to connect to. The default LDAP port can be obtained by supplying the constant LDAP_PORT ✨

ldap_open() returns a "connection handle," which is a pointer to an LDAP structure that gets passed to later calls about the connection 🫢 It returns NULL if the connection can't be opened. One of the ldap_bind calls described below MUST be completed before any other operations can be performed on the connection ⚠️

The calling program SHOULD assume nothing about the order of the fields in the LDAP structure πŸ™ˆ There MAY be other fields in the structure for internal library use. The fields shown above are described as needed in the descriptions of other calls below πŸ“

4.2. Authenticating to the directory πŸ”

ldap_bind() and friends are used to authenticate to the directory πŸ’…

        int ldap_bind( LDAP *ld, char *dn, char *cred, int method );

        int ldap_bind_s( LDAP *ld, char *dn, char *cred, int method );

        int ldap_simple_bind( LDAP *ld, char *dn, char *passwd );

        int ldap_simple_bind_s( LDAP *ld, char *dn, char *passwd );

        int ldap_kerberos_bind( LDAP *ld, char *dn );

        int ldap_kerberos_bind_s( LDAP *ld, char *dn );

Here's what the parameters mean πŸŽ€

  • ld is the connection handle;
  • dn is the name of the entry to bind as;
  • cred holds the credentials to authenticate with;
  • method is one of LDAP_AUTH_SIMPLE, LDAP_AUTH_KRBV41, or LDAP_AUTH_KRBV42, indicating the authentication method to use;
  • passwd is, for ldap_simple_bind(), the password to compare to the entry's userPassword attribute;

There are three types of bind calls: simple authentication, Kerberos authentication, and general routines that can do either one πŸ¦‹ With Kerberos version 4 authentication through the general ldap_bind() routines, the credentials are ignored, because those routines assume a valid ticket granting ticket already exists that can be used to retrieve the right service tickets 🌸

Synchronous versions of these routines have names ending in _s πŸ’– They return the result of the bind: LDAP_SUCCESS if it worked, or another LDAP error code if it didn't. Check the error handling section below for more about possible errors and how to read them πŸ”₯

Asynchronous versions return the message id of the bind operation they started 🌊 You can then call ldap_result(), described below, to get the result of the bind. If something goes wrong, these routines return -1 and set the ld_errno field in the LDAP structure accordingly 😭

Note that no other operations over the connection SHOULD be attempted before a bind call has successfully completed πŸ™…β€β™€οΈ Subsequent bind calls can be used to re-authenticate over the same connection πŸ‘‘

4.3. Closing the connection πŸ‘‹

ldap_unbind() is used to unbind from the directory and close the connection 🧚

        int ldap_unbind( LDAP *ld );

Parameter:

  • ld is the connection handle πŸ’œ

ldap_unbind() works synchronously: it unbinds from the directory, closes the connection, and frees up the ld structure before it returns ✨ It returns LDAP_SUCCESS, or another LDAP error code if the request can't be sent to the LDAP server 🫢 After you call ldap_unbind(), the ld connection handle is invalid, so don't touch it again πŸ’€

4.4. Searching ✨

Okay so searching is where it gets spicy πŸ”₯ ldap_search() and its friends let you search the LDAP directory and get back a requested set of attributes for every entry that matches. There are three variations, bestie πŸ’…

struct timeval {
        long    tv_sec;
        long    tv_usec;
};
int ldap_search(
        LDAP    *ld,
        char    *base,
        int     scope,
        char    *filter,
        char    *attrs[],
        int     attrsonly
);
int ldap_search_s(
        LDAP            *ld,
        char            *base,
        int             scope,
        char            *filter,
        char            *attrs[],
        int             attrsonly,
        LDAPMessage     **res
);
int ldap_search_st(
        LDAP            *ld,
        char            *base,
        int             scope,
        char            *filter,
        char            *attrs[],
        int             attrsonly,
        struct timeval  *timeout,
        LDAPMessage     **res
);

Here's what each parameter does:

  • ld: the connection handle πŸ’–
  • base: the dn of the entry where the search starts 🌸
  • scope: one of LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, or LDAP_SCOPE_SUBTREE, showing how wide the search goes πŸ¦‹
  • filter: a character string as described in RFC 1558 [3], which is the search filter ✨
  • attrs: a NULL-terminated array of strings naming which attributes to return for each matching entry. Passing NULL here means all available attributes get retrieved πŸ‘‘
  • attrsonly: a boolean that should be zero if you want both attribute types and values back, and non-zero if you only want the types πŸ™ˆ
  • timeout: for ldap_search_st() only, the local search timeout value ⏳
  • res: for the synchronous calls, a result parameter that holds the results of the search once the call finishes πŸŽ€

Three fields in the ld connection handle control how the search runs.

  • ld_sizelimit: a limit on how many entries come back from the search. Zero means no limit πŸ“
  • ld_timelimit: a limit on how many seconds the search can spend. Zero means no limit 🧚
  • ld_deref: one of LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, LDAP_DEREF_FINDING, or LDAP_DEREF_ALWAYS, specifying how aliases get handled during the search. LDAP_DEREF_SEARCHING means aliases get dereferenced during the search but not when locating the base object of the search. LDAP_DEREF_FINDING means aliases get dereferenced when locating the base object but not during the search. πŸ”

An asynchronous search starts when you call ldap_search(). It gives back the message id of the search it started. You grab the results with a later call to ldap_result(), then parse them with the result parsing routines we'll get into later. If something goes wrong, you get -1 back and the ld_errno field in the LDAP structure gets set appropriately 😭

A synchronous search happens when you call ldap_search_s() or ldap_search_st(). The two are identical, except ldap_search_st() takes an extra parameter for a search timeout. Both return an indication of the result, either LDAP_SUCCESS or some error (see Error Handling below). Any entries from the search live in the res parameter. That parameter is opaque to you, so pull out entries, attributes, values, etc. with the parsing routines described below. Once you're done with the results in res, free them by calling ldap_msgfree(), which we'll cover later 🧹


4.5. Reading an entry πŸ“–

LDAP doesn't support a read operation directly πŸ™ƒ so reading gets faked with a search. You set base to the DN of the entry you want to read, scope to LDAP_SCOPE_BASE, and filter to "(objectclass=*)". attrs holds the list of attributes to return.

4.6. Listing the children of an entry 🌳

Same trick for listing. LDAP has no list operation, so it's emulated with a search. You set base to the DN of the entry you want to list, scope to LDAP_SCOPE_ONELEVEL, and filter to "(objectclass=*)". attrs holds the list of attributes to return for each child entry.

4.7. Modifying an entry ✏️

ldap_modify() and ldap_modify_s() are the routines for changing an existing LDAP entry.

typedef struct ldapmod {
        int             mod_op;
        char            *mod_type;
        union {
                char            **modv_strvals;
                struct berval   **modv_bvals;
        } mod_vals;
} LDAPMod;
#define mod_values      mod_vals.modv_strvals
#define mod_bvalues     mod_vals.modv_bvals

int ldap_modify( LDAP *ld, char *dn, LDAPMod *mods[] );

int ldap_modify_s( LDAP *ld, char *dn, LDAPMod *mods[] );

Parameters:

  • ld: the connection handle πŸ’–
  • dn: the name of the entry to modify 🌸
  • mods: a NULL-terminated array of modifications to make to the entry ✨

The fields in the LDAPMod structure mean the following:

  • mod_op: the modification operation to perform. It should be one of LDAP_MOD_ADD, LDAP_MOD_DELETE, or LDAP_MOD_REPLACE. This field also tells you what type of values are in the mod_vals union. It is ORed with LDAP_MOD_BVALUES to select the mod_bvalues form. Otherwise, the mod_values form is used πŸ¦‹
  • mod_type: the type of the attribute to modify πŸŽ€
  • mod_vals: the values (if any) to add, delete, or replace. Only one of the mod_values or mod_bvalues variants should be used, selected by ORing the mod_op field with the constant LDAP_MOD_BVALUES. mod_values is a NULL-terminated array of zero-terminated strings, and mod_bvalues is a NULL-terminated array of berval structures that can be used to pass binary values such as images πŸ–ΌοΈ

For LDAP_MOD_ADD modifications, the given values get added to the entry, and the attribute gets created if it's needed. For LDAP_MOD_DELETE modifications, the given values get deleted from the entry, and the attribute gets removed if no values remain. If the entire attribute is to be deleted, the mod_vals field should be set to NULL. For LDAP_MOD_REPLACE modifications, the attribute will have the listed values after the modification, and it gets created if necessary. All modifications are performed in the order they're listed πŸ“‹

ldap_modify_s() returns the LDAP error code resulting from the modify operation. You can interpret that code with ldap_perror() and friends 🧐

ldap_modify() returns the message id of the request it starts, or -1 on error. You get the result of the operation by calling ldap_result() πŸ’…

4.8. Modifying the RDN of an entry 🏷️

ldap_modrdn() and ldap_modrdn_s() are how you change the name of an LDAP entry.

int ldap_modrdn(
        LDAP    *ld,
        char    *dn,
        char    *newrdn,
        int     deleteoldrdn
);
int ldap_modrdn_s(
        LDAP    *ld,
        char    *dn,
        char    *newrdn,
        int     deleteoldrdn
);

Parameters:

  • ld: the connection handle πŸ’–
  • dn: the name of the entry whose RDN is to be changed 🌸
  • newrdn: the new RDN to give the entry ✨
  • deleteoldrdn: a boolean value. If it's non-zero, the old RDN value(s) should be removed. If it's zero, the old RDN value(s) should be kept as non-distinguished values of the entry πŸ¦‹

ldap_modrdn_s() is the synchronous one, and it returns the LDAP error code showing how the operation turned out πŸ‘‘

ldap_modrdn() is asynchronous. It returns the message id of the operation it starts, or -1 if there's trouble. You get the result by calling ldap_result() πŸŽ€

4.9. Adding an entry βž•

ldap_add() and ldap_add_s() add entries to the LDAP directory.

int ldap_add( LDAP *ld, char *dn, LDAPMod *attrs[] );

int ldap_add_s( LDAP *ld, char *dn, LDAPMod *attrs[] );

Parameters:

  • ld: the connection handle πŸ’–
  • dn: the name of the entry to add 🌸
  • attrs: the entry's attributes, specified using the LDAPMod structure defined for ldap_modify(). The mod_type and mod_vals fields should be filled in. The mod_op field is ignored unless it's ORed with the constant LDAP_MOD_BVALUES, which is used to select the mod_bvalues case of the mod_vals union ✨
Heads up bestie: the parent of the entry must already exist before you add it 🚨

ldap_add_s() is synchronous, and it returns the LDAP error code showing the outcome of the operation πŸ‘‘

ldap_add() is asynchronous. It returns the message id of the operation it starts, or -1 in case of trouble. You get the result of the operation by calling ldap_result() πŸ¦‹

4.10. Deleting an entry πŸ—‘οΈ

ldap_delete() and ldap_delete_s() delete entries from the LDAP directory.

int ldap_delete( LDAP *ld, char *dn );

int ldap_delete_s( LDAP *ld, char *dn );

Parameters:

  • ld: the connection handle πŸ’–
  • dn: the name of the entry to delete 🌸

Note that the entry you delete must be a leaf entry (i.e., it must have no children). Deleting entire subtrees isn't supported by LDAP, so no shortcuts there πŸ’€

ldap_delete_s() is synchronous, and it returns the LDAP error code showing the outcome of the operation πŸ‘‘

ldap_delete() is asynchronous. It returns the message id of the operation it starts, or -1 in case of trouble. You get the result of the operation by calling ldap_result() πŸ¦‹

5. Calls for abandoning an operation πŸ›‘

ldap_abandon() is how you abandon an operation that's in progress.

int ldap_abandon( LDAP *ld, int msgid );

ldap_abandon() abandons the operation with message id msgid. It returns zero if the abandon was successful, and -1 otherwise. After a successful call to ldap_abandon(), results with the given message id are never returned from a call to ldap_result() πŸ™…β€β™€οΈβœ¨

6. Calls for obtaining results ✨

Okay bestie, so ldap_result() is how you grab the result of an operation you started asynchronously πŸ’…. And ldap_msgfree() is the cleanup crew 🧹. It frees results you got from an earlier ldap_result() call, or from a synchronous search routine.

int ldap_result(
        LDAP            *ld,
        int             msgid,
        int             all,
        struct timeval  *timeout,
        LDAPMessage     **res
);

int ldap_msgfree( LDAPMessage *res );

Here's the parameter rundown πŸŽ€:

  • ld The connection handle;
  • msgid The message id of the operation whose results you want back, or the constant LDAP_RES_ANY if you'll take any result πŸ¦‹;
  • all A boolean that only matters for search results. If it's non-zero, all the results of a search get retrieved before any of them are returned. If it's zero, search entries come back one at a time as they arrive 🌸;
  • timeout How long to wait for results. A NULL value makes ldap_result() block until results are available. A timeout of zero seconds means polling 🧚;
  • res For ldap_result(), this is the result parameter that will hold the result(s) of the operation. For ldap_msgfree(), it's the result chain to be freed, which you got from a previous call to ldap_result(), ldap_search_s() or ldap_search_st() ✨.

When ldap_result() succeeds, it returns the type of the result in the res parameter πŸ‘‘. That type is one of these constants:

LDAP_RES_BIND
LDAP_RES_SEARCH_ENTRY
LDAP_RES_SEARCH_RESULT
LDAP_RES_MODIFY
LDAP_RES_ADD
LDAP_RES_DELETE
LDAP_RES_MODRDN
LDAP_RES_COMPARE

ldap_result() returns 0 if the timeout expired, and -1 if an error happens. In that second case, the ld_errno field of the ld structure gets set accordingly 😭.

ldap_msgfree() frees the result structure that res points to, and it returns the type of the message it freed 🫢.


7. Calls for error handling πŸ’€

These calls help you make sense of errors that other LDAP API routines throw back at you πŸ™ˆ.

int ldap_result2error(
        LDAP            *ld,
        LDAPMessage     *res,
        int             freeit
);

char *ldap_err2string( int err );

void ldap_perror( LDAP *ld, char *msg );

Parameters, no cap:

  • ld The connection handle;
  • res The result of an LDAP operation, as returned by ldap_result() or by one of the synchronous API operation calls;
  • freeit A boolean that says whether res should be freed (non-zero) or not (zero) πŸ“;
  • err An LDAP error code, as returned by ldap_result2error() or by one of the synchronous API operation calls;
  • msg A message to show before the LDAP error message.

ldap_result2error() takes the LDAP result message you got from ldap_result(), or the res returned by one of the synchronous calls, and turns it into a numeric LDAP error code πŸ”₯. It also parses the ld_matched and ld_error parts of the result message and stashes them in the connection handle information. Every synchronous operation routine calls ldap_result2error() before it returns, which is how those fields stay correct. The relevant fields in the connection structure are:

  • ld_matched When you get an LDAP_NO_SUCH_OBJECT error, this holds the extent of the DN that matched;
  • ld_error This holds the error message the LDAP server sent in the result;
  • ld_errno The LDAP error code showing the outcome of the operation. It's one of these constants:
LDAP_SUCCESS
LDAP_OPERATIONS_ERROR
LDAP_PROTOCOL_ERROR
LDAP_TIMELIMIT_EXCEEDED
LDAP_SIZELIMIT_EXCEEDED
LDAP_COMPARE_FALSE
LDAP_COMPARE_TRUE
LDAP_STRONG_AUTH_NOT_SUPPORTED
LDAP_STRONG_AUTH_REQUIRED
LDAP_NO_SUCH_ATTRIBUTE
LDAP_UNDEFINED_TYPE
LDAP_INAPPROPRIATE_MATCHING
LDAP_CONSTRAINT_VIOLATION
LDAP_TYPE_OR_VALUE_EXISTS
LDAP_INVALID_SYNTAX
LDAP_NO_SUCH_OBJECT
LDAP_ALIAS_PROBLEM
LDAP_INVALID_DN_SYNTAX
LDAP_IS_LEAF
LDAP_ALIAS_DEREF_PROBLEM
LDAP_INAPPROPRIATE_AUTH
LDAP_INVALID_CREDENTIALS
LDAP_INSUFFICIENT_ACCESS
LDAP_BUSY
LDAP_UNAVAILABLE
LDAP_UNWILLING_TO_PERFORM
LDAP_LOOP_DETECT
LDAP_NAMING_VIOLATION
LDAP_OBJECT_CLASS_VIOLATION
LDAP_NOT_ALLOWED_ON_NONLEAF
LDAP_NOT_ALLOWED_ON_RDN
LDAP_ALREADY_EXISTS
LDAP_NO_OBJECT_CLASS_MODS
LDAP_RESULTS_TOO_LARGE
LDAP_OTHER
LDAP_SERVER_DOWN
LDAP_LOCAL_ERROR
LDAP_ENCODING_ERROR
LDAP_DECODING_ERROR
LDAP_TIMEOUT
LDAP_AUTH_UNKNOWN
LDAP_FILTER_ERROR
LDAP_USER_CANCELLED
LDAP_PARAM_ERROR
LDAP_NO_MEMORY

ldap_err2string() turns a numeric LDAP error code, like one from ldap_result2error() or a synchronous call, into a readable NULL-terminated string that describes the error πŸ’–. It returns a pointer to static data.

ldap_perror() prints the message you pass in msg, then shows the error from the ld_errno field of the ld connection handle, all to standard error πŸ“£.


8. Calls for parsing search entries 🧁

These calls parse the entries that ldap_search() and friends return πŸ¦„. The entries come back in an opaque structure, which you should only touch through the routines below. There are routines to step through the entries, step through an entry's attributes, get an entry's name, and get the values tied to a given attribute in an entry πŸ’œ.

8.1. Stepping through a set of entries

ldap_first_entry() and ldap_next_entry() let you step through the entries in a search result 🌈. ldap_count_entries() counts how many entries came back.

LDAPMesage *ldap_first_entry( LDAP *ld, LDAPMessage *res );

LDAPMesage *ldap_next_entry( LDAP *ld, LDAPMessage *entry );

int ldap_count_entries( LDAP *ld, LDAPMessage *res );

Parameters:

  • ld The connection handle;
  • res The search result, as obtained from one of the synchronous search routines or from ldap_result();
  • entry The entry returned by a previous call to ldap_first_entry() or ldap_next_entry().

ldap_first_entry() and ldap_next_entry() return NULL when there are no more entries left πŸ™ƒ. They also return NULL if an error happens while you're stepping, and then the ld_errno field of the ld connection handle gets set to show the error.

ldap_count_entries() returns how many entries are in a chain of entries. You can also call it with an entry from ldap_first_entry() or ldap_next_entry() to count the entries still left in the chain ✨.

8.2. Stepping through the attributes of an entry

ldap_first_attribute() and ldap_next_attribute() step through the list of attribute types that comes back with an entry 🍭.

char *ldap_first_attribute(
        LDAP            *ld,
        LDAPMessage     *entry,
        void            **ptr
);
char *ldap_next_attribute(
        LDAP            *ld,
        LDAPMessage     *entry,
        void            *ptr
);

Parameters:

  • ld The connection handle;
  • entry The entry whose attributes you want to step through, as returned by ldap_first_entry() or ldap_next_entry();
  • ptr In ldap_first_attribute(), this is the address of a pointer used internally to track the current position in the entry. In ldap_next_attribute(), it's the pointer returned by a previous call to ldap_first_attribute() 🌷.

ldap_first_attribute() and ldap_next_attribute() return NULL when the attributes run out, or if there's an error. In the error case, the ld_errno field in the ld connection handle gets set to show the problem 😬.

Both routines return a pointer to a per-connection buffer that holds the current attribute name. Treat that buffer like static data 🫧. ldap_first_attribute() allocates a BerElement to track the current position, and it returns a pointer to that BerElement in ptr. You pass that pointer to later calls of ldap_next_attribute() so you can keep stepping through the entry's attributes.

The attribute names returned are ready to pass to ldap_get_values() and friends, so you can grab the values that go with them πŸŽ€.

8.3. Retrieving the values of an attribute

ldap_get_values() and ldap_get_values_len() pull the values of a given attribute out of an entry πŸ§ƒ. ldap_count_values() and ldap_count_values_len() count the values you got back. ldap_value_free() and ldap_value_free_len() free them.

typedef struct berval {
        unsigned long   bv_len;
        char            *bv_val;
};

char **ldap_get_values(
        LDAP            *ld,
        LDAPMessage     *entry,
        char            *attr
);

struct berval **ldap_get_values_len(
        LDAP            *ld,
        LDAPMessage     *entry,
        char            *attr
);

int ldap_count_values( char **vals );

int ldap_count_values_len( struct berval **vals );

int ldap_value_free( char **vals );

int ldap_value_free_len( struct berval **vals );

Parameters:

  • ld The connection handle;
  • entry The entry to get values from, as returned by ldap_first_entry() or ldap_next_entry();
  • attr The attribute whose values you want. This comes from ldap_first_attribute() or ldap_next_attribute(), or it can be a string you supply yourself, like "mail" πŸ’Œ;
  • vals The values returned by a previous call to ldap_get_values() or ldap_get_values_len().

There are two forms of most of these calls πŸ¦‹. The first form only works for non-binary character string data. The second _len form works with any kind of data.

Heads up, bestie: the values returned are malloc'ed, so you need to free them with ldap_value_free() or ldap_value_free_len() once you're done with them 🫢.

8.4. Retrieving the name of an entry πŸͺͺ

okay bestie, so to get an entry's name you call ldap_get_dn() πŸ’… Then you can chop it into pieces with ldap_explode_dn(), and ldap_dn2ufn() turns it into a more "user friendly" format so people don't have to read raw DN soup 🫢

char *ldap_get_dn( LDAP *ld, LDAPMessage *entry );

char **ldap_explode_dn( char *dn, int notypes );

char *ldap_dn2ufn( char *dn );

Here's what each parameter does ✨

ld      The connection handle;

entry   The entry whose name is to be retrieved, as returned by
        ldap_first_entry() or ldap_next_entry();

dn      The dn to explode, as returned by ldap_get_dn();

notypes A boolean parameter, if non-zero indicating that the dn com-
        ponents should have their type information stripped off
        (i.e., "cn=Babs" would become "Babs").

ldap_get_dn() gives you back NULL if something goes wrong while parsing the dn 😭 When that happens it sets ld_errno in the ld connection handle so you know what went wrong. If it works, you get a pointer to malloc'ed space, and you're responsible for calling free() on it once you're done. The DN format it returns is described in [4] 🌸

ldap_explode_dn() returns a char * array holding the RDN components of your DN. Whether the type info is kept depends on notypes πŸ¦‹ When you're finished with the array, free it with ldap_value_free(), not free(). Yes, it's a different function, and yes, it matters πŸ’€

ldap_dn2ufn() converts the DN into the user friendly format described in [5] 🧚 The UFN it returns is malloc'ed too, so call free() on it when you don't need it anymore πŸ’–


9. Security Considerations πŸ”

⚠️ Heads up, this one is a little scary: LDAP supports minimal security during connection authentication. Don't assume the connection is locked down just because you authenticated 😭

10. Acknowledgements πŸŽ€

This material is based upon work supported by the National Science Foundation under Grant No. NCR-9416667. Thank you, NSF, you're iconic πŸ‘‘

11. Bibliography πŸ“š

  • [1] The Directory: Selected Attribute Syntaxes. CCITT, Recommendation X.520.
  • [2] Howes, T., Kille, S., Yeong, W., and C. Robbins, "The String Representation of Standard Attribute Syntaxes", University of Michigan, ISODE Consortium, Performance Systems International, NeXor Ltd., RFC 1778, March 1995.
  • [3] Howes, T., "A String Representation of LDAP Search Filters", RFC 1558, University of Michigan, December 1993.
  • [4] Kille, S., "A String Representation of Distinguished Names", RFC 1779, ISODE Consortium, March 1995.
  • [5] Kille, S., "Using the OSI Directory to Achieve User Friendly Naming", RFC 1781, ISODE Consortium, March 1995.
  • [6] S.P. Miller, B.C. Neuman, J.I. Schiller, J.H. Saltzer, "Kerberos Authentication and Authorization System", MIT Project Athena Documentation Section E.2.1, December 1987
  • [7] Yeong, W., Howes, T., and S. Kille, "Lightweight Directory Access Protocol," RFC 1777, Performance Systems International, University of Michigan, ISODE Consortium, March 1995.

12. Authors' Addresses πŸ’Œ

These are the people who made it happen, so here they are, exactly as listed 🫢

       Tim Howes
       University of Michigan
       ITD Research Systems
       535 W William St.
       Ann Arbor, MI 48103-4943
       USA

       Phone: +1 313 747-4454
       EMail: tim@umich.edu


       Mark Smith
       University of Michigan
       ITD Research Systems
       535 W William St.
       Ann Arbor, MI 48103-4943
       USA

       Phone: +1 313 764-2277
       EMail: mcs@umich.edu

13. Appendix A - Sample LDAP API Code πŸ§‘β€πŸ’»

And finally, the full example, so you can see every single call working together ✨ This is the whole thing from connecting to unbinding πŸ¦‹

#include <ldap.h>

main()
{
        LDAP            *ld;
        LDAPMessage     *res, *e;
        int             i;
        char            *a, *dn;
        void            *ptr;
        char            **vals;

        /* open a connection */
        if ( (ld = ldap_open( "dotted.host.name", LDAP_PORT ))
                == NULL )
                exit( 1 );

        /* authenticate as nobody */
        if ( ldap_simple_bind_s( ld, NULL, NULL ) != LDAP_SUCCESS ) {
                ldap_perror( ld, "ldap_simple_bind_s" );
                exit( 1 );
        }

        /* search for entries with cn of "Babs Jensen",
                return all attrs  */
        if ( ldap_search_s( ld, "o=University of Michigan, c=US",
            LDAP_SCOPE_SUBTREE, "(cn=Babs Jensen)", NULL, 0, &res )
            != LDAP_SUCCESS ) {
                ldap_perror( ld, "ldap_search_s" );
                exit( 1 );
        }

        /* step through each entry returned */
        for ( e = ldap_first_entry( ld, res ); e != NULL;
            e = ldap_next_entry( ld, e ) ) {
                /* print its name */
                dn = ldap_get_dn( ld, e );
                printf( "dn: %s0, dn );
                free( dn );

                /* print each attribute */
                for ( a = ldap_first_attribute( ld, e, &ptr );
                        a != NULL;
                    a = ldap_next_attribute( ld, e, ptr ) ) {
                        printf( "attribute: %s0, a );

                        /* print each value */
                        vals = ldap_get_values( ld, e, a );
                        for ( i = 0; vals[i] != NULL; i++ ) {
                                printf( "value: %s0, vals[i] );
                        }
                        ldap_value_free( vals );
                }
        }
        /* free the search results */
        ldap_msgfree( res );

        /* close and free connection resources */
        ldap_unbind( ld );
}
πŸ“¦ where this page comes from