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:
- 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.
- Download the latest Signed Jar File from: http://www.bouncycastle.org/latest_releases.html
- Copy bcprov-jdkXX-XXX.jar to the $JAVA_HOME/jre/lib/ext directory.
- Rearrange the security providers in $JAVA-HOME/jre/lib/security/java.security:
- 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:
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.
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import edu.vt.middleware.ldap.ed.EdAuth;
/**
* <p>
* EdAuthLibTest provides a test for the EdAuth class.
* </p>
*/
public final class EdAuthLibTest
{
public void doTest()
throws Exception
{
String uupid = "UUPID";
String credential = "PASSWORD";
String filter = "AUTHORIZATION FILTER";
final EdAuth auth = new EdAuth();
if (auth.authenticateAndAuthorize(uupid, credential, filter)) {
System.out.println("Primary affiliation: "+
auth.getPrimaryAffiliation(uupid, credential));
final String[] affil = auth.getAffiliations(uupid, credential);
System.out.println("Affiliations: ");
for (int i = 0; i < affil.length; i++) {
System.out.println(" "+affil[i]);
}
} else {
System.out.println("Authentication or Authorization failed");
}
}
public static void main(final String[] args)
throws Exception
{
final EdAuthLibTest test = new EdAuthLibTest();
test.doTest();
}
}
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.
- 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <stdio.h>
#include <ldap.h>
int main(int argc, char* argv[])
{
LDAP* ldap;
LDAPMessage *result, *entry;
BerElement* ber;
char *HOST_NAME = "authn.directory.vt.edu";
int PORT_NUMBER = 389;
char *PASSWORD = "PASSWORD";
char *filter = "(uupid=UUPID)";
char *attrs[] = {"eduPersonPrimaryAffiliation",
"eduPersonAffiliation", NULL};
char *base = "ou=People,dc=vt,dc=edu";
char *attribute = NULL;
char **values = NULL;
char *dn = NULL;
char *cmpAttr = "eduPersonAffiliation";
char *cmpVal = "VT-ACTIVE-MEMBER";
int i, resultCode, version;
/* Initialize an LDAP connection. */
if( (ldap = ldap_init(HOST_NAME, PORT_NUMBER)) == NULL)
{
perror("ldap_init");
return 1;
}
/* Set the version number so that we may use startTLS. */
version = LDAP_VERSION3;
ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
if(ldap_start_tls_s(ldap, NULL, NULL) != LDAP_SUCCESS)
{
ldap_perror(ldap, "ldap_start_tls");
}
resultCode = ldap_search_ext_s(ldap, base, LDAP_SCOPE_SUBTREE,
filter, NULL, 0, NULL, NULL,
NULL, LDAP_NO_LIMIT, &result);
if(resultCode != LDAP_SUCCESS)
{
/* another way to print errors...
fprintf(stderr, "ldap_search_ext_s: %s\n",
ldap_err2string(resultCode));
*/
ldap_perror(ldap, "ldap_search_ext_s");
ldap_unbind_ext_s(ldap, NULL, NULL);
return 1;
}
entry = ldap_first_entry(ldap, result);
if(entry != NULL)
{
dn = ldap_get_dn(ldap, entry);
ldap_msgfree(result);
}
else
{
printf("search on filter: %s returned no entries\n", filter);
ldap_msgfree(result);
ldap_unbind_ext_s(ldap, NULL, NULL);
return 1;
}
/* Bind as a user. If PASSWORD is NULL, resultCode will be LDAP_UNWILLING_TO_PERFORM.
Always make sure password is not NULL. */
resultCode = ldap_simple_bind_s(ldap, dn, PASSWORD);
if(resultCode != LDAP_SUCCESS)
{
ldap_perror(ldap, "ldap_simple_bind_s");
ldap_memfree(dn);
return 1;
}
/* Search for the user. */
resultCode = ldap_search_ext_s(ldap, dn, LDAP_SCOPE_BASE,
filter, attrs, 0, NULL, NULL,
NULL, LDAP_NO_LIMIT, &result);
if( resultCode != LDAP_SUCCESS)
{
ldap_perror(ldap, "ldap_search_ext_s");
ldap_memfree(dn);
return 1;
}
/* Since we are doing a base search, there should be only one
matching entry */
entry = ldap_first_entry(ldap, result);
if(entry != NULL)
{
printf("\ndn: %s\n", dn);
/* Iterate through each attribute in the entry. */
for( attribute = ldap_first_attribute(ldap, entry, &ber);
attribute != NULL;
attribute = ldap_next_attribute(ldap, entry, ber))
{
/*For each attribute, print the name and values.*/
values = ldap_get_values(ldap, entry, attribute);
if( values != NULL)
{
for(i = 0; values[i] != NULL; i++)
{
printf("%s: %s\n", attribute, values[i]);
}
ldap_value_free(values);
}
ldap_memfree(attribute);
}
if(ber != NULL)
{
ber_free(ber, 0);
}
}
/* see if user has a specific affiliation */
resultCode = ldap_compare_s(ldap, dn, cmpAttr, cmpVal);
if(resultCode == LDAP_COMPARE_TRUE)
printf("ldap_compare_s: %s has %s=%s\n", dn, cmpAttr, cmpVal);
else
printf("ldap_compare_s: %s does not have %s=%s\n", dn, cmpAttr, cmpVal);
ldap_msgfree(result);
ldap_memfree(dn);
ldap_unbind_ext_s(ldap, NULL, NULL);
return 0;
}
Note that in addition to setting up the OpenLDAP Library for certificates (Appendix: OpenLDAP and Certificates), you can do this in the code directly:
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.
- 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* winldap-edauth.c
* This code is an example of how to connect to ED-Auth,
* search for an entry by a person's UUPID, bind as that
* UUPID, and then determine if that person is an active
* member at VT with the winldap library.
* This illustrates the basic authentication/
* authorization ED-Auth is to be used for.
*
* Notes: * You must have imported the VTCA chain into the
* Windows keystore before this code will work properly.
* This is available at
* https://4help.vt.edu/sp?id=kb_article&sysparm_article=KB0011371
* * You must link this against wldap32.lib
*/
#include <windows.h>
#include <ntldap.h>
#include <winldap.h>
#include <stdio.h>
/**
* Search for the DN with the supplied UUPID, bind as that DN with the
* given credentials, and then determine if the entry has the
* specified affiliation.
*/
int main(int argc, char* argv[])
{
LDAP* ld = NULL;
INT retVal = 0;
PCHAR pHost = "authn.directory.vt.edu";
int port = 636;
char* base = "ou=People,dc=vt,dc=edu";
char* filter = "(uupid=UUPID)";
LDAPMessage *result, *entry;
char *dn;
char *pass = "PASSWORD";
char *cmpAttr = "eduPersonAffiliation";
char *cmpVal = "VT-ACTIVE-MEMBER";
ULONG version = LDAP_VERSION3;
SecPkgContext_ConnectionInfo sslInfo;
LONG lv = 0;
printf("\nConnecting to host \"%s\" ...\n",pHost);
// Create an LDAP session.
ld = ldap_sslinit(pHost, port, 1);
if (ld == NULL)
{
printf( "ldap_sslinit failed with 0x%x.\n",GetLastError());
return -1;
}
// Specify version 3; the default is version 2.
printf("Setting Protocol version to 3.\n");
retVal = ldap_set_option(ld,
LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
if (retVal != LDAP_SUCCESS)
return 1;
retVal = ldap_set_option(ld,LDAP_OPT_SSL,LDAP_OPT_ON);
// Connect to the server.
retVal = ldap_connect(ld, NULL);
if(retVal == LDAP_SUCCESS)
printf("ldap_connect succeeded \n");
else
{
printf("ldap_connect failed with 0x%x.\n",retVal);
return 1;
}
// Search for the UUPID
retVal = ldap_search_s(ld, base, LDAP_SCOPE_SUBTREE, filter, NULL, NULL, &result);
if(retVal != LDAP_SUCCESS)
{
printf("ldap_search_s failed with 0x%x.\n",retVal);
return 1;
}
// Get the DN
entry = ldap_first_entry(ld, result);
dn = ldap_get_dn(ld, entry);
ldap_msgfree(result);
// Bind with current credentials.
printf("Binding with dn %s...\n", dn);
retVal = ldap_bind_s(ld,dn,pass,LDAP_AUTH_SIMPLE);
if (retVal != LDAP_SUCCESS)
printf("Bind failed with 0x%x.\n", retVal);
else
printf("Bind as %s succeeded.\n", dn);
// Determine if this person has the affiliation we want
retVal = ldap_compare_s(ld, dn, cmpAttr, cmpVal);
if(retVal != LDAP_COMPARE_TRUE)
printf("ldap_compare_s failed with 0x%x.\n",retVal);
else
printf("%s == %s", cmpAttr, cmpVal);
ldap_memfree(dn);
ldap_unbind_s(ld);
return 0;
}
.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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Net;
using System.DirectoryServices.Protocols;
using System.Security.Cryptography.X509Certificates;
using EdCommon;
namespace EdAuthTest
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1)
{
Console.WriteLine("USAGE: EdAuthTest uupid");
return;
}
string uupid = args[0];
string password = GetPassword(string.Format("Password for {0}:", uupid));
string ldapHost = EdConstants.ED_AUTH;
int ldapPort = 389;
string ldapFilter = "uupid=" + uupid;
Console.WriteLine(string.Format("Attempting to authenticate to {0} as {1}", ldapHost, uupid));
// Create connection and attempt to bind and search
LdapConnection conn = null;
try
{
conn = new LdapConnection(
new LdapDirectoryIdentifier(ldapHost, ldapPort),
null,
AuthType.Basic);
// VT Enterprise Directory requires LDAPv3
conn.SessionOptions.ProtocolVersion = 3;
// Must use custom hostname verification strategy due to DNS aliases
conn.SessionOptions.VerifyServerCertificate += new VerifyServerCertificateCallback(
new EdCertificateVerifier(ldapHost).VerifyCertificate);
// A QueryClientCertificateCallback is required based on our testing.
// We can only imagine that this handler is required when the SSL handshake
// contains a client certificate request directive.
conn.SessionOptions.QueryClientCertificate
delegate(LdapConnection c, byte[][] trustedCAs)
{
return null;
};
// Must conduct initial search over TLS connection to overcome suppressed PIDs
conn.SessionOptions.StartTransportLayerSecurity(null);
// Bind anonymously for initial search
conn.Bind();
// Search for the DN of the user with given uupid
SearchResponse response = (SearchResponse)conn.SendRequest(
new SearchRequest(EdConstants.SEARCH_BASE, ldapFilter, SearchScope.Subtree, "dn"));
if (response.Entries.Count == 0)
{
throw new ArgumentException("Cannot find DN for uupid=" + uupid);
}
string dn = response.Entries[0].DistinguishedName;
Console.WriteLine("Found user DN " + dn);
// Rebind as authenticated user
conn.Bind(new NetworkCredential(dn, password));
// 4th parameter, attributeList, is omitted to indicate all available attributes
response = (SearchResponse)conn.SendRequest(
new SearchRequest(dn, ldapFilter, SearchScope.Base));
// Print attributes of entry
SearchResultEntry entry = response.Entries[0];
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();
}
}
catch (Exception e)
{
Console.WriteLine("Application error: \n" + e);
}
finally
{
if (conn != null)
{
conn.Dispose();
}
}
}
/// <summary>
/// Lightly adapted from http://msdn.microsoft.com/en-us/library/ms733131.aspx.
/// </summary>
static string GetPassword(string prompt)
{
Console.WriteLine(prompt);
string password = "";
ConsoleKeyInfo info = Console.ReadKey(true);
while (info.Key != ConsoleKey.Enter)
{
if (info.Key != ConsoleKey.Backspace)
{
password += info.KeyChar;
info = Console.ReadKey(true);
}
else if (info.Key == ConsoleKey.Backspace)
{
if (!string.IsNullOrEmpty(password))
{
password = password.Substring
(0, password.Length - 1);
}
info = Console.ReadKey(true);
}
}
for (int i = 0; i < password.Length; i++)
Console.Write("*");
Console.WriteLine();
return password;
}
}
}
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.
- 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#!/usr/bin/perl
use Net::LDAP;
use strict;
my $server = "authn.directory.vt.edu";
my $port = 389;
my $dn = "";
my $pass = "PASSWORD";
my $cafile = "/PATH/TO/ED_AUTH/CA_file.pem";
my $base = "ou=People,dc=vt,dc=edu";
my $filter = "(uupid=UUPID)";
my $ldap = Net::LDAP->new($server, port => $port, version => 3) or die $@;
my $mesg = $ldap->start_tls(verify => 'require',
cafile => $cafile,
ciphers => 'AES256-SHA256', sslversion => 'tlsv1_2');
$mesg->code && die $mesg->error;
$mesg = $ldap->search(base => $base, filter => $filter);
$mesg->code && die $mesg->error;
my $entry = $mesg->entry(0);
if($entry)
{
$dn = $entry->dn;
}
$mesg = $ldap->bind( dn => $dn, password => $pass);
$mesg->code && die $mesg->error;
$mesg = $ldap->search(base => $base, filter => $filter,
attrs => ['eduPersonPrimaryAffiliation',
'eduPersonAffiliation']);
$mesg->code && die $mesg->error;
$entry = $mesg->entry(0);
if($entry)
{
print $entry->get_value('edupersonprimaryaffiliation')."\n";
my $ref = $entry->get_value('edupersonaffiliation', asref => 1);
foreach(@{$ref}){ print $_."\n"; }
}
# print all attributes for the entry
#foreach $entry ($mesg->all_entries) { $entry->dump; }
$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).
- 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/python
import ldap
base = "ou=People,dc=vt,dc=edu"
scope = ldap.SCOPE_SUBTREE
searchFilter = "uupid=UUPID"
password = "PASSWORD"
attribute = "eduPersonAffiliation";
affiliation = "VT-ACTIVE-MEMBER"
try:
# initialize the ldap connection
edauth = ldap.initialize("ldap://authn.directory.vt.edu:389")
edauth.protocol_version=ldap.VERSION3
edauth.start_tls_s()
results = edauth.search(base, scope, searchFilter, None)
resultType, result_data = edauth.result(results, 0)
if result_data:
dn = result_data[0][0]
edauth.simple_bind_s(dn, password)
if edauth.compare_s(dn, attribute, affiliation) == 0:
print dn, "does not have", attribute, "=", affiliation
else:
print dn, "has", attribute, "=", affiliation
else:
print "UUPID not found"
except ldap.LDAPError,e:
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.
- 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
$host = 'ldap://authn.directory.vt.edu';
$baseDn = 'ou=People,dc=vt,dc=edu';
$userfield = 'uupid';
$pid = 'UUPID';
$credential = 'PASSWORD';
$attr = 'eduPersonAffiliation';
$value = 'VT-ACTIVE-MEMBER';
$groupAttr = 'groupMembership';
$group = 'uugid=department.staff,ou=Groups,dc=vt,dc=edu';
/*ldap will bind anonymously, make sure we have some credentials*/
if (isset($pid) && $pid != '' && isset($credential)) {
$ldap = ldap_connect($host);
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
if (!@ldap_start_tls($ldap)) {
print('Could not start TLS');
} else if (isset($ldap) && $ldap != '') {
/* search for pid dn */
$result = @ldap_search($ldap, $baseDn, $userfield.'='.$pid, array('1.1'));
if ($result != 0) {
$entries = ldap_get_entries($ldap, $result);
$principal = $entries[0]['dn'];
if (isset($principal)) {
/* bind as this user */
if (@ldap_bind($ldap, $principal, $credential)) {
print("Authenticate success\n");
/* determine if the user has the $attr */
if(@ldap_compare($ldap, $principal, $attr, $value) === true) {
print("$principal has $attr = $value\n");
} else {
print("$principal does not have $attr = $value\n");
}
/* determine if the user is in the $group */
if(@ldap_compare($ldap, $principal, $groupAttr, $group) === true) {
print("$principal is a member of $group\n");
} else {
print("$principal is not a member of $group\n");
}
/* Do a base search on the dn to view all
eduPersonAffilation(s). */
$r = ldap_read($ldap, $principal, $userfield.'='.$pid);
$e = ldap_first_entry($ldap, $r);
$attrs = ldap_get_attributes($ldap, $e);
print("$attr: \n");
for($i = 0; $i < $attrs[$attr]['count'];
$i++)
{
print("\t".$attrs[$attr][$i]."\n");
}
ldap_free_result($r);
} else {
print('Authenticate failure');
}
} else {
print('User not found in LDAP');
}
ldap_free_result($result);
} else {
print('Error occured searching the LDAP');
}
ldap_close($ldap);
} else {
print('Could not connect to LDAP at '.$host);
}
}
?>
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:
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.
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:
- 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:
- Add the following directives, within a
directive, into your httpd.conf file for Apache:
- Download the VT CA Chain, also available from VT Middleware CA.
- Add the following line to your ldap.conf file, usually found at $OPENLDAP_HOME/etc/openldap/ldap.conf (see Appendix: OpenLDAP and Certificates for more ways to set up your trusted certificates):
Apache 2.0
- Compile OpenLDAP with SSL/TLS support (Appendix: Compiling OpenLDAP Libraries)
- Configure Apache with support for LDAP authentication, here is the configuration we use:
- Compile and install Apache with make and make install.
- Add the following configuration to your httpd.conf file:
Apache 2.2
- Compile OpenLDAP with SSL/TLS support (Appendix: Compiling OpenLDAP Libraries)
- Configure Apache with support for LDAP authentication, here is the configuration we use:
- Compile and install Apache with make and make install.
- Add the following configuration to your httpd.conf file:
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.
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.
EdAuthRealm also supports container-based authorization. Simply specify an
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.
- Download PAM LDAP.
- Configure pam_ldap: (see Appendix: Compiling OpenLDAP Libraries for OpenLDAP dependency)
- 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:
- Add the following to nslcd.conf:
- Have something similiar in your nsswitch.conf:
- 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.
- 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).
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:
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:
To bind as a person:
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:
and place the following line in it:
Next, place the Virginia Tech Middleware Certificate Chain in the
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:
If your OpenSSL libraries are not in the standard include and lib paths, you may need to run the following:
- make depend && make && make install
Appendix: Resources
- ED-Auth Schema
- Virginia Tech Middleware Certificate Chain
- Virginia Tech CA Certificates Download Page
- Person Affiliations Explained
- Middleware LDAP Libraries
- JNDI Website
- JSSE Website
- OpenLDAP LDAP C Library
- NET::LDAP Perl module
- IO::Socket:SSL Perl module
- PHP Documentation
- PEAR Net_LDAP
- auth_ldap
- mod_ssl
- OpenSSL