ED-ID Usage Instructions

Introduction

ED-ID is a directory designed to allow applications easy programmatic access to data needed for authentication, authorization, application personalization and customization, and programmatic business decision evaluation. See the ED-ID FAQ for more information.

While ED-ID provides the same basic functionality as ED-Auth, namely PID/pass authentication and simple affiliation authorization, ED-ID exists mainly to give privileged accounts access to person information that can otherwise not be publicly viewed. These privileged accounts, called ED-ID Services, are primarily used to look up person data and authorize people on this data. See the ED-ID Schema for the complete listing of person data.

Note: These instructions do not show how to do PID/pass authentication. The ED-Auth Usage instructions show how this is done. The same code will apply in ED-ID.

What can I use ED-ID for?

ED-ID can be used for PID/pass authentication and simple affiliation authorization (though ED-Auth is recommended if this is all your application needs to do), authorization based on any number of person attributes, and simple lookup of person data such as preferred email address and major. Once you have been issued an ED-ID Service and have been approved to view person attributes, you may use ED-ID to authenticate, authorize, or retrieve person data as you wish.

See this FAQ Entry for more information.

ED-Auth vs. ED-ID

Middleware provides two directories for authentication and authorization purposes. Before you begin the process of interfacing your system with ED-Auth or ED-ID (or both!), you must first consider which system better suits your needs. Both ED-Auth and ED-ID support authentication and provide authorization data (what is the difference?), however, ED-ID provides much more authorization data (See the schemas for each: ED-Auth, ED-ID). Below are some criteria to help you decide which system will work best for you. Please note you may have to ask the vendor of your product whether it has some of the functionality listed below.

Use ED-Auth if:

  • Your application can support LDAP over SSL, or startTLS (an LDAP version 3 function)

AND

  • Your application requires extremely fast authentication responses

OR

  • Your application only requires knowledge of a person's basic affiliation (faculty, staff, student, etc.) for authorization decisions

Use ED-ID if:

  • Your application can support SSL or TLS with client certificates

AND

  • Your application requires more information about a person than simply their affiliation

OR

  • Your application requires access to information about which groups a person is a member of

OR

  • Your application requires that the directory keep an explicit list of people who are authorized to use your service

Some applications may even be able to support the usage of both directories. If this is the case, it is strongly recommended that you use ED-Auth for authentication and use ED-ID for the lookup of data pertaining to a person. This guarantees fast response times on authentication requests and allows access to all the information about a person that has been collected for placement in the directory.

Primary Affiliations vs. Standard Affiliations

The following information is taken from the Person Affiliations Explained document. Please refer to this document for more information.

Primary Affiliations

The eduPersonPrimaryAffiliation attribute is an attribute used to communicate, to other institutions, the most basic affiliation a person has with Virginia Tech. This attribute is used in conjunction with systems like Shibboleth* to allow other universities to make authorization decisions about this person. This attribute should NEVER be used by internal Virginia Tech systems for purposes of authorization, it is strictly meant as an external, to VT, facing attribute.

Standard Affiliations

The eduPersonAffiliation attribute gives all the affiliations a person associated with Virginia Tech has with the university. This attribute is meant to be used by internal applications, and will often be used in authorization logic. It is vitally important to realize that this attribute can, and almost always will, have more than one value, which is a change from the current affiliation tracking systems.

A Bit About SASL

Unlike the other LDAP directories (ED-Auth and ED-Lite), ED-ID credentials are not passwords but instead are digital certificates. SASL EXTERNAL, the SASL mechanism used with ED-ID, allows ED-ID to derive a service's DN from the certificate as well as authenticate the service given the certificate. Thus, you will see that DNs are not required on bind by the service, nor is any special credential presented. This makes it appear as if the service is doing an anonymous bind when in fact it is not. The use of SASL EXTERNAL in conjunction with TLS client certificate authentication gives us an extremely strong form of authentication to ED-ID.

ED-ID Services

What are ED-ID Services?

ED-ID Services are privileged accounts that are allowed to see person data in ED-ID. ED-ID Services are given access to person data with permissions that correspond to person attributes. These permissions allow each ED-ID Service to have a specialized set of user data that it is able to see. For instance, it is possible to have an ED-ID Service that is only capable of seeing email information, while another ED-ID Service could see only information about peoples' names. Further service permission enhancements are on the horizon. Additionally, an ED-ID Service with enough permission will be able to see confidential and suppressed information. ED-ID Services are an extremely powerful way to look up person information at Virginia Tech.

What can ED-ID Services see?

ED-ID Services can see:

  • Their own entry by searching on ou=services,dc=vt,dc=edu with the filter '(objectclass=*)'.
  • All users in the ou=people,dc=vt,dc=edu tree.
  • User attributes that they have been approved to see (corresponds to the viewablePersonAttribute in the ED-ID Service's entry).
  • Entries in ou=addresses,dc=vt,dc=edu if they have the viewablePersonAttribute value address.
  • Group data (in development)

Java Applications

Installing the Unlimited Strength JCE Policy Files

The key sizes of the VT Root CA certificates are too large to be used by the Java keytool implementation.
In order to work around these problems the Unlimited Strength JCE Policy Files must be installed.

  1. Download the policy files for the version of your JVM
  2. Unzip the archive and copy US_export_policy.jar and local_policy.jar to $JAVA_HOME/jre/lib/security

Creating a Java keystore

Java uses keystores for all SSL negotiations.
These instructions detail how to create a keystore with your client certificate and private key.

The key sizes of the VT Root CA certificates are too large to be used by the Java keytool implementation. In order to work around these problems the Bouncy Castle Keystore type must be used.

  1. Convert your client certificate into DER format:
    openssl x509 -outform DER -in service.crt -out service.crt.der
    
  2. Convert your private key into DER format:
    openssl rsa -outform DER -in service.key -out service.key.der
    
  3. Import your client certificate into a Bouncy Castle keystore
    • Execute the keystore script that is bundled with the EDLdap libs:
      bin/keystore.sh import-keypair service.crt.der service.key.der edid.keystore service
      
    • Windows users should use the keystore.bat script
  4. Delete the DER formats since your private key is not encrypted in this form
  5. To verify the integrity of the edid.keystore file , execute the following command:
    bin/keystore.sh list edid.keystore
    

Using JNDI to Access ED-ID

The following code sample demonstrates how to query ED-ID via SSL client authentication to bind as an ED-ID service user mentioned in the CN of the X.509 client certificate.

Requirements:

  1. JDK 1.5 or greater
  2. ED-ID service user on the certificate must have access to the attributes mentioned in the example, namely givenname, surname, and displayname
  3. edid.keystore on $CLASSPATH (see creating a Java keystore)

The sample code may be executed by a command like the following:

java \
-Djavax.net.ssl.keyStore=/path/to/edid.keystore \
-Djavax.net.ssl.keyStoreType=BKS \
-Djavax.net.ssl.keyStorePassword=changeit \
EdIdTlsExample '(uupid=serac)'

Download JNDI example

import java.io.IOException;
import java.util.Hashtable;
import java.util.ArrayList;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
 
public class EdIdTlsExample
{
  public static void main(String[] args) {
    if (args.length < 1) {
      System.err.println("USAGE: EdIdTlsExample query");
      return;
    }
    String hostName = "ldap://ed-dev.middleware.iad.vt.edu:10389";
    String baseDn = "ou=People,dc=vt,dc=edu";
    String query = args[0];
    String[] returnAttributes = {
      "givenname",
      "surname",
      "displayName"
    };
 
    // Initial JNDI environment setup
    Hashtable env = new Hashtable();
    env.put(Context.INITIAL_CONTEXT_FACTORY,
        "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, hostName);
    env.put("java.naming.ldap.version", "3");
    LdapContext ctx = null;
    StartTlsResponse tls = null;
    try {
      ctx = new InitialLdapContext(env, null);
 
      // Authentication must be performed over a secure channel
      tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
      tls.negotiate();
 
      // Authenticate via SASL EXTERNAL mechanism using client X.509
      // certificate contained in JVM keystore
      ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "EXTERNAL");
      ctx.reconnect(null);
 
      // Perform search for privileged attributes under authenticated context
      SearchControls controls = new SearchControls();
      controls.setReturningAttributes(returnAttributes);
      controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
      System.out.println("Executing query "+query);
      NamingEnumeration ne = ctx.search(baseDn, query, controls);
      while (ne.hasMore()) {
        SearchResult sr = (SearchResult) ne.next();
        System.out.println("Result DN " + sr.getNameInNamespace());
        NamingEnumeration attributes = sr.getAttributes().getAll();
        while (attributes.hasMore()) {
          Attribute attr = (Attribute) attributes.next();
          System.out.println(attr.getID());
          NamingEnumeration values = attr.getAll();
          while (values.hasMore()) {
            System.out.println("\t" + values.next());
          }
        }
      }
    } catch (IOException e) {
      System.err.println("TLS negotiation error:");
      e.printStackTrace();
    } catch (NamingException e) {
      System.err.println("JNDI error:");
      e.printStackTrace();
    } finally {
      if (tls != null) {
        try {
          // Tear down TLS connection
          tls.close();
        } catch (IOException e) {
          System.err.println("Error tearing down TLS connection.");
        }
      }
      if (ctx != null) {
        try {
          // Close LDAP connection
          ctx.close();
        } catch (NamingException e) {
          System.err.println("Error closing JNDI context.");
        }
      }
    }
  }
}

Using Middleware's LDAP Library (EDLdap)

Requirements:

  1. JDK 1.5 or greater
  2. edid.keystore on $CLASSPATH (see creating a Java keystore)

The sample code may be executed by a command like the following:

java -cp /path/to/edldap.jar:. \
-Djavax.net.ssl.keyStore=/path/to/edid.keystore \
-Djavax.net.ssl.keyStoreType=BKS \
-Djavax.net.ssl.keyStorePassword=changeit \
EdIdLibTest

Download EDLdap Example

Lines 1-3 import classes
Lines 14-16 create filter
Line 17 create EdId ojbect
Lines 18-19 search on filter and print out the entries

1  import java.util.Iterator;
2  import edu.vt.middleware.ldap.LdapUtil;
3  import edu.vt.middleware.ldap.ed.EdId;
 
4  /**
5   * <p>
6   * EdIdLibTest provides a test for the EdId class.
7   * </p>
8   */
9  public final class EdIdLibTest
10 {
11   public void doTest()
12     throws Exception
13   {
14     final String uupid = "UUPID";
15
16     final String query = "uupid="+uupid;
17     final EdId id = new EdId();
18     final Iterator i = id.search(query, null);
19     LdapUtil.print(i, System.out);
20   }
21
22   public static void main(final String[] args)
23     throws Exception
24   {
25     final EdIdLibTest test = new EdIdLibTest();
26     test.doTest();
27   }
28 }

EDLdap Maven Integration

The EDLdap module may be included in Java projects with a Maven build system by including the following in the project pom.xml file:

...
<dependencies>
  <dependency>
      <groupId>edu.vt.middleware</groupId>
      <artifactId>edldap</artifactId>
      <version>2.0</version>
  </dependency>
<dependencies>
...
<repositories>
  <repository>
    <id>middleware.repo</id>
    <url>http://middleware.vt.edu/maven2</url>
  </repository>
</repositories>
...

IMPORTANT
If you filter resources in your build, be careful not to filter binary files like the ED-ID keystore. If you see strange authentication errors or NullPointerException errors thrown on operations that perform client authentication, it may be a sign that the ED-ID keystore has been inadvertently corrupted by resource filtering. We are aware of at least two occurrences of this problem at the time of writing, and both were capable developers with Maven experience. It happens, so doublecheck that the build does not filter binary files.

Spring Framework Configuration

The Middleware EDLdap Libraries may be used in conjunction with the Spring LDAP LdapContextSource object to connect to ED-ID in a way that is natural for Spring applications. In order for the following Spring context configuration example to work, the following conditions must be met:

  • EDLdap libraries must be on application classpath.
  • ED-ID keystore file edid.keystore must exist at root of application classpath, e.g. WEB-INF/classes/edid.keystore for a Java servlet.
<!-- Define LDAP context source pointing at ED-ID -->
<bean id="edIdContextSource"
  class="org.springframework.ldap.core.support.LdapContextSource">
  <property name="pooled" value="true" />
  <property name="baseEnvironmentProperties">
    <map>
      <entry
        key="java.naming.ldap.factory.socket"
        value="edu.vt.middleware.ldap.ed.EdIdTLSSocketFactory" />
      <entry
        key="java.naming.security.protocol"
        value="SSL" />
      <!-- Use SASL EXTERNAL authentication -->
      <entry
        key="java.naming.security.authentication"
        value="EXTERNAL" />
    </map>
  </property>
  <property name="urls">
    <list>
      <value>ldaps://id.directory.vt.edu</value>
    </list>
  </property>
</bean>

Common Problems

javax.naming.AuthenticationNotSupportedException: [LDAP: error code 7 - SASL(-4): no mechanism available: ]

You did not successfully negotiate client authentication, most likely because your keystore could not be found.

  • Verify that the directory containing edid.keystore is in your $CLASSPATH
  • Add the -Djavax.net.debug=ssl switch when starting your JVM and verify the correct keystore is being used
  • Ensure the edid.keystore is not a symbolic link (e.g. ln -s edid-cas.bks edid.keystore); we have seen cases where symbolic links are not handled correctly.

java.security.UnrecoverableKeyException: no match

You need to install the Unlimited Strength JCE jars.

javax.naming.AuthenticationNotSupportedException: EXTERNAL

Java 1.5

You do not have the SASL security provider installed.

  • Add security.provider.7=com.sun.security.sasl.Provider to $JAVA_HOME/jre/lib/security/java.security

Troubleshooting Certificate Problems

The edldap/edldap.sh script provided in the bin directory of the unpacked Middleware EDLdap Libraries is an indispensible tool for troubleshooting ED-ID certificate problems. To perform ED-ID operations with this tool, copy the keystore containing your ED-ID certificate to $EDLDAP_HOME/edid.keystore, where $EDLDAP_HOME is the location where the library archive was unpacked.

1 # The following code executes a simple LDAP query against the development
2 # instance of ED-ID using the ED-ID certificate in $EDLDAP_HOME/edid.keystore
3 # as a credential.
4
5 cd $EDLDAP_HOME
6 bin/edldap.sh edid-dev -query uupid=dhawes

If you are interested in particular attributes, you can specify a space-delimited list of attributes after the LDAP filter:

1 # Query to display only the values of the uid and uupid attributes.
4
5 cd $EDLDAP_HOME
6 bin/edldap.sh edid-dev -query uupid=dhawes uid uupid

C/C++ Applications

A C/C++ LDAP library supporting LDAP with the startTLS extension and SASL support is required in order to connect to ED-ID. Instructions for compiling such a library can be found at Appendix: Compiling OpenLDAP Libraries These instructions use the OpenLDAP C LDAP library. You must make sure this library is in your applications library path. Please see Appendix: OpenLDAP and Certificates for information on how to set up your environment for SSL/TLS.

Download C Example

Lines 1-2 include necessary headers
Lines 6-20 declare necessary variables
Lines 22-23 set the creds as an empty string (SASL negotiation)
Lines 25-30 initialize the LDAP connection
Lines 32-39 set the version to LDAPv3, and startTLS
Lines 41-46 perform a SASL EXTERNAL bind
Lines 48-60 search on the filter
Lines 62-71 get the first entry and its DN
Lines 73-98 print out all attributes and their values
Lines 100-105 perform an LDAP compare operation (typical authorization with ED-ID)
Lines 106-109 clean up

Example compilation:

gcc -I/usr/local/openldap/include -L/usr/local/openldap/lib -o ed-id ed-id.c -lldap
1   #include <stdio.h>
2   #include <ldap.h> 
3    
4   int main(int argc, char* argv[])
5   {   
6       LDAP* ldap;   
7       LDAPMessage *result, *entry;
8       BerElement* ber; 
9       char *HOST_NAME = "id.directory.vt.edu";
10      int PORT_NUMBER = 389;
11      char *filter = "(uupid=UUPID)"; 
12      char *attrs[] = { NULL };
13      char *base = "ou=People,dc=vt,dc=edu";
14      char *attribute = NULL; 
15      char **values = NULL;
16      char *dn = NULL; 
17      char *cmpAttr = "eduPersonAffiliation";
18      char *cmpVal  = "VT-ACTIVE-MEMBER";
19      int i, resultCode, version;
20      struct berval cred;
21    
22      cred.bv_val = "";
23      cred.bv_len = strlen(cred.bv_val)*sizeof(char);
24
25      /* Initialize an LDAP connection. */
26      if( (ldap = ldap_init(HOST_NAME, PORT_NUMBER)) == NULL)
27      {
28          perror("ldap_init");
29          return 1;
30      }
31
32      /* Set the version number so that we may use startTLS. */
33      version = LDAP_VERSION3;
34      ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
35
36      if(ldap_start_tls_s(ldap, NULL, NULL) != LDAP_SUCCESS)
37      {
38          ldap_perror(ldap, "ldap_start_tls_s");
39      }
40
41      resultCode = ldap_sasl_bind_s(ldap, NULL, "EXTERNAL", &cred, NULL,
42                                    NULL, NULL);
43      if(resultCode != LDAP_SUCCESS)
44      {
45          ldap_perror(ldap, "ldap_sasl_bind_s");
46      }
47
48      resultCode = ldap_search_ext_s(ldap, base, LDAP_SCOPE_SUBTREE,
49                                     filter, attrs, 0, NULL, NULL,
50                                     NULL, LDAP_NO_LIMIT, &result);
51      if(resultCode != LDAP_SUCCESS)
52      {
53          /* another way to print errors...
54          fprintf(stderr, "ldap_search_ext_s: %s\n", 
55                  ldap_err2string(resultCode)); 
56          */
57          ldap_perror(ldap, "ldap_search_ext_s");
58          ldap_unbind_ext_s(ldap, NULL, NULL);
59          return 1;
60      }
61
62      entry = ldap_first_entry(ldap, result);
63      if(entry != NULL)
64          dn = ldap_get_dn(ldap, entry);
65      else
66      {
67          printf("search on filter: %s returned no entries\n", filter);
68          ldap_msgfree(result);
69          ldap_unbind_ext_s(ldap, NULL, NULL);
70          return 1;
71      }
72  
73      /* print out all attribute/value pairs */
74      if(entry != NULL)
75      {
76          printf("\ndn: %s\n", dn);
77          /* Iterate through each attribute in the entry. */
78         for( attribute = ldap_first_attribute(ldap, entry, &ber);
79              attribute != NULL;
80              attribute = ldap_next_attribute(ldap, entry, ber))
81         {
82             /*For each attribute, print the name and values.*/
83             values = ldap_get_values(ldap, entry, attribute);
84             if( values != NULL)
85             {
86                 for(i = 0; values[i] != NULL; i++)
87                 {
88                     printf("%s: %s\n", attribute, values[i]);
89                 }
90                 ldap_value_free(values);
91             }
92             ldap_memfree(attribute);
93         }
94         if(ber != NULL)
95         {
96             ber_free(ber, 0);
97         }
98      }
99
100     /* see if user has a specific affiliation */
101     resultCode = ldap_compare_s(ldap, dn, cmpAttr, cmpVal);
102     if(resultCode == LDAP_COMPARE_TRUE)
103         printf("ldap_compare_s: %s has %s=%s\n", dn, cmpAttr, cmpVal);
104     else
105         printf("ldap_compare_s: %s does not have %s=%s\n", dn, cmpAttr, cmpVal); 
106     ldap_msgfree(result);
107     ldap_memfree(dn);
108     ldap_unbind_ext_s(ldap, NULL, NULL);
109     return 0;
110 }

Note that in addition to setting up the OpenLDAP Library for certificates (Appendix: OpenLDAP and Certificates), you can do this in the code directly:

char *cacertfile = "/path/to/cachain.pem";
char *clientcertfile = "/path/to/clientcertfile.pem";
char *clientkeyfile = "/path/to/clientkeyfile.pem"
ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, (void *) cacertfile);
ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, (void *) clientcertfile);
ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, (void *) clientkeyfile);

WinLDAP C Applications (Windows)

This example uses the native Windows LDAP API (WinLDAP) to connect to ED-ID. Similar code could probably be compiled as a COM object or DLL for use with .Net or VB. Pay special attention to the notes section at the beginning of the example.

Download WinLDAP Example

Lines 24-28 include necessary headers
Lines 37-49 declare variables necessary for example
Lines 56-62 initialize the LDAP connection
Lines 64-70 set LDAPv3 and ensure SSL in on
Lines 73-81 connect to ED-ID
Lines 85-90 perform a SASL EXTERNAL bind
Lines 93-100 search on the filter, get the first entry, and grab its DN
Lines 103-128 print out all viewable attributes and their values for the person
Lines 132-138 determine if the person has the specified affiliation
Lines 140-141 clean up

1   /**
2    * winldap-edid.c
3    * This code is an example of how to connect to ED-ID, do a 
4    * SASL EXTERNAL bind with a client certificate, search for a user,
5    * print out the user's attributes, and then determine if the user
6    * has the proper affiliation specified.  This is but the tip of the 
7    * iceberg for authorization that can be done with ED-ID.  
8    * 
9    * Notes: * You must have a client certificate that has been issued
10   *          by the VT Middleware CA to connect to ED-ID.
11   *        * You must have a service entry that corresponds to your
12   *          client certificate to be able to view entries
13   *        * You must have imported the VTCA chain into the 
14   *          Windows keystore before this code will work properly.
15   *          This is available at http://www.middleware.vt.edu/pubs/vt-cachain.pem, 
16   *          or click on "immediate installation" and run the .exe at
17   *          http://www.pki.vt.edu/download/ie6.html.  This will 
18   *          automatically install the CA for you.
19   *        * Your client certificate must also be in the Windows 
20   *          keystore (see Appendix for instructions)
21   *        * You must link this against wldap32.lib
22   */
23
24  #include <windows.h>
25  #include <ntldap.h>
26  #include <winldap.h>
27  #include <stdio.h>
28  #include <winber.h>
29
30  /**
31   * Do a SASL EXTERNAL bind, search for the user with the supplied UUPID,
32   * print all attributes for the person, determine if the person has the
33   * specified affiliation.
34   */
35  int main(int argc, char* argv[])
36  {
37      LDAP* ld = NULL;
38      INT retVal = 0;
39      PCHAR pHost = "id.directory.vt.edu";
40      int port = 636;
41      char* base = "ou=people,dc=vt,dc=edu";
42      char* filter = "(uupid=UUPID)";
43      LDAPMessage *result, *entry;
44      char *dn;
45      char *cmpAttr = "eduPersonAffiliation";
46      char *cmpVal  = "VT-ACTIVE-MEMBER";
47      struct berval cred;
48      struct berval *servercredp;
49      ULONG version = LDAP_VERSION3;
50
51      cred.bv_val = "";
52      cred.bv_len = strlen(cred.bv_val)*sizeof(char);
53  
54      printf("\nConnecting to host \"%s\" ...\n",pHost);
55  
56      // Create an LDAP session.
57      ld = ldap_sslinit(pHost, port, 1);
58      if (ld == NULL)
59      {
60          printf( "ldap_sslinit failed with 0x%x.\n",GetLastError());
61          return -1;
62      }
63
64      // Specify version 3; the default is version 2.
65      printf("Setting Protocol version to 3.\n");
66      retVal = ldap_set_option(ld,
67                               LDAP_OPT_PROTOCOL_VERSION,
68                               (void*)&version);
69
70      retVal = ldap_set_option(ld,LDAP_OPT_SSL,LDAP_OPT_ON);
71 
72      // Connect to the server.
73      retVal = ldap_connect(ld, NULL);
74
75      if(retVal == LDAP_SUCCESS)
76          printf("ldap_connect succeeded \n");
77      else
78      {
79          printf("ldap_connect failed with 0x%x.\n",retVal);
80          return 1;
81      }
82
83      // Perform a SASL EXTERNAL bind.  The CN of your client certificate
84      // will be your service's UUSID
85      retVal = ldap_sasl_bind_s(ld, "", "EXTERNAL" , &cred, NULL, NULL, &servercredp);
86
87      if(retVal != LDAP_SUCCESS)
88          printf("ldap_sasl_bind_s failed with 0x%x\n", retVal);
89      else
90          printf("ldap_sasl_bind_s succeeded\n");
91
92      // Search for a person by UUPID
93      retVal = ldap_search_s(ld, base, LDAP_SCOPE_SUBTREE, filter, NULL, NULL, &result);
94
95      if(retVal != LDAP_SUCCESS)
96          printf("ldap_search_s failed with 0x%x.\n",retVal);
97
98      // Get the first entry and its DN
99      entry = ldap_first_entry(ld, result);
100     dn = ldap_get_dn(ld, entry);
101
102     // Print out all viewable attributes for the person
103     if(entry != NULL)
104     {
105         char *attribute;
106         BerElement *ber;
107         char **values;
108 
109         for(attribute = ldap_first_attribute(ld, entry, &ber);
110             attribute != NULL;
111             attribute = ldap_next_attribute(ld, entry, ber))
112         {
113             if((values = ldap_get_values(ld, entry, attribute)) != NULL)
114             {
115                 for(int i = 0; values[i] != NULL; i++)
116                 {
117                     printf("%s: %s\n", attribute, values[i]);
118                 }
119                 ldap_value_free(values);
120             }
121             ldap_memfree(attribute);
122         }
123
124         if(ber != NULL)
125         {
126             ber_free(ber, 0);
127         }
128     }
129
130     ldap_msgfree(result);
131
132     // Determine if the person has the specified affiliation
133     retVal = ldap_compare_s(ld, dn, cmpAttr, cmpVal);
134
135     if(retVal != LDAP_COMPARE_TRUE)
136         printf("ldap_compare_s failed with 0x%x.\n",retVal);
137     else
138         printf("\n%s == %s", cmpAttr, cmpVal);
139
140     ldap_memfree(dn);
141     ldap_unbind_s(ld);
142     return 0;
143 }

.NET Applications

Prerequisites

  1. .NET Framework 2.0 or higher. (System.DirectoryServices was introduced in that version.)
  2. A PKCS#12 file containing your ED-ID certificate/key pair must be installed into the Personal certificate store of the Local Machine. See http://technet.microsoft.com/en-us/library/bb727068.aspx for more information on certificate management in Windows.
using System.Security.Cryptography.X509Certificates;
using System.DirectoryServices.Protocols;
 
...
 
string ldapHost = "id.directory.vt.edu";
int ldapPort = 636;
// The following should be the uusid of your ED-ID service
string certCN = "YOUR_UUSID"
string ldapBase = "ou=people,dc=vt,dc=edu"
string ldapQuery = "uupid=SOME_UUPID";
 
// Create connection and attempt to bind and search
LdapConnection conn = null;
try
{
  conn = new LdapConnection(
    new LdapDirectoryIdentifier(ldapHost, ldapPort),
    null,
    AuthType.External);
  // VT Enterprise Directory requires LDAPv3
  conn.SessionOptions.ProtocolVersion = 3;
  conn.SessionOptions.SecureSocketLayer = true;
 
  // Look up client cert in Local Machine store by subject CN
  conn.SessionOptions.QueryClientCertificate =
    delegate(LdapConnection c, byte[][] trustedCAs)
    {
      X509Store lmStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
      lmStore.Open(OpenFlags.ReadOnly);
      // Uncomment the following lines to help diagnose cert problems
      //Console.WriteLine();
      //Console.WriteLine("Available certificates in Local Machine store:");
      //foreach (X509Certificate cert in lmStore.Certificates)
      //{
      //  Console.WriteLine("   " + cert.Subject);
      //}
      X509Certificate2Collection clientCerts = lmStore.Certificates.Find(
        X509FindType.FindBySubjectName, certCN, true);
      if (clientCerts.Count == 0)
      {
        throw new ArgumentException("Cannot find certificate " + certCN);
      }
      return clientCerts[0];
    };
  conn.Bind();
 
  // The 4th parameter, attributeList, is omitted to indicate all available attributes
  SearchResponse response = (SearchResponse)conn.SendRequest(
    new SearchRequest(ldapBase, ldapQuery, SearchScope.Subtree));
  Console.WriteLine();
  Console.WriteLine(response.Entries.Count + " entries found:");
  foreach (SearchResultEntry entry in response.Entries)
  {
    Console.WriteLine("   " + entry.DistinguishedName);
    foreach (String name in entry.Attributes.AttributeNames)
    {
      Console.Write("      " + name + "=");
      int n = 0;
      foreach(object value in entry.Attributes[name].GetValues(typeof(string)))
      {
        if (n++ > 0)
        {
          Console.Write(',');
        }
        Console.Write(value);
      }
      Console.WriteLine();
    }
  }
}
finally
{
  if (conn != null)
  {
    conn.Dispose();
  }
}

Perl Applications

To connect to ED-ID with Perl you will need the following Perl modules: Net::SSLeay, IO::Socket::SSL, Convert::BER, Authen::SASL, and Net::LDAP. These modules can be found on the CPAN website. For your convenience, a link to the current versions are supplied in the resource appendix.

Note that certain versions of perl-ldap can cause various errors, including SASL errors. Be sure to use the latest version, 0.34.

Download Perl Example

Lines 3-4 use required modules
Lines 7-16 declare variables
Line 17 set up SASL ojbect
Line 20 create a new LDAP
Lines 22-27 start TLS
Lines 29-31 perform a SASL EXTERNAL bind
Lines 33-34 search on the filter
Line 46 print the entry
Lines 48-59 get the entry and do an LDAP compare operation
Line 61 disconnect from ED-ID

1  #!/usr/bin/perl 
2
3  use Net::LDAP;
4  use Authen::SASL;
5  use strict;
6
7  my $server = "id.directory.vt.edu";
8  my $port = 389;
9  my $dn = "";
10 my $cafile = "/PATH/TO/ED_ID/CA_file.pem";
11 my $clientcert = "/PATH/TO/ED_ID/clientcert.pem";
12 my $clientkey = "/PATH/TO/ED_ID/clientkey.pem";
13 my $base = "ou=people,dc=vt,dc=edu";
14 my $filter = "(uupid=UUPID)";
15 my $attr = "eduPersonAffiliation";
16 my $value = "VT-ACTIVE-MEMBER";
17 my $sasl = Authen::SASL->new(mechanism => 'EXTERNAL',
18                              callback  => { pass => '', user => ''});
19
20 my $ldap = Net::LDAP->new($server, port => $port, version => 3) or die $@;
21
22 my $mesg = $ldap->start_tls(verify     => 'require',
23                             cafile     => $cafile,
24                             ciphers    => 'RC4-SHA',
25                             clientcert => $clientcert,
26                             clientkey  => $clientkey);
27 $mesg->code && die $mesg->error;
28
29 $mesg = $ldap->bind(dn   => '',
30                     sasl => $sasl);
31 $mesg->code && die $mesg->error;
32
33 $mesg = $ldap->search(base => $base, filter => $filter);
34 $mesg->code && die $mesg->error;
35
36 # print specific attribute
37 #$entry = $mesg->entry(0); 
38 #if($entry) 
39 #{ 
40 #    print $entry->get_value('edupersonprimaryaffiliation')."\n"; 
41 #    my $ref = $entry->get_value('edupersonaffiliation', asref => 1); 
42 #    foreach(@{$ref}){ print $_."\n"; } 
43 #} 
44
45 # print all attributes for the entry
46 foreach my $entry ($mesg->all_entries) { $entry->dump; }
47
48 my $entry = $mesg->entry(0);
49 $mesg = $ldap->compare($entry,
50                        attr => $attr,
51                        value => $value);
52 if($mesg->code == 6) # error code 6 is LDAP_COMPARE_TRUE
53 {
54     print $entry->dn.": $attr == $value\n";
55 }
56 else
57 {
58     print $entry->dn.": $attr != $value\n";
59 }
60
61 $ldap->unbind;

Python Applications

This example uses python-ldap to communicate with ED-ID. Since python-ldap is a wrapper around the OpenLDAP libraries, OpenLDAP, OpenSSL, and Cyrus SASL are required for this example to work. Certificates can be set up according to Appendix: OpenLDAP and Certificates. See Appendix: Compiling OpenLDAP Libraries for help with compiling the OpenLDAP library. See the INSTALL document bundled with python-ldap for installation instructions (hint: modify setup.cfg to give OpenLDAP, OpenSSL, and Cyrus SASL include and library paths).

Download Python Example

Line 3 import the ldap modules
Lines 5-9 declare variables
Line 12 initialize the connection
Line 13 set LDAPv3
Line 14 startTLS on the connection
Line 15 perform a SASL EXTERNAL bind
Line 16 search on the filter
Line 17 get the results from the search
Line 18 get the DN
Line 21 print out the entire entry
Lines 22-25 determine if the entry has the specified eduPersonAffiliation

1  #!/usr/bin/python
2
3  import ldap,ldap.sasl
4
5  base = "ou=People,dc=vt,dc=edu"
6  scope = ldap.SCOPE_SUBTREE
7  searchFilter = "uupid=UUPID"
8  attribute = "eduPersonAffiliation"
9  value = "VT-ACTIVE-MEMBER";
10
11 try:
12     edid = ldap.initialize("ldap://id.directory.vt.edu:389")
13     edid.protocol_version=ldap.VERSION3
14     edid.start_tls_s()
15     edid.sasl_interactive_bind_s("", ldap.sasl.external())
16     results = edid.search(base, scope, searchFilter, None)
17     resultType, result_data = edid.result(results, 1)
18     dn = result_data[0][0]
19     # barf out all user data for first entry returned (not pretty)
20     print dn
21     print result_data[0][1]
22     if edid.compare_s(dn, attribute, value) == 0:
23         print dn, attribute, "!=", value
24     else:
25         print dn, attribute, "==", value
26 except ldap.LDAPError,e:
27     print str(e)

PHP Applications

This example uses PHP to connect to ED-ID. You must compile PHP with LDAP for this example to work (–with-ldap and –with-ldap-sasl). See Appendix: Compiling OpenLDAP Libraries for instructions on installing OpenLDAP libraries.

Download PHP Example

Lines 3-8 create and initialize variables
Lines 10-13 get a connection and do a SASL EXTERNAL bind
Lines 16-18 search for a user
Lines 21-29 print out the user's entry
Lines 30-37 check for affiliation

1  <?php
2  
3  $host = 'ldap://id.directory.vt.edu';
4  $baseDn = 'ou=People,dc=vt,dc=edu';
5  $userfield = 'uupid';
6  $pid = "UUPID";
7  $compareAttr = 'eduPersonAffiliation';
8  $compareValue = 'VT-ACTIVE-MEMBER';
9
10 $ldap = ldap_connect($host);
11 ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
12 ldap_start_tls($ldap);
13 ldap_sasl_bind($ldap, null, null, "EXTERNAL");
14
15 if (isset($ldap) && $ldap != '') {
16     /* search for pid dn */
17     $result = @ldap_search($ldap, $baseDn, $userfield.'='.$pid);
18     if ($result != 0) {
19         $entries = ldap_get_entries($ldap, $result);
20         for ($i = 0; $i < $entries["count"]; $i++) {
21             $dn = $entries[$i]["dn"];
22             print "dn: $dn\n";
23             /* print out attributes and values */
24             for ($j = 0; $j < $entries[$i]["count"]; $j++) {
25                 $attrib = $entries[$i][$j];
26                 for ($k = 0; $k < $entries[$i][$attrib]["count"]; $k++) {
27                     print $attrib.": ".$entries[$i][$attrib][$k]."\n";
28                 }
29             }
30             /* check for affiliation */
31             print("\nDoes user ".$compareAttr."=".$compareValue.": ");
32             $cmp = ldap_compare($ldap, $dn, $compareAttr, $compareValue);
33             if($cmp == true) {
34                 print("YES\n");
35             } else {
36                 print("NO\n");
37             }
38         }
39         
40         ldap_free_result($result);
41     } else {
42         print('Error occured searching the LDAP');
43     }
44     ldap_close($ldap);
45 } else {
46     print('Could not connect to LDAP at '.$host);
47 }
48 ?>

Ruby Applications

To use this script to connect to ED-Auth you must have the ruby-ldap module compiled against the OpenLDAP and OpenSSL libraries. See Appendix: Compiling OpenLDAP Libraries.

Download Ruby Example

1  #!/usr/bin/ruby
2  # Simple ED-ID script; requires ruby-ldap built against OpenLDAP (with SASL)
3  # and OpenSSL.  Add error checking at your own discretion.
4
5  require 'ldap'
6
7  HOST, PORT = 'id.directory.vt.edu', 389
8  BASEDN = 'ou=People,dc=vt,dc=edu'
9  FILTER = 'uupid=UUPID'
10 ATTRIBUTE = 'eduPersonAffiliation'
11 VALUE = 'VT-ACTIVE-MEMBER'
12
13 dn =  nil
14 
15 ldap = LDAP::SSLConn.new(HOST, PORT, true)
16 ldap.sasl_quiet = true
17 ldap.sasl_bind('', 'EXTERNAL')
18
19 # search for affiliations
20 ldap.search(BASEDN, LDAP::LDAP_SCOPE_SUBTREE, FILTER,
21             ['eduPersonAffiliation']) do |entry|
22     dn = entry.get_dn
23     puts dn
24     puts entry.get_values(ATTRIBUTE)
25     end
26
27 # determine if we have a specific affiliation
28 if ldap.compare(dn, ATTRIBUTE, VALUE) == true
29     puts dn + ':  ' + ATTRIBUTE + ' == ' + VALUE
30 else
31     puts dn + ':  ' + ATTRIBUTE + ' != ' + VALUE
32 end
33
34 ldap.unbind

Appendix: OpenLDAP and Certificates

If the application you are using to connect to ED-ID or ED-Auth uses the OpenLDAP libraries, it may be necessary to set up the certificates for your client. This is done with the following directives: TLS_CACERT, TLS_CERT, and TLS_KEY. TLS_CACERT refers to the certificate chain used to verify the server. TLS_CERT and TLS_KEY refer to the client certificate and private key, respectively, that are used for TLS client authentication (only used for ED-ID). These directives can be set up in the following ways:

Download the VT Middleware CA chain file

1. Add the following to the OpenLDAP library's ldap.conf. This must be the ldap.conf that corresponds to the OpenLDAP library you are using for your application (note that there may be multiple ldap.conf files on your system, but only one will actually be used by a particular OpenLDAP library).

#############
# ldap.conf
TLS_CACERT      /path/to/cachain.pem
### only needed for ED-ID ###
### NOTE:  TLS_CERT and TLS_KEY are user-only attributes, meaning they 
###        cannot be in ldap.conf, but must be in .ldaprc or ldaprc or environment variables
TLS_CERT                /path/to/cert.pem 
TLS_KEY         /path/to/key.pem
#############

This sets up certificates system-wide for the OpenLDAP library.

2. Put the above directives in a file called ldaprc in the user's $HOME directory that is using the application. This will override the system-wide settings for the user.

3. Put the above directives in a file called .ldaprc in the user's $HOME directory that is using the application. This will override the system-wide settings for the user.

4. Put the above directives in a file called ldaprc in the user's current working directory ($PWD) that is using the application. This will override the system-wide settings for the user.

5. Set the following environment variables:

export LDAPCONF=/path/to/ldap.conf
OR
export LDAPRC=/path/to/ldaprc
OR
export LDAPTLS_CACERT=/path/to/cachain.pem
export LDAPTLS_CERT=/path/to/cert.pem
export LDAPTLS_KEY=/path/to/key.pem 

These directives are searched for in this order. The last directives set override any previous directives. For more information on this, see the ldap.conf manpage for OpenLDAP.

Appendix: Testing and Debugging With OpenLDAP

The ldapsearch program distributed with OpenLDAP is a useful tool for testing and debugging your custom application, particularly if it uses the OpenLDAP libraries at some level. Below is an example of how to connect to ED-ID with ldapsearch. Please refer to Appendix: OpenLDAP and Certificates for information on how to set up your server and client certificates.
NOTE: Your OpenLDAP libraries must be compiled with SSL and SASL support to do this.
Search for a person:

ldapsearch -H ldap://id.directory.vt.edu -Y EXTERNAL -Z \ 
           -b ou=people,dc=vt,dc=edu '(uupid=<uupid to search for>)' 

View the service account:

ldapsearch -H ldap://id.directory.vt.edu -Y EXTERNAL -Z \
           -b ou=services,dc=vt,dc=edu

Meaning of options:

  • -H – specifies the LDAP URL
  • -Y – specifies the SASL Mechanism (SASL EXTERNAL) when doing a SASL bind
  • -Z – specifies that we want to do a Start TLS request
  • -b – specifies the search base

If you are having difficulties with this example, you can turn debugging on by appending -d 1 to the end of the ldapsearch command. This will produce a wealth of debugging information.

Appendix: Compiling OpenLDAP Libraries

Many of the examples contained in this document depend on the OpenLDAP LDAP Libraries for their functionality. This includes the C, Net::LDAP, Python, and PHP examples as well as the standard LDAP utilities such as ldapsearch. This appendix exists to help you compile the libraries needed for your application to interface with ED-ID.

1. Download the OpenLDAP Software Distribution. The OpenLDAP stable Release is always the best choice to install.

2. Configure OpenLDAP with the following line:

./configure --prefix=/path/to/openldap \
            --enable-slapd=no  \
            --enable-slurpd=no \
            --with-tls \
            --with-cyrus-sasl

If your OpenSSL and/or Cyrus SASL libraries are not in the standard include and lib paths, you may need to run the following:

CPPFLAGS="-I/path/to/ssl/include -I/path/to/cyrus-sasl/include" \
LDFLAGS="-L/path/to/ssl/lib -L/path/to/cyrus-sasl/lib" \
./configure --prefix=/path/to/openldap \
            --enable-slapd=no \ 
            --enable-slurpd=no \ 
            --with-tls \
            --with-cyrus-sasl

3. make depend && make && make install

Appendix: LDAP Filters and Searching

TODO

Appendix: The Windows Keystore

TODO

Appendix: Resources

 
middleware/ed/edid/usage.txt · Last modified: 2009/11/10 13:47 by serac
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki