Introduction


There are efforts in the industry to do Microsoft Windows Active Directiory integration. Often the first step is to research how to connect to AD using LDAP in general. Explanations are always cryptic, examples may help cut down on time.

The code example below demonstrates how to connect to a Active Directory domain controller using the LDAP protocol. It retrieves the domain group information for a given username. The program is written in 'C', using OpenLDAP's client library libldap.
At the end, there are also two simple C programs that help me determine all active domain users that belong to a Windows group, and which groups a user has been added to.

ad_ldaptest program output example


fm@susie112:~> ./ad_ldaptest
Connecting to host 192.168.100.2 at port 389...

Generated LDAP handle.
Set LDAPv3 client version.
LDAP connection successful.
LDAP search successful.
LDAP search returned 1 objects.
Found Object: CN=frank4dd,OU=IT_Department,OU=User,DC=frank4dd,DC=com
Found Attribute: memberOf
memberOf: CN=acl_secure_exchange,OU=Global Groups,OU=User,DC=frank4dd,DC=com
...
memberOf: CN=acl_security_audit,OU=Global Groups,OU=User,DC=frank4dd,DC=com
memberOf: CN=adm_LINUX_PRD,OU=Global Groups,OU=User,DC=frank4dd,DC=com

ad_ldaptest.c source code


/* --------------------------------------------------------------------------- *
 * file:        ad_ldaptest.c v1.0                                             *
 * author:      10/19/2010 Frank4DD, see http://fm4dd.com/programming          *
 * purpose:     This test program connects to a Actice Directory LDAP using    *
 *              simple bind and returns the groups for a given domain user.    *
 *                                                                             *
 * compile:     gcc -o ad_ldaptest ad_ldaptest.c -lldap                        *
 * --------------------------------------------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <ldap.h>

int main( int argc, char **argv ) {

  LDAP *ldap;
  LDAPMessage *answer, *entry;
  BerElement *ber;

  int  result;
  int  auth_method    = LDAP_AUTH_SIMPLE;
  int  ldap_version   = LDAP_VERSION3;
  char *ldap_host     = "192.168.100.2";
  int   ldap_port     = 389;
  char *ldap_dn       = "ldapconnect@frank4dd.com";
  char *ldap_pw       = "testldpa5!";
  char *base_dn       = "OU=User,DC=frank4dd,DC=com";

  // The search scope must be either LDAP_SCOPE_SUBTREE or LDAP_SCOPE_ONELEVEL
  int  scope          = LDAP_SCOPE_SUBTREE;
  // The search filter, "(objectClass=*)" returns everything. Windows can return
  // 1000 objects in one search. Otherwise, "Size limit exceeded" is returned.
  char *filter        = "(&(objectClass=user)(sAMAccountName=frank4dd))";
  // The attribute list to be returned, use {NULL} for getting all attributes
  char *attrs[]       = {"memberOf", NULL};
  // Specify if only attribute types (1) or both type and value (0) are returned
  int  attrsonly      = 0;
  // entries_found holds the number of objects found for the LDAP search
  int  entries_found  = 0;
  // dn holds the DN name string of the object(s) returned by the search
  char *dn            = "";
  // attribute holds the name of the object(s) attributes returned
  char *attribute     = "";
  // values is  array to hold the attribute values of the object(s) attributes
  char **values;
  // i is the for loop variable to cycle through the values[i]
  int  i              = 0;

  /* First, we print out an informational message. */
  printf( "Connecting to host %s at port %d...\n\n", ldap_host, ldap_port );

  /* STEP 1: Get a LDAP connection handle and set any session preferences. */
  /* For ldaps we must call ldap_sslinit(char *host, int port, int secure) */
  if ( (ldap = ldap_init(ldap_host, ldap_port)) == NULL ) {
    perror( "ldap_init failed" );
    exit( EXIT_FAILURE );
  } else {
    printf("Generated LDAP handle.\n");
  }

  /* The LDAP_OPT_PROTOCOL_VERSION session preference specifies the client */
  /* is an LDAPv3 client. */
  result = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);

  if ( result != LDAP_OPT_SUCCESS ) {
      ldap_perror(ldap, "ldap_set_option failed!");
      exit(EXIT_FAILURE);
  } else {
    printf("Set LDAPv3 client version.\n");
  }

  /* STEP 2: Bind to the server. */
  // If no DN or credentials are specified, we bind anonymously to the server */
  // result = ldap_simple_bind_s( ldap, NULL, NULL );
  result = ldap_simple_bind_s(ldap, ldap_dn, ldap_pw );

  if ( result != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_simple_bind_s: %s\n", ldap_err2string(result));
    exit(EXIT_FAILURE);
  } else {
    printf("LDAP connection successful.\n");
  }

  /* STEP 3: Do the LDAP search. */
  result = ldap_search_s(ldap, base_dn, scope, filter,
                         attrs, attrsonly, &answer);

  if ( result != LDAP_SUCCESS ) {
    fprintf(stderr, "ldap_search_s: %s\n", ldap_err2string(result));
    exit(EXIT_FAILURE);
  } else {
    printf("LDAP search successful.\n");
  }

  /* Return the number of objects found during the search */
  entries_found = ldap_count_entries(ldap, answer);
  if ( entries_found == 0 ) {
    fprintf(stderr, "LDAP search did not return any data.\n");
    exit(EXIT_FAILURE);
  } else {
    printf("LDAP search returned %d objects.\n", entries_found);
  }

  /* cycle through all objects returned with our search */
  for ( entry = ldap_first_entry(ldap, answer);
        entry != NULL;
        entry = ldap_next_entry(ldap, entry)) {

    /* Print the DN string of the object */
    dn = ldap_get_dn(ldap, entry);
    printf("Found Object: %s\n", dn);

    // cycle through all returned attributes
    for ( attribute = ldap_first_attribute(ldap, entry, &ber);
          attribute != NULL;
          attribute = ldap_next_attribute(ldap, entry, ber)) {

      /* Print the attribute name */
      printf("Found Attribute: %s\n", attribute);
      if ((values = ldap_get_values(ldap, entry, attribute)) != NULL) {

        /* cycle through all values returned for this attribute */
        for (i = 0; values[i] != NULL; i++) {

          /* print each value of a attribute here */
          printf("%s: %s\n", attribute, values[i] );
        }
        ldap_value_free(values);
      }
    }
    ldap_memfree(dn);
  }

  ldap_msgfree(answer);
  ldap_unbind(ldap);
  return(EXIT_SUCCESS);
}

If anybody needs this example identically written in Java, there is ad_ldaptest.java available for download under the Java code section, helping to migrate code from 'C'. There is also a more advanced Java test program available.

ad_ldap_listuser - returns all groups for a particular domain user


ad_ldap_listuser connects to a Actice Directory LDAP using simple bind and returns all groups for a particular domain user. The domain users account name is given as a single argument. All the connection parameters are hard-coded in the program, which is OK for me since they do not change. Using * as a wildcard works fine, it allows to retrieve multiple names. ad_ldap_listuser ben* would return all Windows domain account names starting with "ben".

fm@susie112:~> ./ad_ldap_listuser frank4dd
Connecting to host 192.168.100.2 at port 389...

Generated LDAP handle.
Set LDAPv3 client version.
LDAP connection successful.
LDAP search successful.
LDAP search returned 1 objects.
Found Object: CN=frank4dd,OU=IT_Department,OU=User,DC=frank4dd,DC=com
Found Attribute: displayName
displayName: Frank Me
Found Attribute: memberOf
memberOf: CN=acl_secure_exchange,OU=GlobalGroups,OU=User,DC=frank4dd,DC=com
...
memberOf: CN=adm_Linux_PRD,OU=GlobalGroups,OU=User,DC=frank4dd,DC=com

ad_ldap_listgroup - returns all members for a particular domain group


ad_ldap_listgroup connects to a Actice Directory LDAP using simple bind and returns all active Windows accounts that belong to this Windows domain group. Active means that their accounts have not been disabled. The group names DN is given as a single argument. It needs to be enclosed in double quotes if the string contains spaces. All the connection parameters are hard-coded in the program, which is OK for me since they do not change.

fm@susie112:~> ./ad_ldap_listgroup "CN=acl_secure_transfer,OU=Global Groups,OU=User,DC=frank4dd,DC=com"
Connecting to host 192.168.100.2 at port 389 as ldapconnect@frank4dd.com ...

Generated LDAP handle.
Set LDAPv3 client version.
LDAP connection successful.
LDAP search successful.
LDAP search found 44 objects.

[ 0] frank4dd      Frank Me
[ 1] starkbe       Benjamin Stark
 ...          
[28] adamssi       Sinclair Adams        

LDAP search returned 29 active users in LDAP group CN=acl_secure_transfer,OU=GlobalGroup,OU=User,DC=frank4dd,DC=com.

A identical Java program ad_ldap_listgroup.java has been written as a reference.

Source:

See also: