diff --git a/src/java/davmail/exchange/ExchangeSessionFactory.java b/src/java/davmail/exchange/ExchangeSessionFactory.java index 2a892ef7..f884f1db 100644 --- a/src/java/davmail/exchange/ExchangeSessionFactory.java +++ b/src/java/davmail/exchange/ExchangeSessionFactory.java @@ -99,7 +99,7 @@ public final class ExchangeSessionFactory { /** * Create authenticated Exchange session * - * @param baseUrl OWA base URL + * @param baseUrl OWA base URL * @param userName user login * @param password user password * @return authenticated session @@ -256,6 +256,21 @@ public final class ExchangeSessionFactory { } } + /** + * Get user password from session pool for SASL authentication + * + * @param userName Exchange user name + * @return user password + */ + public static String getUserPassword(String userName) { + for (PoolKey poolKey : POOL_MAP.keySet()) { + if (poolKey.userName.equals(userName)) { + return poolKey.password; + } + } + return null; + } + /** * Check if at least one network interface is up and active (i.e. has an address) * diff --git a/src/java/davmail/ldap/LdapConnection.java b/src/java/davmail/ldap/LdapConnection.java index 5613f1c0..42633418 100644 --- a/src/java/davmail/ldap/LdapConnection.java +++ b/src/java/davmail/ldap/LdapConnection.java @@ -30,6 +30,10 @@ import davmail.exchange.ExchangeSessionFactory; import davmail.ui.tray.DavGatewayTray; import org.apache.log4j.Logger; +import javax.security.auth.callback.*; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslServer; import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -299,6 +303,8 @@ public class LdapConnection extends AbstractConnection { static final int LDAP_REP_SEARCH = 0x64; static final int LDAP_REP_RESULT = 0x65; + static final int LDAP_SASL_BIND_IN_PROGRESS = 0x0E; + // LDAP return codes static final int LDAP_OTHER = 80; static final int LDAP_SUCCESS = 0; @@ -348,6 +354,11 @@ public class LdapConnection extends AbstractConnection { } } + /** + * Sasl server for DIGEST-MD5 authentication + */ + protected SaslServer saslServer; + /** * raw connection inputStream */ @@ -496,7 +507,10 @@ public class LdapConnection extends AbstractConnection { DavGatewayTray.resetIcon(); } + protected static final byte[] EMPTY_BYTE_ARRAY= new byte[0]; + protected void handleRequest(byte[] inbuf, int offset) throws IOException { + dumpBer(inbuf, offset); BerDecoder reqBer = new BerDecoder(inbuf, 0, offset); int currentMessageId = 0; try { @@ -509,27 +523,94 @@ public class LdapConnection extends AbstractConnection { ldapVersion = reqBer.parseInt(); userName = reqBer.parseString(isLdapV3()); if (reqBer.peekByte() == (Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3)) { + // SASL authentication reqBer.parseSeq(null); + // Get mechanism, usually DIGEST-MD5 String mechanism = reqBer.parseString(isLdapV3()); - throw new IOException("Unsupported authentication mechanism: "+mechanism); + + byte[] serverResponse; + CallbackHandler callbackHandler = new CallbackHandler() { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + // look for username in callbacks + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + userName = ((NameCallback) callback).getDefaultName(); + // get password from session pool + password = ExchangeSessionFactory.getUserPassword(userName); + } + } + // handle other callbacks + for (Callback callback:callbacks) { + if (callback instanceof AuthorizeCallback) { + ((AuthorizeCallback)callback).setAuthorized(true); + } else if (callback instanceof PasswordCallback) { + if (password != null) { + ((PasswordCallback)callback).setPassword(password.toCharArray()); + } + } + } + } + }; + int status; + if (reqBer.bytesLeft() > 0) { + if (saslServer != null) { + byte[] clientResponse = reqBer.parseOctetString(Ber.ASN_OCTET_STR, null); + serverResponse = saslServer.evaluateResponse(clientResponse); + status = LDAP_SUCCESS; + + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_USER", currentMessageId, userName)); + try { + session = ExchangeSessionFactory.getInstance(userName, password); + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_SUCCESS")); + } catch (IOException e) { + serverResponse = EMPTY_BYTE_ARRAY; + status = LDAP_INVALID_CREDENTIALS; + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_INVALID_CREDENTIALS")); + } + + } else { + throw new IOException("Invalid authentication sequence"); + } + + } else { + Map properties = new HashMap(); + properties.put("javax.security.sasl.qop", "auth,auth-int"); + saslServer = Sasl.createSaslServer(mechanism, "ldap", InetAddress.getLocalHost().getHostName(), properties, callbackHandler); + serverResponse = saslServer.evaluateResponse(EMPTY_BYTE_ARRAY); + status = LDAP_SASL_BIND_IN_PROGRESS; + } + + responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + responseBer.encodeInt(currentMessageId); + responseBer.beginSeq(LDAP_REP_BIND); + responseBer.encodeInt(status, LBER_ENUMERATED); + // server credentials + responseBer.encodeString("", isLdapV3()); + responseBer.encodeString("", isLdapV3()); + // challenge or response + responseBer.encodeOctetString(serverResponse, 0x87); + responseBer.endSeq(); + responseBer.endSeq(); + sendResponse(); + } else { password = reqBer.parseStringWithTag(Ber.ASN_CONTEXT, isLdapV3(), null); - } - if (userName.length() > 0 && password.length() > 0) { - DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_USER", currentMessageId, userName)); - try { - session = ExchangeSessionFactory.getInstance(userName, password); - DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_SUCCESS")); + if (userName.length() > 0 && password.length() > 0) { + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_USER", currentMessageId, userName)); + try { + session = ExchangeSessionFactory.getInstance(userName, password); + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_SUCCESS")); + sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, ""); + } catch (IOException e) { + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_INVALID_CREDENTIALS")); + sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, ""); + } + } else { + DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_ANONYMOUS", currentMessageId)); + // anonymous bind sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, ""); - } catch (IOException e) { - DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_INVALID_CREDENTIALS")); - sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, ""); } - } else { - DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_ANONYMOUS", currentMessageId)); - // anonymous bind - sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, ""); } } else if (requestOperation == LDAP_REQ_UNBIND) {