Yanick
Yanick

Reputation: 151

Java URLConnection error with ntlm authentication, but only on Linux and only Java 7

I am trying to open an http connection to an url protected with the NTLM authentication scheme. This code has been working correctly for 2 year when we were on Java 6.I wrote a small java program which access that particular url to make the test case as simple as possible.

The problem is that I am unable to make the program work on linux and when using versions of the JDK 7. Java tries 20 times to access the URL and then I get an error telling me that the server redirected too many times. It works fine with linux and JDK 6, and in windows 7 with JDK 6 or 7.

I checked and tried the solution listed here (and many others) : Getting "java.net.ProtocolException: Server redirected too many times" Error. It didn't work. I also have to add that when accessing the url from a browser, I can see that there are no cookies involved.

Here is the exact detail of the os/java versions that I have tried :

Success:

Fail:

When the program works, I see the authentication methods that were used and the document that I am trying to download as the output:

Scheme:Negotiate
Scheme:ntlm
.... document content ....
Done

When it fails, I have the following output :

Scheme:Negotiate
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
Scheme:ntlm
java.net.ProtocolException: Server redirected too many  times (20)
        at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1635)
        at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254)
        at TestWs.testWs(TestWs.java:67)
        at TestWs.main(TestWs.java:20)

Here is the source code of the program:

package com.test;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.URLConnection;

public class TestWs {

    public static void main(String[] args) throws Exception {
        new TestWs().testWs();
    }

    public void testWs() {
        try {
            CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
            Authenticator.setDefault(new MyAuthenticator("username", "password"));

            URL url = new URL("https://someurlprotectedbyntlmauthentication.com");
            URLConnection connection = url.openConnection();
            InputStream is = connection.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            while (true) {
                String s = br.readLine();
                if (s == null)
                    break;
                System.out.println(s);
            }
            System.out.println("Done");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

class MyAuthenticator extends Authenticator {
    private String httpUsername;
    private String httpPassword;

    public MyAuthenticator(String httpUsername, String httpPassword) {
        this.httpUsername = httpUsername;
        this.httpPassword = httpPassword;
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        System.out.println("Scheme:" + getRequestingScheme());
        return new PasswordAuthentication(httpUsername, httpPassword.toCharArray());
    }
}

Any help would be greatly appreciated.

UPDATE:

After some more investigation, I found that the authentication works if I use a domain user, but not if I use a local user.

This code from the JDK 7 causes me troubles (class com.sun.security.ntlm.Client) :

public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException {
if (type2 == null || (v != Version.NTLM && nonce == null)) {
throw new NullPointerException("type2 and nonce cannot be null");
}
debug("NTLM Client: Type 2 received\n");
debug(type2);
Reader r = new Reader(type2);
byte[] challenge = r.readBytes(24, 8);
int inputFlags = r.readInt(20);
boolean unicode = (inputFlags & 1) == 1;
String domainFromServer = r.readSecurityBuffer(12, unicode);
if (domainFromServer != null) {
domain = domainFromServer;
}

So, since the server is enroled in a domain, it sends back to the client it's domain as part of the NTLM protocol. Java replaces the domain that I'm trying to force by the variable "domainFromServer" everytime and it fails since the user exists on the server and not on the server's domain.

I don't know exactly what to do with that.

Upvotes: 11

Views: 17163

Answers (5)

Aleksey Timohin
Aleksey Timohin

Reputation: 621

One more possible cause for this error: domain user is blocked

Scheme:ntlm
Scheme:ntlm
java.net.ProtocolException: Server redirected too many  times (20)

I had the same error after I accidentally blocked my domain user, when tried to login with incorrect password more than it allowed by domain policy.

Upvotes: 0

Pallavi Sonal
Pallavi Sonal

Reputation: 3901

This issue was resolved in JDK 9-ea with http://bugs.java.com/view_bug.do?bug_id=7150092 and the fix was back-ported to JDK 8u40 also with the http://bugs.java.com/view_bug.do?bug_id=8049690

Upvotes: 2

berk76
berk76

Reputation: 39

Correct is this:

Authenticator.setDefault(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                    "DOMAIN\\user",
                    "password".toCharArray() ) ;
        }
    });

Upvotes: 3

Maravilloso
Maravilloso

Reputation: 39

I had the same problem and solved it just by specifying the user name with the domain included in it:

    Authenticator.setDefault(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                    System.getProperty("DOMAIN\\user"),
                    System.getProperty("password").toCharArray() ) ;
        }
    });

Upvotes: 3

Yanick
Yanick

Reputation: 151

I changed code in the Client.java class, and recompiled it along with the rest of the com.sun.security.ntlm package, then I created a jar called rt_fix.jar which contains the classes of that particular package. Then I used a java startup option to force it to load my jar before the internal rt.jar.

-Xbootclasspath/p:/path_to_jar/rt_fix.jar

I don't like this solution, but it worked.

Here is the code I changed in Client.java, in the method type3:

Before :

    if (domainFromServer != null) {
        domain = domainFromServer;
    }

After :

    if (domainFromServer != null) {
        //domain = domainFromServer;
    }

It stops Java from altering the domain I try to authenticate to with the one recieved from the server when sending the 3rd part of the NTLM authentication. The domain I was trying to authenticate to is in fact the name of the server because the user accounts are local.

Upvotes: 4

Related Questions