mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-13 03:02:22 -05:00
LdapConnection: check config before BIND to avoid password prompt on network down
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@233 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
79f8800538
commit
1b71e3ebb5
@ -11,18 +11,12 @@ import davmail.tray.DavGatewayTray;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.net.SocketException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a caldav connection.
|
* Handle a caldav connection.
|
||||||
@ -34,16 +28,19 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
static final String BASE_CONTEXT = "ou=people";
|
static final String BASE_CONTEXT = "ou=people";
|
||||||
|
|
||||||
static final List<String> PERSON_OBJECT_CLASSES = new ArrayList<String>();
|
static final List<String> PERSON_OBJECT_CLASSES = new ArrayList<String>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PERSON_OBJECT_CLASSES.add("top");
|
PERSON_OBJECT_CLASSES.add("top");
|
||||||
PERSON_OBJECT_CLASSES.add("person");
|
PERSON_OBJECT_CLASSES.add("person");
|
||||||
PERSON_OBJECT_CLASSES.add("organizationalPerson");
|
PERSON_OBJECT_CLASSES.add("organizationalPerson");
|
||||||
PERSON_OBJECT_CLASSES.add("inetOrgPerson");
|
PERSON_OBJECT_CLASSES.add("inetOrgPerson");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exchange to LDAP attribute map
|
* Exchange to LDAP attribute map
|
||||||
*/
|
*/
|
||||||
static final HashMap<String, String> ATTRIBUTE_MAP = new HashMap<String, String>();
|
static final HashMap<String, String> ATTRIBUTE_MAP = new HashMap<String, String>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ATTRIBUTE_MAP.put("uid", "AN");
|
ATTRIBUTE_MAP.put("uid", "AN");
|
||||||
ATTRIBUTE_MAP.put("mail", "EM");
|
ATTRIBUTE_MAP.put("mail", "EM");
|
||||||
@ -66,6 +63,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final HashSet<String> EXTENDED_ATTRIBUTES = new HashSet<String>();
|
static final HashSet<String> EXTENDED_ATTRIBUTES = new HashSet<String>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
EXTENDED_ATTRIBUTES.add("givenname");
|
EXTENDED_ATTRIBUTES.add("givenname");
|
||||||
EXTENDED_ATTRIBUTES.add("initials");
|
EXTENDED_ATTRIBUTES.add("initials");
|
||||||
@ -82,6 +80,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
* LDAP to Exchange Criteria Map
|
* LDAP to Exchange Criteria Map
|
||||||
*/
|
*/
|
||||||
static final HashMap<String, String> CRITERIA_MAP = new HashMap<String, String>();
|
static final HashMap<String, String> CRITERIA_MAP = new HashMap<String, String>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// assume mail starts with firstname
|
// assume mail starts with firstname
|
||||||
CRITERIA_MAP.put("mail", "FN");
|
CRITERIA_MAP.put("mail", "FN");
|
||||||
@ -117,7 +116,6 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
static final int LDAP_SIZE_LIMIT_EXCEEDED = 4;
|
static final int LDAP_SIZE_LIMIT_EXCEEDED = 4;
|
||||||
static final int LDAP_INVALID_CREDENTIALS = 49;
|
static final int LDAP_INVALID_CREDENTIALS = 49;
|
||||||
|
|
||||||
|
|
||||||
static final int LDAP_FILTER_OR = 0xa1;
|
static final int LDAP_FILTER_OR = 0xa1;
|
||||||
|
|
||||||
// LDAP filter operators (only LDAP_FILTER_SUBSTRINGS is supported)
|
// LDAP filter operators (only LDAP_FILTER_SUBSTRINGS is supported)
|
||||||
@ -146,6 +144,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
* For some unknow reaseon parseIntWithTag is private !
|
* For some unknow reaseon parseIntWithTag is private !
|
||||||
*/
|
*/
|
||||||
static Method parseIntWithTag;
|
static Method parseIntWithTag;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
parseIntWithTag = BerDecoder.class.getDeclaredMethod("parseIntWithTag", int.class);
|
parseIntWithTag = BerDecoder.class.getDeclaredMethod("parseIntWithTag", int.class);
|
||||||
@ -163,12 +162,12 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
/**
|
/**
|
||||||
* reusable BER encoder
|
* reusable BER encoder
|
||||||
*/
|
*/
|
||||||
protected BerEncoder responseBer = new BerEncoder();
|
protected final BerEncoder responseBer = new BerEncoder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current LDAP version (used for String encoding)
|
* Current LDAP version (used for String encoding)
|
||||||
*/
|
*/
|
||||||
int ldapVersion = LDAP_VERSION3;
|
final int ldapVersion = LDAP_VERSION3;
|
||||||
|
|
||||||
// Initialize the streams and start the thread
|
// Initialize the streams and start the thread
|
||||||
public LdapConnection(String name, Socket clientSocket) {
|
public LdapConnection(String name, Socket clientSocket) {
|
||||||
@ -187,7 +186,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
byte inbuf[] = new byte[2048]; // Buffer for reading incoming bytes
|
byte[] inbuf = new byte[2048]; // Buffer for reading incoming bytes
|
||||||
int bytesread; // Number of bytes in inbuf
|
int bytesread; // Number of bytes in inbuf
|
||||||
int bytesleft; // Number of bytes that need to read for completing resp
|
int bytesleft; // Number of bytes that need to read for completing resp
|
||||||
int br; // Temp; number of bytes read from stream
|
int br; // Temp; number of bytes read from stream
|
||||||
@ -195,6 +194,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
boolean eos; // End of stream
|
boolean eos; // End of stream
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ExchangeSessionFactory.checkConfig();
|
||||||
while (true) {
|
while (true) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
@ -204,13 +204,15 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
break; // EOF
|
break; // EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR))
|
if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// get length of sequence
|
// get length of sequence
|
||||||
bytesread = is.read(inbuf, offset, 1);
|
bytesread = is.read(inbuf, offset, 1);
|
||||||
if (bytesread < 0)
|
if (bytesread < 0) {
|
||||||
break; // EOF
|
break; // EOF
|
||||||
|
}
|
||||||
int seqlen = inbuf[offset++]; // Length of ASN sequence
|
int seqlen = inbuf[offset++]; // Length of ASN sequence
|
||||||
|
|
||||||
// if high bit is on, length is encoded in the
|
// if high bit is on, length is encoded in the
|
||||||
@ -248,14 +250,15 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
// read in seqlen bytes
|
// read in seqlen bytes
|
||||||
bytesleft = seqlen;
|
bytesleft = seqlen;
|
||||||
if ((offset + bytesleft) > inbuf.length) {
|
if ((offset + bytesleft) > inbuf.length) {
|
||||||
byte nbuf[] = new byte[offset + bytesleft];
|
byte[] nbuf = new byte[offset + bytesleft];
|
||||||
System.arraycopy(inbuf, 0, nbuf, 0, offset);
|
System.arraycopy(inbuf, 0, nbuf, 0, offset);
|
||||||
inbuf = nbuf;
|
inbuf = nbuf;
|
||||||
}
|
}
|
||||||
while (bytesleft > 0) {
|
while (bytesleft > 0) {
|
||||||
bytesread = is.read(inbuf, offset, bytesleft);
|
bytesread = is.read(inbuf, offset, bytesleft);
|
||||||
if (bytesread < 0)
|
if (bytesread < 0) {
|
||||||
break; // EOF
|
break; // EOF
|
||||||
|
}
|
||||||
offset += bytesread;
|
offset += bytesread;
|
||||||
bytesleft -= bytesread;
|
bytesleft -= bytesread;
|
||||||
}
|
}
|
||||||
@ -270,6 +273,11 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
DavGatewayTray.debug("Closing connection on timeout");
|
DavGatewayTray.debug("Closing connection on timeout");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
DavGatewayTray.error(e);
|
DavGatewayTray.error(e);
|
||||||
|
try {
|
||||||
|
sendErr(0, LDAP_REP_BIND, e);
|
||||||
|
} catch (IOException e2) {
|
||||||
|
DavGatewayTray.warn("Exception sending error to client", e2);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
@ -290,7 +298,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
String password = reqBer.parseStringWithTag(Ber.ASN_CONTEXT, ldapVersion == LDAP_VERSION3, null);
|
String password = reqBer.parseStringWithTag(Ber.ASN_CONTEXT, ldapVersion == LDAP_VERSION3, null);
|
||||||
|
|
||||||
if (userName.length() > 0 && password.length() > 0) {
|
if (userName.length() > 0 && password.length() > 0) {
|
||||||
DavGatewayTray.debug("LDAP_REQ_BIND "+currentMessageId+" "+userName);
|
DavGatewayTray.debug("LDAP_REQ_BIND " + currentMessageId + " " + userName);
|
||||||
try {
|
try {
|
||||||
session = ExchangeSessionFactory.getInstance(userName, password);
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
||||||
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
||||||
@ -298,13 +306,13 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, "");
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, "");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DavGatewayTray.debug("LDAP_REQ_BIND "+currentMessageId+" anonymous"+userName);
|
DavGatewayTray.debug("LDAP_REQ_BIND " + currentMessageId + " anonymous" + userName);
|
||||||
// anonymous bind
|
// anonymous bind
|
||||||
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (requestOperation == LDAP_REQ_UNBIND) {
|
} else if (requestOperation == LDAP_REQ_UNBIND) {
|
||||||
DavGatewayTray.debug("LDAP_REQ_UNBIND "+currentMessageId);
|
DavGatewayTray.debug("LDAP_REQ_UNBIND " + currentMessageId);
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
ExchangeSessionFactory.close(session);
|
ExchangeSessionFactory.close(session);
|
||||||
session = null;
|
session = null;
|
||||||
@ -313,19 +321,21 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
reqBer.parseSeq(null);
|
reqBer.parseSeq(null);
|
||||||
String dn = reqBer.parseString(isLdapV3());
|
String dn = reqBer.parseString(isLdapV3());
|
||||||
int scope = reqBer.parseEnumeration();
|
int scope = reqBer.parseEnumeration();
|
||||||
int derefAliases = reqBer.parseEnumeration();
|
/*int derefAliases =*/
|
||||||
|
reqBer.parseEnumeration();
|
||||||
int sizeLimit = reqBer.parseInt();
|
int sizeLimit = reqBer.parseInt();
|
||||||
if (sizeLimit > 100 || sizeLimit == 0) {
|
if (sizeLimit > 100 || sizeLimit == 0) {
|
||||||
sizeLimit = 100;
|
sizeLimit = 100;
|
||||||
}
|
}
|
||||||
int timelimit = reqBer.parseInt();
|
int timelimit = reqBer.parseInt();
|
||||||
boolean typesOnly = reqBer.parseBoolean();
|
/*boolean typesOnly =*/
|
||||||
|
reqBer.parseBoolean();
|
||||||
Map<String, String> criteria = parseFilter(reqBer);
|
Map<String, String> criteria = parseFilter(reqBer);
|
||||||
Set<String> returningAttributes = parseReturningAttributes(reqBer);
|
Set<String> returningAttributes = parseReturningAttributes(reqBer);
|
||||||
|
|
||||||
int size = 0;
|
int size = 0;
|
||||||
DavGatewayTray.debug("LDAP_REQ_SEARCH "+currentMessageId+" base="+dn+" scope: "+scope+" sizelimit: "+sizeLimit+" timelimit: "+timelimit+
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " base=" + dn + " scope: " + scope + " sizelimit: " + sizeLimit + " timelimit: " + timelimit +
|
||||||
" returning attributes: "+returningAttributes);
|
" returning attributes: " + returningAttributes);
|
||||||
|
|
||||||
if (scope == SCOPE_BASE_OBJECT) {
|
if (scope == SCOPE_BASE_OBJECT) {
|
||||||
if ("".equals(dn)) {
|
if ("".equals(dn)) {
|
||||||
@ -379,9 +389,9 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size = persons.size();
|
size = persons.size();
|
||||||
DavGatewayTray.debug("LDAP_REQ_SEARCH "+currentMessageId+" found "+size+" results");
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " found " + size + " results");
|
||||||
sendPersons(currentMessageId, persons, returningAttributes);
|
sendPersons(currentMessageId, persons, returningAttributes);
|
||||||
DavGatewayTray.debug("LDAP_REQ_SEARCH "+currentMessageId+" end");
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " end");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size == sizeLimit) {
|
if (size == sizeLimit) {
|
||||||
@ -394,13 +404,13 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
try {
|
try {
|
||||||
canceledMessageId = (Integer) parseIntWithTag.invoke(reqBer, LDAP_REQ_ABANDON);
|
canceledMessageId = (Integer) parseIntWithTag.invoke(reqBer, LDAP_REQ_ABANDON);
|
||||||
} catch (IllegalAccessException e) {
|
} catch (IllegalAccessException e) {
|
||||||
DavGatewayTray.error(e);
|
DavGatewayTray.error(e);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
DavGatewayTray.error(e);
|
DavGatewayTray.error(e);
|
||||||
}
|
}
|
||||||
DavGatewayTray.debug("LDAP_REQ_ABANDON "+currentMessageId+" for search "+canceledMessageId+", too late !");
|
DavGatewayTray.debug("LDAP_REQ_ABANDON " + currentMessageId + " for search " + canceledMessageId + ", too late !");
|
||||||
} else {
|
} else {
|
||||||
DavGatewayTray.debug("Unsupported operation: "+requestOperation);
|
DavGatewayTray.debug("Unsupported operation: " + requestOperation);
|
||||||
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_OTHER, "Unsupported operation");
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_OTHER, "Unsupported operation");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -437,7 +447,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
} else if (ldapFilterType == LDAP_FILTER_SUBSTRINGS) {
|
} else if (ldapFilterType == LDAP_FILTER_SUBSTRINGS) {
|
||||||
parseSimpleFilter(reqBer, criteria);
|
parseSimpleFilter(reqBer, criteria);
|
||||||
} else {
|
} else {
|
||||||
DavGatewayTray.warn("Unsupported filter");
|
DavGatewayTray.warn("Unsupported filter");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return criteria;
|
return criteria;
|
||||||
@ -473,15 +483,17 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
reqBer.parseSeq(seqSize);
|
reqBer.parseSeq(seqSize);
|
||||||
int end = reqBer.getParsePosition() + seqSize[0];
|
int end = reqBer.getParsePosition() + seqSize[0];
|
||||||
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
||||||
returningAttributes.add(reqBer.parseString(isLdapV3()).toLowerCase());
|
returningAttributes.add(reqBer.parseString(isLdapV3()).toLowerCase());
|
||||||
}
|
}
|
||||||
return returningAttributes;
|
return returningAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert to LDAP attributes and send entry
|
* Convert to LDAP attributes and send entry
|
||||||
* @param currentMessageId current Message Id
|
*
|
||||||
* @param persons persons Map
|
* @param currentMessageId current Message Id
|
||||||
|
* @param persons persons Map
|
||||||
|
* @param returningAttributes returning attributes
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
protected void sendPersons(int currentMessageId, Map<String, Map<String, String>> persons, Set<String> returningAttributes) throws IOException {
|
protected void sendPersons(int currentMessageId, Map<String, Map<String, String>> persons, Set<String> returningAttributes) throws IOException {
|
||||||
@ -526,6 +538,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Root DSE
|
* Send Root DSE
|
||||||
|
*
|
||||||
* @param currentMessageId current message id
|
* @param currentMessageId current message id
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
@ -538,6 +551,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Send Base Context
|
* Send Base Context
|
||||||
|
*
|
||||||
* @param currentMessageId current message id
|
* @param currentMessageId current message id
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
@ -551,7 +565,7 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
sendEntry(currentMessageId, BASE_CONTEXT, attributes);
|
sendEntry(currentMessageId, BASE_CONTEXT, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendEntry(int currentMessageId, String dn, Map<String,Object> attributes) throws IOException {
|
protected void sendEntry(int currentMessageId, String dn, Map<String, Object> attributes) throws IOException {
|
||||||
responseBer.reset();
|
responseBer.reset();
|
||||||
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
||||||
responseBer.encodeInt(currentMessageId);
|
responseBer.encodeInt(currentMessageId);
|
||||||
@ -593,16 +607,16 @@ public class LdapConnection extends AbstractConnection {
|
|||||||
responseBer.reset();
|
responseBer.reset();
|
||||||
|
|
||||||
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
||||||
responseBer.encodeInt(currentMessageId);
|
responseBer.encodeInt(currentMessageId);
|
||||||
responseBer.beginSeq(responseOperation);
|
responseBer.beginSeq(responseOperation);
|
||||||
responseBer.encodeInt(status, LBER_ENUMERATED);
|
responseBer.encodeInt(status, LBER_ENUMERATED);
|
||||||
// dn
|
// dn
|
||||||
responseBer.encodeString("", isLdapV3());
|
responseBer.encodeString("", isLdapV3());
|
||||||
// error message
|
// error message
|
||||||
responseBer.encodeString(message, isLdapV3());
|
responseBer.encodeString(message, isLdapV3());
|
||||||
responseBer.endSeq();
|
|
||||||
responseBer.endSeq();
|
responseBer.endSeq();
|
||||||
sendResponse();
|
responseBer.endSeq();
|
||||||
|
sendResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void sendResponse() throws IOException {
|
protected void sendResponse() throws IOException {
|
||||||
|
Loading…
Reference in New Issue
Block a user