|
end-to-end DIGEST-MD5 possible? |
|
Subject: end-to-end DIGEST-MD5 possible?
Author: dferrero
In response to: When and Why DIGEST-MD5 Authentication Does Not Work?
Posted on: 02/23/2012 04:40:24 PM
SteveHD: Thank you for this article. It actually helped me add support for DIGEST-MD5 to our product. Simpler than I thought - just needed to get the username in correct format.
I've been looking for a way to do end-to-end SASL LDAP Auth but haven't seen an API to do so. The current JNDI APIs seem to expect you to provide the username and password in plain-text format, then under the covers JNDI will perform the SASL encryption / Hash work.
In my scenario, a client app (which I do not have control over) wants to authenticate with my server application. My server application wants to allow these client apps to authenticate through AD / LDAP. In other words, I am trying to "pass-thru" the client's SASL auth request to AD / LDAP and based on the success of this bind, I allow the client to connect to my server app. Is this possible? If so how? If not, why not? :-)
It defeats the purpose of security if I have to force the client apps to use PLAIN / simple SASL and give me their password in clear-text in order for me to perform the bind on their behalf.
>
> On 06/30/2006 07:03:52 PM SteveHB wrote:
The Digest-MD5 mechanism is described in RFC 2831. In Digest-MD5, the LDAP server sends data that includes various authentication options that it is willing to support plus a special token to the LDAP client. The client responds by sending an encrypted response that indicates the authentication options that it has selected. The response is encrypted in such a way that proves that the client knows its password. The LDAP server then verifies the client's response.
As it can be seen, the major advantages of DIGEST-MD5 are: 1) prevent user password being sent across the Internet via clear text; 2) provide message integrity and confidentiality protection, after authentication
Apparently, the disadvantages are not trivial: Digest authentication requires that the authenticating agent (usually the server) store some data derived from the user's password in certain forms to be able to retrieve the value of:
H({ username-value,":", realm-value, ":", passwd }).
without directly exposing the user's password. This is the source of why DIGEST-MD5 does not work for a lot of cases. (discussed later)
The following example shows how a client performs authentication using Digest-MD5 to an LDAP server.
/**
*
* SaslDigestMD5JndiClient.java
* Sample code to explore how and when DIGEST-MD5 authentication works.
*
*/
import java.util.Hashtable;
import javax.naming.directory.*;
import javax.naming.*;
public class SaslDigestMD5JndiClient
{
public static void main (String[] args)
{
String bind_dn = "testuser";
String bind_password = "secret";
String init_url = "ldap://myserver.mydomain.com:389";
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, init_url);
// Set the authentication mechanism to be DIGEST-MD5
env.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5");
env.put(Context.SECURITY_PRINCIPAL, bind_dn);
env.put(Context.SECURITY_CREDENTIALS, bind_password);
/*
* other environment properties are:
* javax.security.sasl.qop:
* specifies list of qops: "auth", "auth-int", "auth-conf"
* auth -- authentication only;
* auth-int -- authentication & subsequent message's integrity check
* auth-conf -- authentication & subsequent message's
* confidentiality enforcement
* default is "auth"
* env.put("javax.security.sasl.qop", "auth");
* javax.security.sasl.strength
* specifies low/medium/high strength of encryption;
* default is all available ciphers [high,medium,low];
* high means des3 or rc4 (128); medium des or rc4-56; low is rc4-40.
* env.put("javax.security.sasl.strength","high");
* javax.security.sasl.maxbuf
* specifies max receive buf size; default is 65536
* javax.security.sasl.sendmaxbuffer
* specifies max send buf size; default is 65536
*/
DirContext ctx = null;
try {
// Create the initial directory context
ctx = new InitialDirContext(env);
} catch (Exception e) {
System.err.println("Authentication failed: " + e);
}
try{
// Create the search controls
SearchControls searchCtls = new SearchControls();
//Specify the attributes to return
String returnedAtts[]={"sn","givenName","mail"};
searchCtls.setReturningAttributes(returnedAtts);
//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
//specify the LDAP search filter
String searchFilter = "(&(objectClass=user)(mail=*))";
//Specify the Base for the search
String searchBase = "dc=mydomain,dc=com";
// Search for objects using the filter
NamingEnumeration results = ctx.search(searchBase,searchFilter,searchCtls);
//Loop through the search results
while (results.hasMoreElements()) {
SearchResult sr = (SearchResult)results.next();
System.out.println("dn: " + sr.getName());
Attributes attrs = sr.getAttributes();
System.out.println("attributes: " + attrs);
}
ctx.close();
} catch (NamingException e) {
System.err.println("Searching failed: " + e);
}
}
}
The above code has been testet against SunONE and Active Directory, testing scenarios will be discussed in very details but the failed outputs are very much the same like:
javax.naming.AuthenticationException: [LDAP: error code 49 - 8009030C: LdapErr: DSID-0C09043E, comment: AcceptSecurityContext error, data 0, vece at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:2988) at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2934) at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2735) at com.sun.jndi.ldap.LdapCtx.connect(LdapCtx.java:2649) at com.sun.jndi.ldap.LdapCtx.<init>(LdapCtx.java:290) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(LdapCtxFactory.java:175) at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(LdapCtxFactory.java:193) at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(LdapCtxFactory.java:136) at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(LdapCtxFactory.java:66) at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662) at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:243) at javax.naming.InitialContext.init(InitialContext.java:219) at javax.naming.InitialContext.<init>(InitialContext.java:195) at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:80)
References:
|
|
|
|