Can Java's 'single sign-on' (use credentials from 'Credential Manager') on Windows be disabled?
Asked Answered
B

4

15

Oracle's "Http Authentication" page from the Java SE 6 documentation says that "if you are running on a Windows machine as a domain user, or, you are running on a Linux or Solaris machine that has already issued the kinit command and got the credential cache" then the instance passed to Authenticator.setDefault() "will be completely ignored".

This matches what I observed: setting up an HTTP or HTTPS connection on a Windows system to host X always passes the credentials for host X from the 'Windows Credentials' of the 'Windows Vault', as seen in my Windows 7 'Credential Manager' Control Panel page.

However, in my use case I don't want to use any credentials which might be stored by Windows, but instead I always want to use credentials I explicitly specify in the code.

Is there a way to override the documented behavior, i.e., is there a way to ignore the credentials stored by Windows?

Update: If not, could someone point me to a place in the Java SE 6 source code where I can see that the stored Windows credentials cannot be ignored?

Bicuspid answered 31/5, 2011 at 8:29 Comment(1)
You can find the code that validates if using Windows Credentials on 'sun.net.www.protocol.http.HttpURLConnection' class. Take a look at the creation of 'NTLMAuthentication' instances and the flag 'tryTransparentNTLMServer'. I'm looking to do the same thing as you, but I'm not sure how to do that either.Snout
S
6

I've looked for the same thing you are asking. So far, I haven't found a way on the JDK to do that.

There is a request for enhancement on Java Bug Database. Take a look at the report to find out if that gets a response from Sun (vote up the report so that hopefully that gets fixed soon).

What I ended up doing, was override sun.net.www.protocol.http.NTLMAuthentication class. By looking at sun.net.www.protocol.http.HttpURLAuthentication, I found that the only thing you need to modify is the result of:

NTLMAuthentication.supportsTransparentAuth()

That method has a hardcoded return value, true on Windows platforms and false otherwise. This code is extracted from a JDK installed on Windows 7:

static boolean supportsTransparentAuth()
{
  return true;
}

What that method tells is if Windows credentials should be used by default. If set to true, your custom Authenticator code won't be called. See this fragment of HttpURLConnection class:

//Declared as a member variable of HttpURLConnection
private boolean tryTransparentNTLMServer = NTLMAuthentication.supportsTransparentAuth();

//Inside of getServerAuthentication method.
PasswordAuthentication a = null;
if (!tryTransparentNTLMServer) {
    //If set to false, this will call Authenticator.requestPasswordAuthentication().
    a = privilegedRequestPasswordAuthentication(url.getHost(), addr, port, url.getProtocol(), "", scheme, url, RequestorType.SERVER);
}

/* If we are not trying transparent authentication then 
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username 
* and password will be picked up from the current logged 
* on users credentials.
*/
if (tryTransparentNTLMServer || (!tryTransparentNTLMServer && a != null)) {
    //If set to true or if Authenticator did not return any credentials, use Windows credentials.
    //NTLMAuthentication constructor, if receives a == null will fetch current looged user credentials.
    ret = new NTLMAuthentication(false, url1, a);
}

To get NTLMAuthentication source code, I used this Java decompiler. Opened rt.jar located on the JDK installation folder and copied the desired class code.

Then, I simply changed supportsTransparentAuth to return false. However, it would be highly desirable if this method checked first a system property and then return true or false based on that.

To compile it, I just placed the java file under sun/net/www/protocol/http folder structure and run:

javac NTLMAuthentication.java

Then run my application using:

java -Xbootclasspath:"path/to/your/sun/net/www/protocol/http/classes;normal/JDK/boot/directories"

That will tell the JVM to load our implementation of NTLMAuthentication before the one in rt.jar. You have to be careful to don't miss any default class loading paths with -Xbootclasspath, or there will be ClassNotFound errors.

After that, everything worked just fine.

This approach has important drawbacks that you should be aware of.

  • There are security risks. Anyone could drop a different .class file on your boot folder and steal the user credentials or other important information.
  • Code from Sun packages can change without notice and thus be incompatible with your changes.
  • If you deploy this code, you will be contravening the Sun code license. From the documentation:

-Xbootclasspath:bootclasspath Specify a semicolon-separated list of directories, JAR archives, and ZIP archives to search for boot class files. These are used in place of the boot class files included in the Java 2 SDK. Note: Applications that use this option for the purpose of overriding a class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.

So, this is definitely not suitable for production environments.

Finally, this is an excellent source about boot class path option and Java class loaders: PDF

Hope this helps.

Snout answered 26/1, 2012 at 20:21 Comment(5)
Thanks. I've accepted this answer, and this agrees with my own earlier analysis (also by Sun JRE code decompilation) that there is no way to avoid this with Sun's standard code. Thanks for figuring out a way to work around this design flaw (as I see it)-- but this was intended for production use, and I definitely am not going to (tell colleagues to) use this. I'll vote for this Bug ID 7133281 instead.Bicuspid
Yes, for production use this should be definitely avoided. Luckily, I just needed it for a demo so it worked for me.Snout
If anyone comes to this, there is a much simpler approach which consists on using Apache HTTP Client 4 instead of Java underlying HTTP API. In my particular case, I ended up using a combination of Apache HTTP Client and JCIFs NTLM implementation to solve all NTLM related issues in Java.Snout
The bug ID 7133281 that @Marnix-Klooster mentioned no longer seems to exist: bugs.sun.com/bugdatabase/view_bug.do?bug_id=7133281 -- anybody know what happened to it?Excelsior
Another solution which I used in this case was the runas command, and I simply launch my java process with the user I want to authenticate with. One can use bat file to launch the app like this automatically, or use other Windows facilities to run app under different userBiota
L
7

At least in Java 7 there is a class called sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback that seems to help with this situation. Single sign-on is only invoked for "trusted" URLs.

Here is the simplest implementation to turn it off (have this initialiser called prior to opening the HTTP connection):

static {
    NTLMAuthenticationCallback.setNTLMAuthenticationCallback(new NTLMAuthenticationCallback()
    {
        @Override
        public boolean isTrustedSite(URL url)
        {
            return false;
        }
    });
}

I guess the default implementation is to trust everything :(

Leoni answered 5/2, 2013 at 4:27 Comment(1)
+1 for this find. And yes, I see in DefaultNTLMAuthenticationCallback that the default implementation is to try silent (what the JavaDoc calls 'transparent') authentication.Bicuspid
S
6

I've looked for the same thing you are asking. So far, I haven't found a way on the JDK to do that.

There is a request for enhancement on Java Bug Database. Take a look at the report to find out if that gets a response from Sun (vote up the report so that hopefully that gets fixed soon).

What I ended up doing, was override sun.net.www.protocol.http.NTLMAuthentication class. By looking at sun.net.www.protocol.http.HttpURLAuthentication, I found that the only thing you need to modify is the result of:

NTLMAuthentication.supportsTransparentAuth()

That method has a hardcoded return value, true on Windows platforms and false otherwise. This code is extracted from a JDK installed on Windows 7:

static boolean supportsTransparentAuth()
{
  return true;
}

What that method tells is if Windows credentials should be used by default. If set to true, your custom Authenticator code won't be called. See this fragment of HttpURLConnection class:

//Declared as a member variable of HttpURLConnection
private boolean tryTransparentNTLMServer = NTLMAuthentication.supportsTransparentAuth();

//Inside of getServerAuthentication method.
PasswordAuthentication a = null;
if (!tryTransparentNTLMServer) {
    //If set to false, this will call Authenticator.requestPasswordAuthentication().
    a = privilegedRequestPasswordAuthentication(url.getHost(), addr, port, url.getProtocol(), "", scheme, url, RequestorType.SERVER);
}

/* If we are not trying transparent authentication then 
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username 
* and password will be picked up from the current logged 
* on users credentials.
*/
if (tryTransparentNTLMServer || (!tryTransparentNTLMServer && a != null)) {
    //If set to true or if Authenticator did not return any credentials, use Windows credentials.
    //NTLMAuthentication constructor, if receives a == null will fetch current looged user credentials.
    ret = new NTLMAuthentication(false, url1, a);
}

To get NTLMAuthentication source code, I used this Java decompiler. Opened rt.jar located on the JDK installation folder and copied the desired class code.

Then, I simply changed supportsTransparentAuth to return false. However, it would be highly desirable if this method checked first a system property and then return true or false based on that.

To compile it, I just placed the java file under sun/net/www/protocol/http folder structure and run:

javac NTLMAuthentication.java

Then run my application using:

java -Xbootclasspath:"path/to/your/sun/net/www/protocol/http/classes;normal/JDK/boot/directories"

That will tell the JVM to load our implementation of NTLMAuthentication before the one in rt.jar. You have to be careful to don't miss any default class loading paths with -Xbootclasspath, or there will be ClassNotFound errors.

After that, everything worked just fine.

This approach has important drawbacks that you should be aware of.

  • There are security risks. Anyone could drop a different .class file on your boot folder and steal the user credentials or other important information.
  • Code from Sun packages can change without notice and thus be incompatible with your changes.
  • If you deploy this code, you will be contravening the Sun code license. From the documentation:

-Xbootclasspath:bootclasspath Specify a semicolon-separated list of directories, JAR archives, and ZIP archives to search for boot class files. These are used in place of the boot class files included in the Java 2 SDK. Note: Applications that use this option for the purpose of overriding a class in rt.jar should not be deployed as doing so would contravene the Java 2 Runtime Environment binary code license.

So, this is definitely not suitable for production environments.

Finally, this is an excellent source about boot class path option and Java class loaders: PDF

Hope this helps.

Snout answered 26/1, 2012 at 20:21 Comment(5)
Thanks. I've accepted this answer, and this agrees with my own earlier analysis (also by Sun JRE code decompilation) that there is no way to avoid this with Sun's standard code. Thanks for figuring out a way to work around this design flaw (as I see it)-- but this was intended for production use, and I definitely am not going to (tell colleagues to) use this. I'll vote for this Bug ID 7133281 instead.Bicuspid
Yes, for production use this should be definitely avoided. Luckily, I just needed it for a demo so it worked for me.Snout
If anyone comes to this, there is a much simpler approach which consists on using Apache HTTP Client 4 instead of Java underlying HTTP API. In my particular case, I ended up using a combination of Apache HTTP Client and JCIFs NTLM implementation to solve all NTLM related issues in Java.Snout
The bug ID 7133281 that @Marnix-Klooster mentioned no longer seems to exist: bugs.sun.com/bugdatabase/view_bug.do?bug_id=7133281 -- anybody know what happened to it?Excelsior
Another solution which I used in this case was the runas command, and I simply launch my java process with the user I want to authenticate with. One can use bat file to launch the app like this automatically, or use other Windows facilities to run app under different userBiota
A
2

It seems that the class sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback was added to java 6.0 patch 24+ so the solution suggested can work in java 6.0 as well. See reference in the following post: http://www.mail-archive.com/[email protected]/msg22897.html

Alburg answered 4/4, 2013 at 12:49 Comment(1)
+1 Thanks! Please note that the referred post says that the package name on Java 6 is different, viz. sun.net.www.protocol.http.Bicuspid
S
1

With Java v8 212 or higher, "transparent" SSO authentication via NTLMv2 is disabled by default, as a consequence of this CVE: https://nvd.nist.gov/vuln/detail/CVE-2019-2426. For more information: How to provide ntlm authentication while calling any url?

Stroganoff answered 21/5, 2019 at 12:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.