All 4 entries tagged Ldap

No other Warwick Blogs use the tag Ldap on entries | View entries tagged Ldap at Technorati | There are no images tagged Ldap on this blog

March 30, 2006

Password resets in NDS

One of the simplest aspects of increasing security of user accounts and passwords is allowing people to easily and securely change their password whenever they want.

I would not be surprised if the majority of people never changed any passwords once they have been set. In this day and age with everyone registering for so many websites, how you can keep up with regular password changes. Most people just don't see the point, they figure their password is already secure and why would anyone want to hack their account anyway?

As I've mentioned before, we don't quite have Single Sign On at the University because there are still a few places that don't tie into our central NDS LDAP directory. However, for most people that NDS password does cover a lot of things. This is good…and bad. If that password gets compromised then the attacker is going to get into a lot of things. But, if you've only got one password to remember, there would be less resistence to changing it.

At the moment you have to login to our Novell Portal (Insite) to change your password on the web (you can also change it on the managed desktop or via the service desk). However, a lot of people login via the SSO screen that secures things like SiteBuilder, Forums, Blogs, etc… If we had a change password gadget there then it would be much more visible and easy for users to change. Question is…how to do it? Our handy friends at WBS helped us out there with a chunk of code that they use.

env.put("java.naming.security.principal", user);
env.put("java.naming.security.credentials", pass);
LdapContext ldapContext = (LdapContext) getLdapCtxFactory().getInitialContext((Hashtable) env);

ModificationItem removeItem = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,new BasicAttribute("userPassword",pass));
ModificationItem replaceItem = new ModificationItem(DirContext.ADD_ATTRIBUTE,new BasicAttribute("userPassword",newPassword));

ldapContext.modifyAttributes(user,new ModificationItem[] {removeItem,replaceItem});

The trick is that you have to bind as the user who is changing their password and then you must do a remove of the password attribute followed by adding the attribute back again (you can't just do a replace). This works a treat, but it only allows users to change their passwords when they already know them. It does not provide a "I've forgotten my password" facility. This is a lot harder as it means that some web app must have admin access to everyones passwords…so for now we are holding off on that one, but it is something that would be very valuable in the future as I can only imagine how many service desk calls a year we get about forgotten passwords.

Update: Having just looked at our service desk call logging system (HEAT), I see that in the last 2 weeks of term 12% of service desk calls were about forgotten passwords in some shape or form. Obviously an online password reset/forgotten password service would not get all of these, you would hope it could significantly reduce the workload of the service desk.


February 28, 2006

LDAP filters

I recently did a bit of work to make a nice little AJAX/DHTML user picker for SiteBuilder2. It is basically an in page popup that allows quick searching of Warwick users by first and last names to find their usercode. This is useful for helping people work out usercodes for permissions and properties pages and such.

One problem was that it was a touch slow, especially for very broad searches such as everyone with a first name starting with K and last name starting with S.

In LDAP terms, we were doing the following:

NamingEnumeration searchAnswer = ctx.search("o=Warwick", "(&(givenName=K*)(sn=S*))", sctls);
This works just fine and always used to return around 300 users. However, we always had to check for any expired accounts after the results were returned. Because account expiry was not very well populated in the past, only a few out of those 300 would be filtered out. However, after the recent tidying up of the directory due to password resets there are now many many more disabled accounts in NDS (our directory), which is a good thing. Now we can do this:
NamingEnumeration searchAnswer = ctx.search("o=Warwick", "(&(givenName=K*)(sn=S*)(!(logindisabled=*)))", sctls);
So only people matching the first name and last name searches who also do not have a logindisabled attribute. This now returns just 97 results and is around twice as fast meaning out user picker searches should be much faster from now on.

February 24, 2006

Character encoding, Unicode and UTF–8

Writing about web page http://www.joelonsoftware.com/articles/Unicode.html

When you're dealing with reading data from various sources and then end up doing some processing on it and display it on the web, most of the time you don't worry about character encoding. However, occasionally it comes along and bites you.

I always used to know that there were different character encodings and you could end up not displaying international characters properly if you used the wrong type and so on, but I didn't really know about it in depth. This is where good old Joel comes in. He wrote an article a while back entitled:
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets
. He does a pretty good job of explaining things.

My specific problem was an international students name was coming out of our directory (NDS) like this H??hner. It turns out they are actually called Hühner. So that one character was being turned into ??. No good. Usually I just say "oh, some character encoding problem" and give up. But sadly I was determined to get to the bottom it. Upon closer inspection, the ?? were an artifact of appear on the web (different encoding again), but in my java code, their name was: H├╝hner. Nice.

Doing an ethereal trace on the traffic to my machine when I queried NDS for this person, I saw that:
48 e2 94 9c e2 95 9d 68 6e 65 72
seemed to represent our users name. This is hex and having a look at some character encoding charts, it turns out that this is UTF-8. Is there an easy way of fiddling about with different encoding in java…not that I can find. So, following the instructions on UTF-8 encodings from here I worked out that in Unicode that UTF-8 sequence is:
0x48, 0x251C, 0x255D, 0x68, 0x6e, 0x65, 0x72
Which does indeed turn into H├╝hner. So, nothing was wrong in my code and it proved that NDS was storing something obsure. Pleasingly, a quick email to our friendly systems team with this evidence and they got it fixed and are now going through the directory trying to fix bad entries and work out where this strange encoding is coming from. Hopefully our international students will soon no longer be seeing their names scrambled :)

Geek talk over.


October 12, 2005

LDAP connection pooling

We recently had problems with load on our single sign on (SSO) server. Being the start of term, things are generally busier than the rest of the year and we often see higher load than normal. However, this was too far from normal to be right.

A bit of investigation showed that our JBoss instance had literally 100s and 100s of threads. Lsof is a very handy utility in cases like this.

lsof -p <procid>

This revealed 100s of open connections to our LDAP servers. Not good.

Looking at the LDAP code we have, there are two places where we make LDAP connections, or as they are known in Java; contexts.

Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, 
    "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://ourldap.warwick.ac.uk");
LdapContext ctx = new InitialLdapContext(env,null);
// do something useful with ctx
ctx.close()

This is pretty much how our code worked in both places. Importantly I'd checked that the contexts were always closed…and they were.

This is where LDAP connection pooling came into the picture. It turned out that one piece of code (not written by us), used this:

env.put("com.sun.jndi.ldap.connect.pool", "true");

This turns on connection pooling. However, we didn't use pooling in the other bit of code. So, one of the other wasn't working. Trying out pooling on both bits of code didn't improve things either, basically because it is a multi–threaded application with 100's of requests a minute, if you just keep creating new LdapContext's from a LdapCtxFactory, you are using a new LdapCtxFactory every time.

Thankfully our SSO application uses Spring so it was simple enough to create an XML entry for the LdapCtxFactory and the environment config and plug the same LdapCtxFactory into the two places it was needed. At least now we were using the same factory.

We could now do this:

Map env = new Hashtable();
env.putAll(getLdapEnv());
env.put("java.naming.security.principal", user);
env.put("java.naming.security.credentials", pass);
LdapContext ldapContext = (LdapContext) getLdapContextFactory().getInitialContext((Hashtable) env);

Where the base LDAP environment and LdapCtxFactory was injected into where it was needed. Then just the username and password to bind as is passed in dynamically.

To really know if pooling is working you need to turn on the debugging for the ldap connection pooling by adding a java option to your test/application/server. There are other handy options for tweaking the pooling behaviour as well.

-Dcom.sun.jndi.ldap.connect.pool.debug=fine
-Dcom.sun.jndi.ldap.connect.pool.initsize=20 -Dcom.sun.jndi.ldap.connect.pool.timeout=10000

The bugging will give you messages like this if pooling isn't working:

Create com.sun.jndi.ldap.LdapClient@c87d32[nds.warwick.ac.uk:389]
Use com.sun.jndi.ldap.LdapClient@c87d32
Create com.sun.jndi.ldap.LdapClient@c81a32[nds.warwick.ac.uk:389]
Use com.sun.jndi.ldap.LdapClient@c81a32
Create com.sun.jndi.ldap.LdapClient@a17d35[nds.warwick.ac.uk:389]
Use com.sun.jndi.ldap.LdapClient@a17d35
Create com.sun.jndi.ldap.LdapClient@1a7e35[nds.warwick.ac.uk:389]
Use com.sun.jndi.ldap.LdapClient@1a7e35

New connections are just being created every time with no reuse. What you should see is:

Use com.sun.jndi.ldap.LdapClient@17bd5d1
Release com.sun.jndi.ldap.LdapClient@17bd5d1
Create com.sun.jndi.ldap.LdapClient@cce3fe[nds.warwick.ac.uk:389]
Use com.sun.jndi.ldap.LdapClient@cce3fe
Release com.sun.jndi.ldap.LdapClient@cce3fe
Use com.sun.jndi.ldap.LdapClient@1922b38
Release com.sun.jndi.ldap.LdapClient@1922b38
Use com.sun.jndi.ldap.LdapClient@17bd5d1
Release com.sun.jndi.ldap.LdapClient@17bd5d1

As you can see, there are actually two differences here from a fully working connection pool and a well and truely broken one.

  1. There are very few creates and lots of reuse in the good code
  2. There are lots of releases after connection use in the good code

This is where we came across our second problem. Although in theory the connection pooling was working and I could see some reuse, it was still creating a lot of connections and I wasn't seeing barely any 'Release' messages.

Chris hit the nail on the head with pointing out that NamingEnumerations could well be just like PreparedStatements and ResultSets for JDBC. It is all fine and well closing the connection/context itself, but if you don't close the other resources, it won't actually get released.

The proof of this shows up again in lsof or netstat. A context that has been closed but still has an open NamingEnumeration shows up like this:

java    21533 jboss   80u  IPv6 0x32376e2cf70   0t70743    TCP ssoserver:60465->ldapserver.warwick.ac.uk:ldap (ESTABLISHED)

However, when it is closed, it should wait to be closed, like this:

java    21533 jboss   80u  IPv6 0x32376e2cf70   0t70743    TCP ssoserver:60465->ldapserver.warwick.ac.uk:ldap (TIME_WAIT)

Upon closing all NamingEnumerations, we finally got the perfect results. 100s of requests a minute and only ever around 10–15 ldap connections open at any one time.

So, lessons learnt.

  • When creating contexts, share the factory to use pooling
  • Make sure you close everything. If it has a close()...use it!
  • Occasionally take a look at the open connections and threads that you application has…it might surprise you.

Update:

Spring config:


<bean id="ldapContextFactory" class="com.sun.jndi.ldap.LdapCtxFactory" singleton="true"/>

<bean id="ldapEnv" class="java.util.Hashtable">
<constructor-arg>
<map>
<entry key="java.naming.factory.initial"><value>com.sun.jndi.ldap.LdapCtxFactory</value></entry>
<entry key="java.naming.provider.url"><value>ldaps://ourldap.ac.uk</value></entry>
<entry key="java.naming.ldap.derefAliases"><value>never</value></entry>
<entry key="com.sun.jndi.ldap.connect.timeout"><value>5000</value></entry>
<entry key="java.naming.ldap.version"><value>3</value></entry>
<entry key="com.sun.jndi.ldap.connect.pool"><value>true</value></entry>
        </map>
</constructor-arg>
</bean>

Update:
We now do connection pooling with LDAPS so we use the additional system property:

-Dcom.sun.jndi.ldap.connect.pool.protocol="plain ssl"

November 2019

Mo Tu We Th Fr Sa Su
Oct |  Today  |
            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   

Tags

Search this blog

Most recent comments

  • One thing that was glossed over is that if you use Spring, there is a filter you can put in your XML… by Mathew Mannion on this entry
  • You are my hero. by Mathew Mannion on this entry
  • And may all your chickens come home to roost – in a nice fluffy organic, non–supermarket farmed kind… by Julie Moreton on this entry
  • Good luck I hope that you enjoy the new job! by on this entry
  • Good luck Kieran. :) by on this entry

Galleries

Not signed in
Sign in

Powered by BlogBuilder
© MMXIX