Getting Connection reset Exception when storing/retrieving InitialLdapContext in session - java

i have a webpage page to manage active directory groups, and in the initialization of this page i connect to ldap with jndi and save the ldap context in http session.
here's how i connect to ldap:
public static LdapContext connectToLdap(String host,
String userDN, String userPassword,
boolean ssl) throws Exception {
System.out.println("connectToLdap");
String hostPrefix = "ldap";
String ldapPort = "389";
if (ssl) {
hostPrefix = "ldaps";
ldapPort = "636";
}
String providerUrl = hostPrefix + "://" + host + ":" + ldapPort;
//System.out.println("####### LDAP URL: " + providerUrl);
LdapContext ldapContext;
Hashtable<String, String> ldapEnv = new Hashtable<String, String>(11);
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, providerUrl);
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, userDN);
ldapEnv.put(Context.SECURITY_CREDENTIALS, userPassword);
ldapEnv.put("com.sun.jndi.ldap.read.timeout", 1000 * 10 + "");
if (ssl) {
ldapEnv.put(Context.SECURITY_PROTOCOL, "ssl");
}
ldapEnv.put(Context.REFERRAL, "ignore");
try {
ldapContext = new InitialLdapContext(ldapEnv, null);
System.out.println("success connection to ldap");
return ldapContext;
} catch (Exception e) {
System.out.println("failure connection to ldap");
e.printStackTrace();
return null;
}
}
i don't close the context because it will be reused during user session.
i put the ldapcontext in session so that i don't have to make frequent conenctions to ldap per user, only one connection per session per user.
ISSUE: i noticed that sometimes after 10-15 minutes the retrieval of active directory groups fails and i get exception:
javax.naming.CommunicationException: Connection reset [Root exception is java.net.SocketException: Connection reset]
any ideas why ? please advise why i am getting this issue and how to resolve it.

Don't place LdapContext in HTTP session it does not implement Serializable interface so there is no guarantee that it can be stored/restored to/from session.
Think about it, if session was to be stored in database or replicated to another server LdapContext would be converted to bytes, along with any socket descriptors it references, how would that work when you restore them from bytes?
Have it in a singleton if you need long term connections.
Other than that it is common for servers and networking equipment to close [what they think are] inactive network connections, so any long term connections need to be tested or keep-alive'd.
If it's still relevant I would suggest that you use the (GPLv2, LGPLv2.1 licensed) UnboundID LDAP SDK for Java (no affiliation), that handles connection pooling and connection testing a bit better than the shipped JNDI implementation.

Related

"com.hierynomus.mssmb2.SMBApiException: STATUS_ACCESS_DENIED: Authentication failed ..." on authenticate call

I'm trying to connect to an SMB server using com.hierynomus.smbj v0.10.0. Connection is ok but as soon as I try to authenticate I'm getting this exception:
com.hierynomus.mssmb2.SMBApiException: STATUS_ACCESS_DENIED (0xc0000022): Authentication failed for 'USERNAME' using com.hierynomus.smbj.auth.NtlmAuthenticator#4152d38d
at com.hierynomus.smbj.connection.Connection.authenticate(Connection.java:194)
at MyTest.smbj(MyTest.java:...)
Here is the test code to see what I'm doing:
#Test
void smbj() throws Exception {
SMBClient client = new SMBClient();
try (Connection connection = client.connect("my-smb-server.de")) {
AuthenticationContext ac = new AuthenticationContext("USERNAME", "PASSWORD".toCharArray(), "DOMAIN");
Session session = connection.authenticate(ac);
// ... do more here ...
}
}
The SMB server and connection is perfectly fine since I'm able to connect from the same machine the test is running using multiple GUI tools (e.g. ForkLift) without any customized settings.
connection.getConnectionInfo() returns this before doing the authentication:
Successfully connected to: my-smb-server.de
ConnectionInfo{
serverGuid=975b39d3-55c0-4490-84e7-80371b734ce7,
serverName='my-smb-server.de',
negotiatedProtocol=NegotiatedProtocol{dialect=SMB_2_1, maxTransactSize=8388608, maxReadSize=8388608, maxWriteSize=8388608},
clientGuid=85863c1f-0328-4c21-9a72-b3d907b23a1d,
clientCapabilities=[SMB2_GLOBAL_CAP_DFS],
serverCapabilities=[SMB2_GLOBAL_CAP_DFS, SMB2_GLOBAL_CAP_LEASING, SMB2_GLOBAL_CAP_LARGE_MTU], > clientSecurityMode=0,
serverSecurityMode=3,
server='null' }
Any help is highly appreciated.

MQ - Get Multi Instance MQ Manager Connection List

In a Java client app we are connecting to a multi-instance MQ Manager as follows:
java.net.URL ccdt = new URL("file:./config/qmgrs/MQMGR/AMQCLCHL.TAB");
MQQueueManager mqQueueManager = new MQQueueManager("*MQMGR", ccdt);
We can then for example enquire about the current depth of a queue as follows:
int openOptions = CMQC.MQOO_INQUIRE;
MQQueue mqQueue = mqQueueManager.accessQueue("A.QUEUE.NAME", openOptions);
System.out.println("queue depth:" + mqQueue.getCurrentDepth());
Question is, using the same MQQueueManager object, how can we get the list of multi-instance MQ Managers' addresses and ports. Or any other info about the manager itself...
We can see there is the following sort of thing available:
String nameList = mqQueueManager.getAttributeString(MQConstants.MQCA_NAMELIST_NAME, MQConstants.MQ_NAMELIST_NAME_LENGTH);
But when we call the above command, we get:
com.ibm.mq.MQException: MQJE001: Completion Code '2', Reason '2067'.
We are not sure if this is because the client code is not configured correctly or, if it is because the connection that we are using does not have sufficient permissions to get information about the manager?
You will have to use MQ PCF classes to query queue manager attributes. There is sample PCF_WalkThroughQueueManagerAttributes.java shipped with MQ that displays all attributes of queue manager. Here is small sample that lists local queues of a queue manager.
private void runPCFTest() {
try {
PCFAgent agent = new PCFAgent(connect());
PCFParameter[] parameters = { new MQCFST (MQConstants.MQCA_Q_NAME, "*"),
new MQCFIN (MQConstants.MQIA_Q_TYPE, MQConstants.MQQT_LOCAL)};
MQMessage[] responses = agent.send(CMQCFC.MQCMD_INQUIRE_Q_NAMES, parameters);
MQCFH cfh = new MQCFH(responses[0]);
for (int i = 0; i < cfh.getParameterCount(); i++) {
System.out.println (PCFParameter.nextParameter (responses [0]));
}
}catch(Exception ex) {
System.out.println(ex);
}
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private MQQueueManager connect() throws MQException {
Hashtable props = new Hashtable();
props.put(MQConstants.HOST_NAME_PROPERTY, "localhost");
props.put(MQConstants.PORT_PROPERTY, 1414);
props.put(MQConstants.CHANNEL_PROPERTY, "MFT_CHN");
props.put(MQConstants.USER_ID_PROPERTY, "user1");
props.put(MQConstants.PASSWORD_PROPERTY, "passw0rd");
props.put(MQConstants.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
return new MQQueueManager("MQM", props);
}
But why do you want to query connection information, host, port etc?
If I understand your question correctly, you want to know all of the hostnames (or IP addresses) and Port numbers of the servers where the MI queue manager may reside. Correct?
This information is in your CCDT file. When you (or MQAdmin) created your CCDT entry for the CLNTCONN (client-side channel), you would have issued a like:
DEFINE CHANNEL(TEST.CHL) CHLTYPE(CLNTCONN) TRPTYPE(TCP) CONNAME('ipaddr1(1414), ipaddr2(1414)') QMNAME(QM1)
Hence, the CONNAME parameter has the information and that is what the MQ client library uses to connect to the remote queue manager. First it will try 'ipaddr1(1414)' and if it fails then it will try 'ipaddr2(1414)'.

LDAP is not delivering results

I currently face the problem that an LDAP-Query is passed to an LDAP-Server and the LDAP-Server does not deliver results.
The query: (&(objectCategory=user)(mail=tester#oop-expert.de))
The given E-Mail is meant to not be found. So an empty result is expected.
In most environment configurations this query will pass perfectly and the LDAP returns an empty result immediately.
I broke down the problem to something that may relate to the network or host from where the query is sent. So the query will be fine if sent from one host/network and from another host/network the LDAP-Server will "starve" my LDAP-Client so the LDAP-Client closes the connection for a client-side timeout.
On the other hand: searching for an email that exists will always lead to an immediate result. Doesn't matter from which host/network.
The LDAP-Server is an Active Directory. There a several domain controllers providing an LDAP-Service, configured "round robin". Access per ip or dns does not make any difference on this subject.
The communication is secured via ssl. (ldaps)
In all situations the connection was established. So authentication and passing the query to the LDAP went fine.
Authorization should not be an issue either. I used the same LDAP user for all situations.
The LDAP client is always a JAVA implementation using InitialContext.
private InitialDirContext createDirContext(String principal, String credentials) throws NamingException {
if (credentials == null || credentials.isEmpty()) {
throw new LDAPLoginException();
}
return new InitialDirContext(createEnvironment(principal, credentials));
}
private Hashtable<String, String> createEnvironment(String principal, String credentials) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.ldapUrl);
// To get rid of the PartialResultException when using Active Directory
env.put(Context.REFERRAL, "follow");
// Needed for the Bind (User Authorized to Query the LDAP server)
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
return env;
}
The code that builds and executes the query:
private LDAPUser getLDAPUserInfoByUniqueField(String attr, String value) {
DirContext serviceUserContext = null;
NamingEnumeration<SearchResult> results = null;
try {
String searchString = "(&(objectCategory=user)(" + attr + "=" + value + "))";
serviceUserContext = createDirContext(this.serviceUserPrincipal, serviceUserCredentials);
results = serviceUserContext.search("", searchString, createSearchControls()); // blocking...
return createLDAPUserInfo(results);
} catch (LDAPLoginException e) {
throw e;
} catch (Exception e) {
throw new LDAPFatalException(e);
} finally {
try {
if (results != null)
results.close();
} catch (NamingException e) {
}
try {
if (serviceUserContext != null)
serviceUserContext.close();
} catch (NamingException e) {
}
}
}
Utility methods:
private LDAPUser createLDAPUserInfo(NamingEnumeration<SearchResult> results) throws NamingException {
LDAPUser ldapUserInfo = null;
if (results.hasMore()) { // blocking here
SearchResult result = (SearchResult) results.next();
String sAMAccountName = extractsAMAccountName(result);
String distinguishName = extractDistinguishName(result);
String department = extractDepartment(result);
String email = extractEmail(result);
ldapUserInfo = new LDAPUser(sAMAccountName, distinguishName, department, email);
}
return ldapUserInfo;
}
private String extractsAMAccountName(SearchResult result) throws NamingException {
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
return (String) attr.get();
}
The exception:
de.oopexpert.business.ldap.LDAPFatalException: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:90)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByEmail(LDAPImpl.java:57)
Caused by: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:242)
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
at de.oopexpert.business.ldap.impl.LDAPImpl.createLDAPUserInfo(LDAPImpl.java:139)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:84)
Any hints?
We struggled with four persons what is going on. We figured out, that it has something to do with "name resolution problems" on the server side when "following refferals". The customer specific environment configuration inherently prohibits us to prevent this behaviour. So we came up with a workaround.
As we are operating in an active directory domain we have global catalog servers. The important statement to this is from Microsoft "technet":
The global catalog is a distributed data repository that contains a searchable, partial representation of every object in every domain in a multidomain Active Directory Domain Services (AD DS) forest. The global catalog is stored on domain controllers that have been designated as global catalog servers and is distributed through multimaster replication. Searches that are directed to the global catalog are faster because they do not involve referrals to different domain controllers.
(from What Is the Global Catalog?)
The phrase "do not involve refferals" leads us to my JNDI environment configuration where I set the following:
env.put(Context.REFERRAL, "follow");
So as I did that, the LDAP-Server will ask other LDAP-Servers if the result of a query seems to be incomplete. This was confirmed by our administrators when I started the query and they debugged it on TCP-Level.
Somehow, I maybe do not really represent it correctly, the name of the other LDAP-Server could not be resolved which leads the client to starvation as the first LDAP-Server waits for resolution.
We tried to omit the client parameter "Context.Refferals=follow". Here we get immediate response. But the response wasn't as expected:
javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ''
This came up because the first LDAP-Server has the opinion to return incomplete data.
Our administrators say that this can never be the case, because "every domain controller" is a "Global Catalog Server".
So my workaround is: I will deal with this exception and interprete it as "no result".

Simple LDAP SSO auth

I have a very simple LDAP auth :
DirContext ctx = null;
try
{
logger.debug("Trying to log with LDAP");
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, ninjaProperties.get("ldapFactory"));
env.put(javax.naming.Context.PROVIDER_URL, ninjaProperties.get("ldapProvider"));
env.put(javax.naming.Context.SECURITY_AUTHENTICATION, ninjaProperties.get("ldapAuthentication"));
env.put(javax.naming.Context.SECURITY_PRINCIPAL, ninjaProperties.get("ldapDomain") + context.getParameter("login"));
env.put(javax.naming.Context.SECURITY_CREDENTIALS, context.getParameter("password"));
ctx = new InitialDirContext(env);
if(ctx != null){
logger.info( "User [" + context.getParameter("login") + "] logged in successfully." );
ctx.close();
return Results.redirect("/TermeController/consult");
}
}
catch (Exception e)
{
logger.error("LDAP Login failed : " + e.toString());
}
For now, users logg in with a form. I would like to set up an SSO auth, but i can't find a way to set it up easily.
Do i need to use something like CAS or so? Or is there a simple way?
Thx :)
SSO is quite a broad topic, I am not sure what exactly you are trying to achieve here. However if you want SSO for windows users i.e. allowing Windows users that are already authenticated against a domain controller to access your app with the same credentials you could look into WAFFLE which does exactly that.

Clear ehcache of remote server

How can we clear ehcache on a remote server?
My application is running in staging environment (host 111.22.3.44 and port 17000) and I want to write an utility method that can connect to a given host:port and clear the ehcache of my App. This utility should to work in Windows as well as Linux.
I use JConsole.exe utility to flush the cache of ehcache created in stage-server, but there is a situation where I need to do it programatically.
Hurrey...:) I got the solution for clearing ehcache on a remote environment. Here, I have written a Java utility method that will flush out ehcache of a given remote machine that is specified by host name and port.
public void flushEhcache() throws IOException, NamingException, MalformedObjectNameException, NullPointerException, AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException {
String host = "111.22.3.44";
String port = "16000";
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://"
+ host + ":" + port + "/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
ObjectName beanName = new ObjectName("net.sf.ehcache:type=CacheManager,name=Your Application Name Here");
mbsc.invoke(beanName, "clearAll", new Object[0], new String[0]);
System.out.println("Flushed out ehcache succesfully");
}

Categories