Thursday, 29 January 2009

NTLM Authentication and the IE Post Problem

We are using NTLM Windows Authentication for a Single Sign On (SSO) project.

We are using the Spring security Filter NtlmProcessingFilter which for most of the time is absolutely fine.

However the are atleast two scenarios where this fails.

1) When the session is timed out and a form.submit() request is made.
Under this situation a windows logon box is presented. This is obviously not desirable in a SSO project.

2) If the page makes heavy use of dwr/javascript.
In this case the page makes repeated NTLM authentication requests and stack traces are observed with the message 'This is not a Type 3 Message'.

There is a solution described in the jcifs documentation. Search for registry key. This solution works but is not suitable for us as our client would not let us change the registry on all the client PCs. Quite understandably I think.

The fix described here applies to Spring-Security 2.0.4 and jcifs 1.2.25 but is also required for jcifs to atleast 1.3.3

Both Spring-Security and jcifs have an NTLMFilter and it is to this that the fix is required.

Here is the Spring solution to org.springframework.security.ui.ntlm.NtlmProcessingFilter:


protected void doFilterHttp(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpSession session = request.getSession();
Integer ntlmState = (Integer) session.getAttribute(STATE_ATTR);

final String authMessage = request.getHeader("Authorization");

// Check the special IE POST request with Authorization header containing
// type-1 message (see method javadoc)
if (this.reAuthOnIEPost(request)) {
if ((authMessage != null) && (authMessage.startsWith("NTLM "))) {
logger.debug("POST Request with NTLM Authorization detected.");
// decode the NTLM response from the client
byte[] src = Base64.decode(authMessage.substring(5));
// see if a type 1 message was sent by the client
if (src[8] == 1) {
logger
.debug("NTLM Authorization header contains type-1 message. Sending fake response just to pass this stage...");
Type1Message type1 = new Type1Message(src);
// respond with a type 2 message, where the challenge is null since we
// don't
// care about the server response (type-3 message) since we're already
// authenticated
// (This is just a by-pass - see method javadoc)
Type2Message type2 = new Type2Message(type1, new byte[8], null);
String msg = Base64.encode(type2.toByteArray());
response.setHeader("WWW-Authenticate", "NTLM " + msg);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
response.flushBuffer();
}
}
} else {
..... existing filter code
}
chain.doFilter(request, response);
}

The fix for jcifs appears to be very similar and thanks to Asaf Mesika off the jcifs forum for his help. NB: I have not tried this jcifs solution. For jcifs the fix is to jcifs.http.NtlmHttpFilter:


protected NtlmPasswordAuthentication negotiate( HttpServletRequest req,
HttpServletResponse resp,
boolean skipAuthentication ) throws IOException, ServletException {
UniAddress dc;
String msg;
NtlmPasswordAuthentication ntlm = null;
msg = req.getHeader( "Authorization" );
boolean offerBasic = enableBasic && (insecureBasic || req.isSecure());

// Check the special POST request with Authorization header containing type-1 message (see method javadoc)
if (request.getMethod().equalsIgnoreCase("POST")) {
String authorization = request.getHeader( "Authorization" );
if ( (authorization != null) && (authorization.startsWith("NTLM ")) ) {
logger.debug("POST Request with NTLM Authorization detected.");
// decode the NTLM response from the client
byte[] src = Base64.decode(authorization.substring(5));
// see if a type 1 message was sent by the client
if (src[8] == 1) {
logger.debug("NTLM Authorization header contains type-1 message. Sending fake response just to pass this stage...");
Type1Message type1 = new Type1Message(src);
// respond with a type 2 message, where the challenge is null since we don't
// care about the server response (type-3 message) since we're already authenticated
// (This is just a by-pass - see method javadoc)
Type2Message type2 = new Type2Message(type1, new byte[8], null);
String msg = Base64.encode(type2.toByteArray());
response.setHeader("WWW-Authenticate", "NTLM " + msg);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentLength(0);
response.flushBuffer();
return false;
}
}
}

... existing filter code ....
}

I have raised a spring-security bug if you want to see if it is fixed in the version you have.

Wednesday, 7 January 2009

Garmin Forerunner - Pairing extra devices

With my new Garmin Forerunner 50 I also got the GSC 10 cycle speed & cadence sensor.

I really struggled to pair this extra device. I followed the instructions, changed batteries etc with no luck. It flatly refused to see the sensor.

After much hunting I discoverred the Garmin FAQ. This said to try riding the bike at the same time. IE wake up the sensor while pairing is happening.

A simpler solution was to wave the magnet near the sensor. Bingo. it is now all paired up.

Garmin Forerunner ANT Agent and running XP exe's on Vista

Lucky me, I received a Garmin Forerunner 50 heart rate monitor for christmas.

Incidently it was bought off amazon which is selling it an an amazing price.

The Forerunner series have the excellent capability of uploading your heart and distance data to garmin.com.

This is done by a wireless dongle and some software called ANT Agent. However this is an Windows XP executable and would not run. Many people appear to have struggled with this but the solution is quite easy and I found how here. This is presumably true for many other XP executables.

In case the link does not work this is what you have to do:
1. right click the ANT Agent executable you have just downloaded.
2. choose Properties
3. Navigate to the Compatibility tab
4. Set the Compatibility mode to Windows XP (Service Pack 2)
5. Click OK to save the settings
6. Run the exe and all should be OK