Introduction

ED-Auth exists to provide an easy means for applications to do simple PID/password authentication and role based authorization (student, faculty, staff, etc.). The ability to support LDAPv3 over SSL/TLS (ldaps or ldap with startTLS) is the only thing required for connecting to ED-Auth.

Authentication and Authorization

ED-Auth offers simple PID/password authentication plus the ability to authorize on a user’s eduPersonAffiliation with Virginia Tech.

Typical Authentication with ED-Auth

The basic steps to authenticate against ED-Auth are:

  • Collect PID and credential (password) from the user securely (ED Usage Requirements).
  • Bind anonymously and search for the uupid (PID) (search base: ou=People,dc=vt,dc=edu)
  • Retrieve the DN from the entry returned by the search
  • Perform a simple bind with the DN and credential

Typical Authorization with ED-Auth

The basic steps to authorize against ED-Auth are:

  • Bind as above
  • Peform an LDAP compare operation for the DN against the eduPersonAffiliation attribute, or search for the person using a filter that specifies the affiliation (e.g. (eduPersonAffiliation=VT-ACTIVE-MEMBER)), or search for the person and cycle through the eduPersonAffiliations, looking for a match. (compare is recommended)

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, however, ED-ID provides much more authorization data. 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 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

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.

Java Applications

SSL Prerequisites for Java Applications

Required JDK Configuration

The following steps are required for all Java applications that talk to ED-Auth. Only JDK versions 1.5 and greater are supported.

  • Download the VT Root CA in PEM format
  • Import this certificate into your cacerts truststore:
keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -file $PATH_TO/cacert.pem
  • Note that all SSL negotiations used by this JVM will trust the VT Root CA by default.
  • Alternatively, per-application certificate trust can be configured using the javax.net.ssl.trustStore system property.
  • Download the policy files for the version of your JVM
  • Unzip the archive and copy US_export_policy.jar and local_policy.jar to $JAVA_HOME/jre/lib/security

Optional JDK Configuration

The following steps are optional, but may provide additional functionality or better performance in some cases.

Installing the Bouncy Castle Provider

The BC cryptographic provider has a number of additional ciphers compared to the default JSSE provider. For example the AES cipher is provided in 3 flavors (AESEngine, AESFastEngine, AESLightEngine) to allow optimization for performance versus resource consumption, so the use of these features of BC can improve application performance accordingly. Additionally, the implementation of the common ciphers is arguably better.

security.provider.1=sun.security.provider.Sun
security.provider.2=com.sun.crypto.provider.SunJCE
security.provider.3=sun.security.jgss.SunProvider
security.provider.4=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.5=com.sun.net.ssl.internal.ssl.Provider
security.provider.6=com.sun.rsajca.Provider
  • If your JRE does not contain one of these providers, do not add it.
  • Note that the BouncyCastle provider must be before the Sun SSL provider and the Sun RSA provider, but after the Sun Security provider.

Debugging

If problems persist after following these instructions it is useful to verify that your JVM is configured properly. To do so, add the following switch when starting the JVM:

java -Djavax.net.debug=ssl ...

This will provide a trace of the SSL negoitation and verification of the keystores that the JVM is configured to use. This switch is not recommmended for production use.

Using ED-Auth with Applications that Use JNDI

The following example uses JNDI to communicate with ED-Auth over an explicit TLS connection.

Download ED-Auth JNDI example

 1 import java.io.IOException;
 2 import java.util.Hashtable;
 3 import java.util.ArrayList;
 4 import javax.naming.Context;
 5 import javax.naming.NamingEnumeration;
 6 import javax.naming.NamingException;
 7 import javax.naming.directory.Attribute;
 8 import javax.naming.directory.Attributes;
 9 import javax.naming.directory.BasicAttributes;
10 import javax.naming.directory.SearchResult;
11 import javax.naming.ldap.InitialLdapContext;
12 import javax.naming.ldap.LdapContext;
13 import javax.naming.ldap.StartTlsRequest;
14 import javax.naming.ldap.StartTlsResponse;
15 
16 public class EdAuthTlsExample
17 {
18   public static void main(String[] args) {
19     if (args.length < 2) {
20       System.err.println("USAGE: EdAuthSslExample uupid pass");
21       return;
22     }
23     String hostName = "ldap://authn.directory.vt.edu";
24     String baseDn = "ou=People,dc=vt,dc=edu";
25     String pid = args[0];
26     String credential = args[1];
27 
28     // Set up JNDI context for an anonymous search for uupid
29     Hashtable env = new Hashtable();
30     env.put(Context.INITIAL_CONTEXT_FACTORY,
31         "com.sun.jndi.ldap.LdapCtxFactory");
32     env.put(Context.PROVIDER_URL, hostName);
33     env.put("java.naming.ldap.version", "3");
34     LdapContext ctx = null;
35     StartTlsResponse tls = null;
36     try {
37       ctx = new InitialLdapContext(env, null);
38 
39       // Authentication must be performed over a secure channel
40       tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
41       tls.negotiate();
42 
43       // Perform anonymous lookup of user DN based on uupid attribute
44       BasicAttributes attrs = new BasicAttributes("uupid", pid);
45       String[] retAttrs = new String[] { "dn" };
46       NamingEnumeration ne1 = ctx.search(baseDn, attrs, retAttrs);
47       SearchResult pidSearchResult = (SearchResult) ne1.next();
48       if (pidSearchResult == null) {
49         System.out.println(pid+" not found in ED-Auth");
50         return;
51       }
52       String userDn = pidSearchResult.getNameInNamespace();
53       System.out.println("Found user DN "+userDn);
54 
55       // Authenticate the user and search for privileged attributes
56       // belonging to authenticated user
57       ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
58       ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDn);
59       ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, credential);
60       ctx.reconnect(null);
61       NamingEnumeration ne2 = ctx.search(baseDn, attrs);
62       while (ne2.hasMore()) {
63         SearchResult sr = (SearchResult) ne2.next();
64         NamingEnumeration attributes = sr.getAttributes().getAll();
65         while (attributes.hasMore()) {
66           Attribute attr = (Attribute) attributes.next();
67           System.out.println(attr.getID());
68           NamingEnumeration values = attr.getAll();
69           while (values.hasMore()) {
70             System.out.println("\t" + values.next());
71           }
72         }
73       }
74     } catch (IOException e) {
75       System.err.println("TLS negotiation error:");
76       e.printStackTrace();
77     } catch (NamingException e) {
78       System.err.println("JNDI error:");
79       e.printStackTrace();
80     } finally {
81       if (tls != null) {
82         try {
83           // Tear down TLS connection
84           tls.close();
85         } catch (IOException e) {
86           System.err.println("Error tearing down TLS connection.");
87         }
88       }
89       if (ctx != null) {
90         try {
91           // Close LDAP connection
92           ctx.close();
93         } catch (NamingException e) {
94           System.err.println("Error closing JNDI context.");
95         }
96       }
97     }
98   }
99 }

As an alternative to explicitly managing TLS, for ED-Auth it is sufficient to specify “ssl” for the Context.SECURITY_PROTOCOL initial environment parameter to establish a TLS connection. Since ED-Auth is intended for the authentication/authorization use case where all operations need to be performed over a secure channel, an implicit TLS connection may be preferable.

Using ED-Auth via Middleware’s LDAP Library

Requires the Middleware EDLdap Library

Download the Middleware EDLdap example

  • Line 1 import the EdAuth library
  • Lines 13-15 initialize variables
  • Line 17 initialize ED-Auth object
  • Line 18 authenticate and authorize the user
  • Lines 19-25 get affiliations and print them out
 1 import edu.vt.middleware.ldap.ed.EdAuth;
 2 
 3 /**
 4  * <p>
 5  * EdAuthLibTest provides a test for the EdAuth class.
 6  * </p>
 7  */
 8 public final class EdAuthLibTest
 9 {
10   public void doTest()
11     throws Exception
12   {
13     String uupid = "UUPID";
14     String credential  = "PASSWORD";
15     String filter = "AUTHORIZATION FILTER";
16 
17     final EdAuth auth = new EdAuth();
18     if (auth.authenticateAndAuthorize(uupid, credential, filter)) {
19       System.out.println("Primary affiliation: "+
20                          auth.getPrimaryAffiliation(uupid, credential));
21       final String[] affil = auth.getAffiliations(uupid, credential);
22       System.out.println("Affiliations: ");
23       for (int i = 0; i < affil.length; i++) {
24         System.out.println("  "+affil[i]);
25       }
26     } else {
27       System.out.println("Authentication or Authorization failed");
28     }
29   }
30 
31   public static void main(final String[] args)
32     throws Exception
33   {
34     final EdAuthLibTest test = new EdAuthLibTest();
35     test.doTest();
36   }
37 }

C/C++ Applications

A C/C++ LDAP library supporting LDAP with the startTLS extension is required in order to connect to ED-Auth. 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-21 initialize variables used to connect and perform operations
  • Lines 23-28 initialize LDAP connection
  • Lines 30-37 make the LDAP connection use TLS
  • Lines 39-51 search for the DN for this user
  • Lines 53-65 get the DN from the search result
  • Lines 67-75 authenticate the user
  • Lines 77-86 get this person s record
  • Lines 88-115 retrieve and print the affiliation information for this user
  • Lines 117-122 determine if the user has a certain affiliation
  • Lines 124-127 close the LDAP connection and free used memory
Example compilation:

gcc -I/usr/local/openldap/include -L/usr/local/openldap/lib -o ed-auth ed-auth.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 = "authn.directory.vt.edu";
 10     int PORT_NUMBER = 389;
 11     char *PASSWORD = "PASSWORD";
 12     char *filter = "(uupid=UUPID)";
 13     char  *attrs[] = {"eduPersonPrimaryAffiliation",
 14                      "eduPersonAffiliation", NULL};
 15     char *base = "ou=People,dc=vt,dc=edu";
 16     char *attribute = NULL;
 17     char **values = NULL;
 18     char *dn = NULL;
 19     char *cmpAttr = "eduPersonAffiliation";
 20     char *cmpVal  = "VT-ACTIVE-MEMBER";
 21     int i, resultCode, version;
 22 
 23     /* Initialize an LDAP connection. */
 24     if( (ldap = ldap_init(HOST_NAME, PORT_NUMBER)) == NULL)
 25     {
 26         perror("ldap_init");
 27         return 1;
 28     }
 29 
 30     /* Set the version number so that we may use startTLS. */
 31     version = LDAP_VERSION3;
 32     ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
 33 
 34     if(ldap_start_tls_s(ldap, NULL, NULL) != LDAP_SUCCESS)
 35     {
 36         ldap_perror(ldap, "ldap_start_tls");
 37     }
 38 
 39     resultCode = ldap_search_ext_s(ldap, base, LDAP_SCOPE_SUBTREE,
 40                                    filter, NULL, 0, NULL, NULL,
 41                                    NULL, LDAP_NO_LIMIT, &result);
 42     if(resultCode != LDAP_SUCCESS)
 43     {
 44         /* another way to print errors...
 45         fprintf(stderr, "ldap_search_ext_s: %s\n", 
 46                 ldap_err2string(resultCode)); 
 47         */
 48         ldap_perror(ldap, "ldap_search_ext_s");
 49         ldap_unbind_ext_s(ldap, NULL, NULL);
 50         return 1;
 51     }
 52 
 53     entry = ldap_first_entry(ldap, result);
 54     if(entry != NULL)
 55     {
 56         dn = ldap_get_dn(ldap, entry);
 57         ldap_msgfree(result);
 58     }
 59     else
 60     {
 61         printf("search on filter: %s returned no entries\n", filter);
 62         ldap_msgfree(result);
 63         ldap_unbind_ext_s(ldap, NULL, NULL);
 64         return 1;
 65     }
 66 
 67     /* Bind as a user. If PASSWORD is NULL, resultCode will be LDAP_UNWILLING_TO_PERFORM. 
 68        Always make sure password is not NULL. */
 69     resultCode = ldap_simple_bind_s(ldap, dn, PASSWORD);
 70     if(resultCode != LDAP_SUCCESS)
 71     {
 72         ldap_perror(ldap, "ldap_simple_bind_s");
 73         ldap_memfree(dn);
 74         return 1;
 75     }
 76 
 77     /* Search for the user. */
 78     resultCode = ldap_search_ext_s(ldap, dn, LDAP_SCOPE_BASE,
 79                                    filter, attrs, 0, NULL, NULL,
 80                                    NULL, LDAP_NO_LIMIT, &result);
 81     if( resultCode != LDAP_SUCCESS)
 82     {
 83         ldap_perror(ldap, "ldap_search_ext_s");
 84         ldap_memfree(dn);
 85         return 1;
 86     }
 87 
 88     /* Since we are doing a base search, there should be only one 
 89        matching entry */
 90     entry = ldap_first_entry(ldap, result);
 91     if(entry != NULL)
 92     {
 93         printf("\ndn: %s\n", dn);
 94         /* Iterate through each attribute in the entry. */
 95        for( attribute = ldap_first_attribute(ldap, entry, &ber);
 96             attribute != NULL;
 97             attribute = ldap_next_attribute(ldap, entry, ber))
 98        {
 99            /*For each attribute, print the name and values.*/
100            values = ldap_get_values(ldap, entry, attribute);
101            if( values != NULL)
102            {
103                for(i = 0; values[i] != NULL; i++)
104                {
105                    printf("%s: %s\n", attribute, values[i]);
106                }
107                ldap_value_free(values);
108            }
109            ldap_memfree(attribute);
110        }
111        if(ber != NULL)
112        {
113            ber_free(ber, 0);
114        }
115     }
116 
117     /* see if user has a specific affiliation */
118     resultCode = ldap_compare_s(ldap, dn, cmpAttr, cmpVal);
119     if(resultCode == LDAP_COMPARE_TRUE)
120         printf("ldap_compare_s: %s has %s=%s\n", dn, cmpAttr, cmpVal);
121     else
122         printf("ldap_compare_s: %s does not have %s=%s\n", dn, cmpAttr, cmpVal);
123 
124     ldap_msgfree(result);
125     ldap_memfree(dn);
126     ldap_unbind_ext_s(ldap, NULL, NULL);
127     return 0;
128 }

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";
ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, (void *) cacertfile);

WinLDAP C Applications (Windows)

This example uses the native Windows LDAP API WinLDAP to connect to ED-Auth. Similar code could probably be compiled as a COM object or DLL for use with .Net or VB.

Download WinLDAP Example

  • Lines 20-24 include necessary headers
  • Lines 33-47 initialize variables
  • Lines 51-57 initialize the LDAP connection
  • Lines 59-67 use LDAPv3 and SSL
  • Lines 69-78 connect to ED-Auth
  • Lines 80-87 search for the UUPID
  • Lines 89-92 get the DN
  • Lines 94-100 bind with the supplied credentials
  • Lines 102-107 determine if the person has the given affiliation
  • Lines 109-110 clean up
  1 /**
  2  * winldap-edauth.c
  3  * This code is an example of how to connect to ED-Auth,
  4  * search for an entry by a person's UUPID, bind as that
  5  * UUPID, and then determine if that person is an active
  6  * member at VT with the winldap library.  
  7  * This illustrates the basic authentication/
  8  * authorization ED-Auth is to be used for.
  9  *
 10  * Notes:  * You must have imported the VTCA chain into the 
 11  *           Windows keystore before this code will work properly.
 12  *           This is available at http://www.pki.vt.edu/developer/rootca.html, 
 13  *           or click on "immediate installation" and run the .exe at
 14  *           http://www.pki.vt.edu/download/ie6.html.  This will 
 15  *           automatically install the CA for you.
 16  *         
 17  *         * You must link this against wldap32.lib
 18  */
 19 
 20 #include <windows.h>
 21 #include <ntldap.h>
 22 #include <winldap.h>
 23 #include <stdio.h>
 24 
 25 /**
 26  * Search for the DN with the supplied UUPID, bind as that DN with the
 27  * given credentials, and then determine if the entry has the 
 28  * specified affiliation.
 29  */
 30 int main(int argc, char* argv[])
 31 {
 32     LDAP* ld = NULL;
 33     INT retVal = 0;
 34     PCHAR pHost = "authn.directory.vt.edu";
 35     int port = 636;
 36     char* base = "ou=People,dc=vt,dc=edu";
 37     char* filter = "(uupid=UUPID)";
 38     LDAPMessage *result, *entry;
 39     char *dn;
 40     char *pass = "PASSWORD";
 41     char *cmpAttr = "eduPersonAffiliation";
 42     char *cmpVal  = "VT-ACTIVE-MEMBER";
 43 
 44     ULONG version = LDAP_VERSION3;
 45     SecPkgContext_ConnectionInfo sslInfo;
 46     LONG lv = 0;
 47 
 48     printf("\nConnecting to host \"%s\" ...\n",pHost);
 49 
 50     // Create an LDAP session.
 51     ld = ldap_sslinit(pHost, port, 1);
 52     if (ld == NULL)
 53     {
 54         printf( "ldap_sslinit failed with 0x%x.\n",GetLastError());
 55         return -1;
 56     }
 57 
 58     // Specify version 3; the default is version 2.
 59     printf("Setting Protocol version to 3.\n");
 60     retVal = ldap_set_option(ld,
 61                              LDAP_OPT_PROTOCOL_VERSION,
 62                              (void*)&version);
 63     if (retVal != LDAP_SUCCESS)
 64         return 1;
 65 
 66     retVal = ldap_set_option(ld,LDAP_OPT_SSL,LDAP_OPT_ON);
 67 
 68     // Connect to the server.
 69     retVal = ldap_connect(ld, NULL);
 70 
 71     if(retVal == LDAP_SUCCESS)
 72         printf("ldap_connect succeeded \n");
 73     else
 74     {
 75         printf("ldap_connect failed with 0x%x.\n",retVal);
 76         return 1;
 77     }
 78 
 79     // Search for the UUPID
 80     retVal = ldap_search_s(ld, base, LDAP_SCOPE_SUBTREE, filter, NULL, NULL, &result);
 81 
 82     if(retVal != LDAP_SUCCESS)
 83     {
 84         printf("ldap_search_s failed with 0x%x.\n",retVal);
 85         return 1;
 86     }
 87 
 88     // Get the DN
 89     entry = ldap_first_entry(ld, result);
 90     dn = ldap_get_dn(ld, entry);
 91     ldap_msgfree(result);
 92 
 93     // Bind with current credentials. 
 94     printf("Binding with dn %s...\n", dn);
 95     retVal = ldap_bind_s(ld,dn,pass,LDAP_AUTH_SIMPLE);
 96     if (retVal != LDAP_SUCCESS)
 97         printf("Bind failed with 0x%x.\n", retVal);
 98     else
 99         printf("Bind as %s succeeded.\n", dn);
100 
101     // Determine if this person has the affiliation we want
102     retVal = ldap_compare_s(ld, dn, cmpAttr, cmpVal);
103     if(retVal != LDAP_COMPARE_TRUE)
104         printf("ldap_compare_s failed with 0x%x.\n",retVal);
105     else
106         printf("%s == %s", cmpAttr, cmpVal);
107 
108    ldap_memfree(dn);
109    ldap_unbind_s(ld);
110    return 0;
111 }

.NET Framework Applications (Windows)

The System.DirectoryServices.Protocols assembly available in .NET Framework 2.0 and later provides a convenient integration option for Windows applications based on the .NET Framework. A simple command-line authenticator is show below in C#.

  1 using System;
  2 using System.Net;
  3 using System.DirectoryServices.Protocols;
  4 using System.Security.Cryptography.X509Certificates;
  5 
  6 using EdCommon;
  7 
  8 namespace EdAuthTest
  9 {
 10   class Program
 11   {
 12     static void Main(string[] args)
 13     {
 14       if (args.Length < 1)
 15       {
 16         Console.WriteLine("USAGE: EdAuthTest uupid");
 17         return;
 18       }
 19       string uupid = args[0];
 20       string password = GetPassword(string.Format("Password for {0}:", uupid));
 21       string ldapHost = EdConstants.ED_AUTH;
 22       int ldapPort = 389;
 23       string ldapFilter = "uupid=" + uupid;
 24 
 25       Console.WriteLine(string.Format("Attempting to authenticate to {0} as {1}", ldapHost, uupid));
 26 
 27       // Create connection and attempt to bind and search
 28       LdapConnection conn = null;
 29       try
 30       {
 31         conn = new LdapConnection(
 32           new LdapDirectoryIdentifier(ldapHost, ldapPort),
 33           null,
 34           AuthType.Basic);
 35         // VT Enterprise Directory requires LDAPv3
 36         conn.SessionOptions.ProtocolVersion = 3;
 37 
 38         // Must use custom hostname verification strategy due to DNS aliases
 39         conn.SessionOptions.VerifyServerCertificate += new VerifyServerCertificateCallback(
 40           new EdCertificateVerifier(ldapHost).VerifyCertificate);
 41 
 42         // A QueryClientCertificateCallback is required based on our testing.
 43         // We can only imagine that this handler is required when the SSL handshake
 44         // contains a client certificate request directive.
 45         conn.SessionOptions.QueryClientCertificate
 46         delegate(LdapConnection c, byte[][] trustedCAs)
 47         {
 48           return null;
 49         };
 50 
 51         // Must conduct initial search over TLS connection to overcome suppressed PIDs
 52         conn.SessionOptions.StartTransportLayerSecurity(null);
 53 
 54         // Bind anonymously for initial search
 55         conn.Bind();
 56 
 57         // Search for the DN of the user with given uupid
 58         SearchResponse response = (SearchResponse)conn.SendRequest(
 59           new SearchRequest(EdConstants.SEARCH_BASE, ldapFilter, SearchScope.Subtree, "dn"));
 60 
 61         if (response.Entries.Count == 0)
 62         {
 63           throw new ArgumentException("Cannot find DN for uupid=" + uupid);
 64         }
 65         string dn = response.Entries[0].DistinguishedName;
 66         Console.WriteLine("Found user DN " + dn);
 67 
 68         // Rebind as authenticated user
 69         conn.Bind(new NetworkCredential(dn, password));
 70 
 71         // 4th parameter, attributeList, is omitted to indicate all available attributes
 72         response = (SearchResponse)conn.SendRequest(
 73           new SearchRequest(dn, ldapFilter, SearchScope.Base));
 74 
 75         // Print attributes of entry
 76         SearchResultEntry entry = response.Entries[0];
 77         foreach (String name in entry.Attributes.AttributeNames)
 78         {
 79           Console.Write("    " + name + "=");
 80           int n = 0;
 81           foreach (object value in entry.Attributes[name].GetValues(typeof(string)))
 82           {
 83             if (n++ > 0)
 84             {
 85               Console.Write(',');
 86             }
 87             Console.Write(value);
 88           }
 89           Console.WriteLine();
 90         }
 91       }
 92       catch (Exception e)
 93       {
 94         Console.WriteLine("Application error: \n" + e);
 95       }
 96       finally
 97       {
 98         if (conn != null)
 99         {
100           conn.Dispose();
101         }
102       }
103     }
104 
105     /// <summary>
106     /// Lightly adapted from http://msdn.microsoft.com/en-us/library/ms733131.aspx.
107     /// </summary>
108     static string GetPassword(string prompt)
109     {
110       Console.WriteLine(prompt);
111       string password = "";
112       ConsoleKeyInfo info = Console.ReadKey(true);
113       while (info.Key != ConsoleKey.Enter)
114       {
115         if (info.Key != ConsoleKey.Backspace)
116         {
117           password += info.KeyChar;
118           info = Console.ReadKey(true);
119         }
120         else if (info.Key == ConsoleKey.Backspace)
121         {
122           if (!string.IsNullOrEmpty(password))
123           {
124             password = password.Substring
125             (0, password.Length - 1);
126           }
127           info = Console.ReadKey(true);
128         }
129       }
130       for (int i = 0; i < password.Length; i++)
131         Console.Write("*");
132       Console.WriteLine();
133       return password;
134     }
135   }
136 }

The EdCertificateVerifier is available in a standalone library assembly, EdCommon, to facilitate use in real applications. A complete Visual Studio solution file containing the source for EdCommon and all .NET ED samples is available in the Middleware git repository.

Perl Applications

This document uses the NET::LDAP LDAP module, which in turn requires the IO::Socket::SSL module. If you choose to use a different LDAP module it must be able to support either LDAP over SSL (LDAPS) or LDAP with the startTLS extension.

Download Perl Example

  • Lines 3 import the Net::LDAP module
  • Lines 6-12 declare and initialize variables for use
  • Lines 14 setup connection to LDAP server
  • Lines 16-19 make connection use TLS
  • Lines 21-22 search for the user s DN
  • Lines 24-28 get person s DN from search result
  • Lines 30-31 authenticate user
  • Lines 33-36 search for the person s affiliation information
  • Lines 38-44 get affiliation information out of search result
  • Lines 49 close LDAP connection
 1 #!/usr/bin/perl 
 2 
 3 use Net::LDAP;
 4 use strict;
 5 
 6 my $server = "authn.directory.vt.edu";
 7 my $port = 389;
 8 my $dn = "";
 9 my $pass = "PASSWORD";
10 my $cafile = "/PATH/TO/ED_AUTH/CA_file.pem";
11 my $base = "ou=People,dc=vt,dc=edu";
12 my $filter = "(uupid=UUPID)";
13 
14 my $ldap = Net::LDAP->new($server, port => $port, version => 3) or die $@;
15 
16 my $mesg = $ldap->start_tls(verify => 'require',
17                             cafile => $cafile,
18                             ciphers    => 'AES256-SHA256', sslversion => 'tlsv1_2');
19 $mesg->code && die $mesg->error;
20 
21 $mesg = $ldap->search(base => $base, filter => $filter);
22 $mesg->code && die $mesg->error;
23 
24 my $entry = $mesg->entry(0);
25 if($entry)
26 {
27     $dn = $entry->dn;
28 }
29 
30 $mesg = $ldap->bind( dn => $dn, password => $pass);
31 $mesg->code && die $mesg->error;
32 
33 $mesg = $ldap->search(base => $base, filter => $filter,
34                       attrs => ['eduPersonPrimaryAffiliation',
35                                 'eduPersonAffiliation']);
36 $mesg->code && die $mesg->error;
37 
38 $entry = $mesg->entry(0);
39 if($entry)
40 {
41     print $entry->get_value('edupersonprimaryaffiliation')."\n";
42     my $ref = $entry->get_value('edupersonaffiliation', asref => 1);
43     foreach(@{$ref}){ print $_."\n"; }
44 }
45 
46 # print all attributes for the entry
47 #foreach $entry ($mesg->all_entries) { $entry->dump; }
48 
49 $ldap->unbind;

Python Applications

This example uses python-ldap to communicate with ED-Auth. Since python-ldap is a wrapper around the OpenLDAP libraries, OpenLDAP and OpenSSL 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 and OpenSSL include and library paths).

Download Python Example

  • Line 3 import python-ldap
  • Lines 5-10 initialize variables
  • Line 14 initialize thy LDAP connection
  • Lines 15-16 startTLS on the connection
  • Line 17 search for the user
  • Line 18 get result of search
  • Lines 20-21 get the DN from the search and bind as that user
  • Lines 22-25 determine if user has the specified eduPersonAffiliation (authorization)
 1 #!/usr/bin/python
 2 
 3 import ldap
 4 
 5 base = "ou=People,dc=vt,dc=edu"
 6 scope = ldap.SCOPE_SUBTREE
 7 searchFilter = "uupid=UUPID"
 8 password = "PASSWORD"
 9 attribute = "eduPersonAffiliation";
10 affiliation = "VT-ACTIVE-MEMBER"
11 
12 try:
13     # initialize the ldap connection
14     edauth = ldap.initialize("ldap://authn.directory.vt.edu:389")
15     edauth.protocol_version=ldap.VERSION3
16     edauth.start_tls_s()
17     results = edauth.search(base, scope, searchFilter, None)
18     resultType, result_data = edauth.result(results, 0)
19     if result_data:
20         dn = result_data[0][0]
21         edauth.simple_bind_s(dn, password)
22         if edauth.compare_s(dn, attribute, affiliation) == 0:
23             print dn, "does not have", attribute, "=", affiliation
24         else:
25             print dn, "has", attribute, "=", affiliation
26     else:
27         print "UUPID not found"
28 except ldap.LDAPError,e:
29     print str(e)

PHP Applications

This example uses PHP to connect to ED-Auth. You must compile PHP with LDAP for this example to work. See Appendix: Compiling OpenLDAP Libraries for instructions on installing OpenLDAP libraries.

You also need to make sure that certificates are set up properly. See Appendix: OpenLDAP and Certificates for instructions.

Download PHP Example

  • Lines 3-11 create and initialize variables
  • Lines 14-24 determine user’s DN
  • Lines 28-41 authenticate and authorize user
  • Lines 42-53 do a base search on the user’s DN and print an attribute (eduPersonAffiliation)
 1 <?php
 2      
 3  $host = 'ldap://authn.directory.vt.edu';
 4  $baseDn = 'ou=People,dc=vt,dc=edu';
 5  $userfield = 'uupid';
 6  $pid = 'UUPID';                                                               
 7  $credential = 'PASSWORD';
 8  $attr = 'eduPersonAffiliation';
 9  $value = 'VT-ACTIVE-MEMBER';
10  $groupAttr = 'groupMembership';
11  $group = 'uugid=department.staff,ou=Groups,dc=vt,dc=edu';
12  
13  /*ldap will bind anonymously, make sure we have some credentials*/
14  if (isset($pid) && $pid != '' && isset($credential)) {
15      $ldap = ldap_connect($host);
16      ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
17      if (!@ldap_start_tls($ldap)) {                                           
18          print('Could not start TLS');
19      } else if (isset($ldap) && $ldap != '') {
20          /* search for pid dn */
21          $result = @ldap_search($ldap, $baseDn, $userfield.'='.$pid, array('1.1'));               
22          if ($result != 0) {
23              $entries = ldap_get_entries($ldap, $result);                     
24              $principal = $entries[0]['dn'];
25              if (isset($principal)) {
26                      
27                  /* bind as this user */
28                  if (@ldap_bind($ldap, $principal, $credential)) {
29                      print("Authenticate success\n");
30                      /* determine if the user has the $attr */
31                      if(@ldap_compare($ldap, $principal, $attr, $value) === true) {              
32                         print("$principal has $attr = $value\n");
33                      } else {
34                         print("$principal does not have $attr = $value\n");
35                      }   
36                      /* determine if the user is in the $group */
37                      if(@ldap_compare($ldap, $principal, $groupAttr, $group) === true) {         
38                         print("$principal is a member of $group\n");
39                      } else {
40                         print("$principal is not a member of $group\n");
41                      }
42                      /* Do a base search on the dn to view all
43                         eduPersonAffilation(s). */
44                      $r = ldap_read($ldap, $principal, $userfield.'='.$pid);
45                      $e  = ldap_first_entry($ldap, $r);
46                      $attrs = ldap_get_attributes($ldap, $e);
47                      print("$attr: \n");
48                      for($i = 0; $i < $attrs[$attr]['count'];
49                          $i++)
50                      {
51                          print("\t".$attrs[$attr][$i]."\n");
52                      }
53                      ldap_free_result($r);
54                  } else {
55                      print('Authenticate failure');
56                  }
57              } else {
58                  print('User not found in LDAP');
59              }
60              ldap_free_result($result);
61          } else {
62              print('Error occured searching the LDAP');
63          }
64          ldap_close($ldap);
65      } else {
66          print('Could not connect to LDAP at '.$host);
67      }
68  }
69  ?>

note: if you are testing ldaps you must specify a fully qualified URL:

ldap_connect("ldaps://$hostname:$port");

you cannot use:

ldap_connect($hostname, $port);

See the PHP Documentation for ldap_connect().

Hosting Users

If your website runs on Hosting, you can only test authentication using the https://secure.hosting.vt.edu URL associated with your website.

A note about the magic_quotes_gpc option and web forms

If you have magic_quotes_gpc set to On in your php.ini, people with certain special characters (‘,”,\, for example) in their passwords will be unable to authenticate, as the characters will be escaped. Either set magic_quotes_gpc to Off or use stripslashes() to ensure that these people can authenticate. Note that Hosting currently has this option On.

PEAR Net_LDAP

Here is a simplistic example that shows how to bind with PEAR Net_LDAP:

Download PHP Example

 1 <?php
 2 
 3 require_once 'Net/LDAP.php';
 4 
 5 $host = 'directory.vt.edu';
 6 $port = 389;
 7 $base = 'ou=people,dc=vt,dc=edu';
 8 $uupid = 'UUPID';
 9 $pass = 'PASS';
10 
11 $config = array (
12     'basedn'    => $base,
13     'host'      => $host,
14     'port'      => $port,
15     'starttls'  => true);
16 
17 $ldap = Net_LDAP::connect($config);
18 
19 if(Net_LDAP::isError($ldap))
20 {
21     die('Could not connect to LDAP server: '.$ldap->getMessage());
22 }
23 
24 $filter = "uupid=$uupid";
25 
26 $search = $ldap->search($base, $filter);
27 
28 if(Net_LDAP::isError($search))
29 {
30     die('Could not fetch entry: '.$search->getMessage());
31 }
32 
33 $entry = $search->entries();
34 
35 if($entry)
36 {
37     $dn = $entry[0]->dn();
38 
39     $result = $ldap->bind($dn,  $pass);
40     if(Net_LDAP::isError($result))
41     {
42         die($result->getMessage()."\n");
43     }
44     else
45     {
46         echo "Authenticate success\n";
47     }
48 }
49 else
50 {
51     echo "No entries found for uupid = $uupid\n";
52 }
53 
54 $ldap->done();
55 ?>

Ruby Applications

To use this class 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 # Simple ED-Auth library; requires ruby-ldap built
 2 # against OpenLDAP and OpenSSL.
 3 # Sam Stephenson <sams@vt.edu> 2004-06-17
 4 # Modified a bit by Brad Tilley < rtilley@vt.edu> 2006-12-13
 5 
 6 require 'ldap'
 7 
 8 class NotAuthenticatedError < Exception
 9 end
10 
11 class EdAuth
12     HOST, PORT = 'authn.directory.vt.edu', 389
13     DN, FILTER = 'ou=People,dc=vt,dc=edu', '(uupid=%s)'
14 
15     def initialize(pid, pass)
16         @ldap = LDAP::SSLConn.new(HOST, PORT, true)
17         @authenticated = false
18         @pid, @pass = pid, pass
19         @dn, @filter = DN, format(FILTER, pid)
20 
21         @authenticity = false
22         @pri_affil, @affil = nil, nil
23     end
24 
25     def authenticate
26         return @authenticity if @authenticated
27         begin
28             @ldap.search(@dn, LDAP::LDAP_SCOPE_ONELEVEL,
29                         @filter) {|c| @dn = c.get_dn}
30             @ldap.bind(@dn, @pass)
31             @authenticity = true
32         rescue LDAP::ResultError
33             @authenticity = false
34         ensure
35             @authenticated = true
36         end
37         @authenticity
38     end
39 
40     def get_primary_affiliation
41         query if @pri_affil.nil?
42     puts @pri_affil
43         @pri_affil
44     end
45 
46     def get_affiliations
47         query if @affil.nil?
48     puts @affil
49         @affil
50     end
51 
52     def close
53         begin
54             @ldap.unbind
55         rescue LDAP::InvalidDataError
56         end
57     end
58 
59     private
60     def query
61         raise NotAuthenticatedError unless @authenticated and
62         @authenticity
63         @ldap.search(@dn, LDAP::LDAP_SCOPE_SUBTREE, @filter,
64                     ['eduPersonPrimaryAffiliation',
65                     'eduPersonAffiliation']) do |entry|
66                     @pri_affil
67                     entry.get_values('edupersonprimaryaffiliation').shift
68                     @affil = entry.get_values('edupersonaffiliation')
69         end
70     end
71 end
72 
73 x = EdAuth.new('your_pid', 'your_password')
74 x.authenticate
75 x.get_primary_affiliation
76 x.get_affiliations
77 x.close

Using ED-Auth via Apache Modules

Please note you must accept PID/password credentials securely. Since these credentials are given to the Apache server using HTTP Basic Auth, this means that all your restricted resources must be served over an SSL (HTTPS) encrypted connection. Failure to do so is a violation of the ED Usage Requirements.

Apache 1.3

These instructions require the OpenSSL, mod_ssl, OpenLDAP, and auth_ldap libraries. Links to these are provided in the resource appendix. You must have these libraries available to Apache for this to work (See Appendix: Compiling OpenLDAP Libraries for help). These instructions do not describe how to compile Apache, OpenSSL, or mod_ssl. Please refer to the documentation included with those individual programs for compilation instructions.

  • Compile Apache with SSL support. Be sure to enable loadable module support.
  • Compile OpenLDAP with SSL/TLS support (Appendix: Compiling OpenLDAP Libraries)
  • Compile auth_ldap. Here is the configuration we use, obviously some of the paths may be different on your machine:
./configure --with-apxs=../apache/bin/apxs
                --with-ldap-sdk=openldap
                --with-sdk-headers=/usr/local/openldap/include
                --with-sdk-libs=/usr/local/openldap/lib
  • Compile and install via make and make install
  • Add the following directive into your httpd.conf file so that Apache loads the auth_ldap module:
LoadModule auth_ldap_module libexec/auth_ldap.so
  • Add the following directives, within a directive, into your httpd.conf file for Apache:
AuthLDAPURL ldap://authn.directory.vt.edu:389/ou=People,dc=vt,dc=edu?uupid 
AuthLDAPStartTLS on 
AuthType Basic 
AuthName "Virginia Tech ED-Auth (PID/pass)"  
require valid-user
TLS_CACERT /path/to/cert/download/above/cert.pem

Apache 2.0

CPPFLAGS=-I/path/to/openldap/include \ 
LDFLAGS=-L/path/to/openldap/lib \ 
./configure --enable-ssl \ 
            --with-ldap \ 
            --enable-ldap=shared \ 
            --enable-auth-ldap=shared \ 
            --prefix=/location/to/install/apache
  • Compile and install Apache with make and make install.
  • Add the following configuration to your httpd.conf file:
LDAPTrustedCA /path/to/CA.pem 
LDAPTrustedCAType BASE64_FILE 

<Directory  /some/directory > 
    AuthType Basic 
    AuthName "Virginia Tech ED-Auth (PID/pass)"  
    AuthLDAPURL  ldaps://authn.directory.vt.edu:636/ou=People,dc=vt,dc=edu?uupid  
    require valid-user 
</Directory>

Apache 2.2

CPPFLAGS=-I/path/to/openldap/include \ 
LDFLAGS=-L/path/to/openldap/lib \ 
./configure --enable-ssl \ 
            --with-ldap \ 
            --enable-ldap=shared \ 
            --enable-authnz-ldap=shared \ 
            --prefix=/location/to/install/apache
  • Compile and install Apache with make and make install.
  • Add the following configuration to your httpd.conf file:
LDAPTrustedGlobalCert CA_BASE64 /path/to/CA.pem 

<Directory  /some/directory > 
    AuthType Basic
    AuthBasicProvider ldap
    AuthzLDAPAuthoritative Off
    AuthName "Virginia Tech ED-Auth (PID/pass)"  
    AuthLDAPURL  ldaps://authn.directory.vt.edu:636/ou=People,dc=vt,dc=edu?uupid  
    require valid-user 
</Directory>

Falling Through to file-based Authentication

Sometimes it is desirable to use both ED-Auth and some form of local authentication together. The following is an example of how to use Apache’s password files.

LDAPTrustedGlobalCert CA_BASE64 /path/to/CA.pem 

<Directory  /some/directory > 
    AuthType Basic
    AuthBasicProvider ldap file
    AuthUserFile /path/to/apache.passwd
    AuthzLDAPAuthoritative Off
    AuthName "Virginia Tech ED-Auth (PID/pass)"  
    AuthLDAPURL  ldaps://authn.directory.vt.edu:636/ou=People,dc=vt,dc=edu?uupid  
    require valid-user 
</Directory>

The AuthBasicProvider directive sets the order of auth modules to attempt to use. In this case it uses file-based auth with apache.passwd if the user is not found in the LDAP.

Note: If you reverse the order of the modules in AuthBasicProvider, you will be able to override users that exist in the LDAP in the password file. This is probably not desirable, and you should ensure that the Apache password file is properly protected.

Tomcat Servlet Container Authentication to ED-Auth

The Tomcat Servlet container can be configured to use the EdAuthRealm provided by the Middleware EDLdap Library to provide container-based authentication and authorization.

Integration steps:

  • Copy the edldap.jar file to a directory on the container classpath ($TOMCAT_HOME/server/lib for Tomcat 5.x, $TOMCAT_HOME/lib for Tomcat 6.x)
  • Configure the $TOMCAT_HOME/conf/server.xml file similar to the sample below.
  • Create the file $TOMCAT_HOME/conf/edauth-users.xml that is formatted similarly to tomcat-users.xml; the password attribute for each user is not needed.
  • Restart Tomcat.

Template server.xml file for configuring an ED-Auth container authentication realm.

<Server port="8005" shutdown="SHUTDOWN">

  <Listener className="org.apache.catalina.core.AprLifecycleListener" />
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
            descriptors="/edu/vt/middleware/ldap/catalina/mbeans/mbeans-descriptors.xml"/>
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>

  <!-- Global JNDI resources -->
  <GlobalNamingResources>

    <!-- Add EdAuth Database as global resource -->
    <Resource name="EdAuthDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="EdAuth role database"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/edauth-users.xml" />

  </GlobalNamingResources>

  <!-- Define the Tomcat Stand-Alone Service -->
  <Service name="Catalina">

    <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 -->
    <Connector port="8080" maxHttpHeaderSize="8192"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" redirectPort="8443" acceptCount="100"
               connectionTimeout="20000" disableUploadTimeout="true" />

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8009" 
               enableLookups="false" redirectPort="8443" protocol="AJP/1.3" />

    <!-- Define the top level container in our container hierarchy -->
    <Engine name="Catalina" defaultHost="localhost">

      <!-- Because this Realm is here, an instance will be shared globally -->

      <!-- Add EdAuth Realm to this Engine -->
      <Realm className="edu.vt.middleware.ldap.catalina.realm.EdAuthRealm"/>

      <!-- Define the default virtual host
           Note: XML Schema validation will not work with Xerces 2.2.
       -->
      <Host name="localhost" appBase="webapps"
       unpackWARs="true" autoDeploy="true"
       xmlValidation="false" xmlNamespaceAware="false">

      </Host>
    </Engine>
  </Service>
</Server>

EdAuthRealm also supports container-based authorization. Simply specify an in the section of the application web.xml to authorize users based on their membership in a given ED group. The following example requires the authenticated user to be a member of the //middleware.staff// ED group to access protected resources.

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">
...
  <security-role>
    <description>ED group describing the administrative role.</description>
    <role-name>middleware.staff</role-name>
  </security-role>
...
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected Resources</web-resource-name>
      <url-pattern>/protected/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>middleware.staff</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
...
</web-app>

PAM LDAP

PAM LDAP gives Unix and Linux machines the ability to authenticate against an LDAP server such as ED-Auth. PAM is highly tunable and powerful, and allows administrators to determine how services (login, xdm, ssh, etc.) authenticate users. These instructions assume you are compiling from source, but you are free to use your favorite distribution’s package at your own risk.

./configure --prefix=/path/to/pam_ldap \
            --with-ldap=/path/to/openldap \
            --with-ldap-lib=openldap
  • make && make install
  • In your /etc/ldap.conf((Note that this is a pam_ldap specific file, and it not used by OpenLDAP.)), add the following:
base ou=People,dc=vt,dc=edu
uri ldap://authn.directory.vt.edu
ldap_version 3
pam_login_attribute uupid
ssl start_tls
tls_checkpeer yes
tls_cacertfile /path/to/cachain.pem
  • Add the following to nslcd.conf:
uri ldap://authn.directory.vt.edu
base dc=vt,dc=edu
ssl start_tls
tls_cacertdir /etc/openldap/cacerts
tls_cacertfile /etc/openldap/certs/vt-cachain.pem
filter passwd (objectclass=virginiaTechPerson)
map passwd uidNumber uid
map passwd uid uupid
map passwd gidNumber uid
map passwd homeDirectory "${homeDirectory:-/home/$uupid}"
map passwd loginShell "${loginShell:-/bin/bash}"
#filter group (objectClass=virginiaTechGroup)
#map    group  uniqueMember     member
#map    group  cn               uugid
#map    group  gidNumber        uid
  • Have something similiar in your nsswitch.conf:
...
passwd:     files ldap
shadow:     files ldap
group:      files ldap
netgroup:   files ldap
automount:  files ldap
...
  • Download vt-cachain.pem and put the file in the location you specified in step 4.
  • man pam and read up about the pam.conf configuration file, pam.d, and services.
  • Modify your service rules in /etc/pam.d accordingly, making sure the rules do what you think they do (!!!). (hint: the pam.d directory that comes with the pam_ldap distribution is a good place to look at rules. The login rules file works well.)

pGina Windows Authentication

pGina is a replacement for the authentication portion of Windows 2000/XP, and allows Windows users to authenticate against ED-Auth. This is especially useful in a lab or similar enviromment where PID/pass authentication is desired.

  • Import the VTCA chain into the Windows keystore. This is available at the VT Root CA site, or, alternatively, click on “immediate installation” and run the .exe at the VT Root CA site. This will automatically install the VTCA chain for you.
  • Download pGina and the LDAPAuth Plugin
  • Install pGina and the LDAPAuth Plugin.
  • Start up the pGina Configuration Tool, click on the Plugin tab, and put the path to the LDAPAuth Plugin for the Plugin Path.
  • Click on Configure and fill in the following:

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 Middeware CA chain file

  • 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.

  • 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.
  • 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.
  • 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.
  • 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 are examples for how to connect to ED-Auth. Please refer to Appendix: OpenLDAP and Certificates for information on how to set up certificates. NOTE: Your OpenLDAP libraries must be compiled with SSL support to do this.

To search for a person:

ldapsearch -H ldap://authn.directory.vt.edu -x -Z \
    -b ou=People,dc=vt,dc=edu '(uupid=<uupid to search for>)'

To bind as a person:

ldapsearch -H ldap://authn.directory.vt.edu -x -Z \ 
    -b ou=People,dc=vt,dc=edu \ 
    -D uid=<uid to bind as>,ou=People,dc=vt,dc=edu \ 
    -W '(uupid=<uupid to search for>)'

Options Key:

  • -H – specifies the LDAP URL
  • -x – specifies that we want to a simple bind (anonymous in this case)
  • -Z – specifies that we want to do Start TLS request
  • -b – specifies the search base for the search
  • -D – the DN of the user you want to bind as
  • -W – prompts the user for the bind password

Appendix: PHP, SSL, and Windows

To use LDAPS functionality in PHP on Windows you must create the following file:

c:\OpenLDAP\sysconf\ldap.conf

and place the following line in it:

TLS_CACERT c:\OpenLDAP\sysconf\certs\vt-cachain.pem

Next, place the Virginia Tech Middleware Certificate Chain in the

c:\OpenLDAP\sysconf\certs\

directory.

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, PHP, Ruby, and Apache 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-Auth.

  • Download the OpenLDAP Software Distribution. The OpenLDAP //stable// Release is always the best choice to install.
  • Configure OpenLDAP with the following line:
./configure --prefix=/path/to/openldap \
            --enable-slapd=no  \
            --enable-slurpd=no \
            --with-tls

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

CPPFLAGS=-I/path/to/ssl/include LDFLAGS=-L/path/to/ssl/lib \
./configure --prefix=/path/to/openldap \
            --enable-slapd=no \ 
            --enable-slurpd=no \ 
            --with-tls
  • make depend && make && make install

Appendix: Resources